简体   繁体   中英

How to read bytes from socket with ctime() in C

At the moment I'm doing some kind of practice trying to emulate the Time Protocol RFC 868 via TCP connection. I've done the server in Java and I'm doing the client in C. Here are the main code lines for the server.

private static final long SECONDS_FROM_1900_TO_1970 = 2208988800L; //Long I need in order to calculate the
//time past since 1900. It is because time protocol sets the epoch at 1900 and the java Date class at 
//1970.
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        try{
            int serverPort = 37000;
            ServerSocket receivingConnections = new ServerSocket (serverPort);
            for(;;) { //Infinity loop equivalent to while(true)
              clientConnection = receivingConnections.accept();
              System.out.println("Connection established with client IP: " +clientConnection.getInetAddress());
                try{
                    in = new DataInputStream(clientConnection.getInputStream());
                    out = new DataOutputStream(clientConnection.getOutputStream());
                    Date actualTime = new Date();//Date gives us the second for the epoch until 1970. Had to import utils.date
                    long secondsSince1970 = (actualTime.getTime())/1000;//I divide the time by 1000 because actualTime.getTime()
                    //returns me the result in ms
                    long secondsFromTheBeginning = secondsSince1970 + SECONDS_FROM_1900_TO_1970; //Total seconds i do need to calculate
                    //the time from 1990 in order to respect RFC 868 specification

                    byte[] actualTimeToBytes = new byte[4]; //Byte structure I need to send to the client. 4 bytes = 32 bits.
                    //With the next 4 lines of code I will introduce the whole long into 4 bytes. A long takes 4 bytes = 32 bits.
                    actualTimeToBytes[0] = (byte) ((secondsFromTheBeginning & 0x00000000FF000000L) >> 24);
                    actualTimeToBytes[1] = (byte) ((secondsFromTheBeginning & 0x0000000000FF0000L) >> 16);
                    actualTimeToBytes[2] = (byte) ((secondsFromTheBeginning & 0x000000000000FF00L) >> 8);
                    actualTimeToBytes[3] = (byte) (secondsFromTheBeginning & 0x00000000000000FFL);
                    String s = new String(actualTimeToBytes);
                    System.out.println(secondsFromTheBeginning); //The actual time being sent over the socket..
                    System.out.println(s); // The actual byte being sent over the socket.
                    out.write(actualTimeToBytes);//Here I send to the receiver the time in Byte form via the socket
                    out.flush();
                    System.out.println("The time was indeed sent to: " +clientConnection.getInetAddress());


                } catch (IOException e){System.out.println("Error: "+e.getMessage()); }
                finally { if (clientConnection != null) {

                    System.out.println("Connection finished with client IP: " +clientConnection.getInetAddress());
                    clientConnection.close(); 
                }
            }
        } 

    } catch (IOException ex) {
        System.err.println(ex);
    }
    }

The server works fine but the problem comes when executin the client. Client code:

#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include <time.h>


/*****************************************************
 * Reproduced from Unix Network Programming
                W.Richard Stevens
                Prentice Hall Software Series 1990
 * Definitions for TCP and UDP client/server programs
 */
#include    <sys/types.h>
#include    <sys/socket.h>
#include    <netinet/in.h>
#include    <arpa/inet.h>
/*****************************************************/


#define SERVERPORT 37000
#define SERVERIP "192.168.0.18"
#define TIMELENGTHBYTES 4

/* 
 * Program a new function that reads four bytes from the socket:
 * int readDate(...)
 */
int readDate(int *sockfd){

     int    n;
     char   buffer[4];
     unsigned long  lorovrigida;
    n = read(*sockfd, buffer, 4);  
   lorovrigida = (buffer[0]<<24) + (buffer[1]<<16) + (buffer[2]<<8) + (buffer[3]);
   lorovrigida-=2208988800;

    printf(ctime(&lorovrigida));

    if ( n < 0 ) {
    printf("TIMECLNT: Error in read()\n");
    exit(EXIT_FAILURE);
    }
}
int connectToServer(char *ip, int port, int *sockfd) {
    int rv = 0;    
    struct sockaddr_in serv_addr;

    char buf[4];

    bzero((char *) &serv_addr, sizeof (serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = inet_addr(SERVERIP);
    serv_addr.sin_port = htons(SERVERPORT);

    if ((*sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        rv = -1;
    } else {

        if (connect(*sockfd, (struct sockaddr *) &serv_addr, sizeof (serv_addr)) < 0) {
            rv = -2;
        }
    }

    return rv;
}

int main(int argc, char** argv) {

    int e, sockfd;


    if ((e = connectToServer(SERVERIP, SERVERPORT, &sockfd)) != 0) {
        printf("Error on call to connectToServer: %d\n", e);
        exit(e);
    }

    printf("Successfully connected to server\n");
    if(sockfd==NULL)
        printf("El socket esta vacio\n");
    fflush(stdout);    

    /*
     * Call the readDate() function and print out the result:
     */
    readDate(&sockfd);



    return (EXIT_SUCCESS);
}

The main problem is that I don't really know how to read the 4 received Bytes. When I'm printing out lorovrigida seems like it is a huge and random number.

How could I do the correct reading? Is it because I'm using a unsigned long?? I'm just stuck because I've tried a ton oh different methods and nothing works.

Thank you for your help.

I don't think it's the problem of endian issue, for your java server have processed that.

In your client char buffer[4]; , you declare it as signed , when do bit shifting, you're not gone to get the correct result when highest bit of some byte is 1. Defined to be unsigned char buffer[4];

 unsigned char ua = 0x80;
 unsigned int b = ua << 1;
 printf("%u\n", b);

 char a = 0x80;
 unsigned int c = a << 1;
 printf("%u\n", c);

Result:

256
4294967040

The reconstruction of the 32-bit result is a problem starting with 4 char which are likely signed.

Assuming char is signed and 2's complement...

When buffer[3] is added, it first goes through the usual integer promotions. This means a bit pattern like 1000_0000 (-128) becomes 1111_1111_...1000_0000 . The number of leading 1 bits depends on int bit width.


To avoid all this sign extension, best to work with unsigned types.

IMO: recommend #4

// Method 1 - cast buffer
int readDate(int *sockfd) {
  char   buffer[4];
  int    n;
  unsigned long  lorovrigida;
  n = read(*sockfd, buffer, 4);  
  if ( n < 0 ) {
    printf("TIMECLNT: Error in read()\n");
    exit(EXIT_FAILURE);
  }
  lorovrigida = ((unsigned char)buffer[0]<<24) + ((unsigned char)buffer[1]<<16) + 
      ((unsigned char)buffer[2]<<8) + ((unsigned char)buffer[3]);
  ....
}

// Method 2 - use unsigned char type
int readDate_2(int *sockfd) {
  unsigned char buffer[4];
  ...
  lorovrigida = (buffer[0]<<24) + (buffer[1]<<16) + 
      (buffer[2]<<8) + (buffer[3]);
  ....
}

// Method 3 - exact size types
int readDate_2(int *sockfd) {
  uint8_t buffer[4];
  ...
  lorovrigida = (buffer[0]<<24) + (buffer[1]<<16) + 
      (buffer[2]<<8) + (buffer[3]);
  ....
}

// Method 4 - * by unsigned
int readDate_2(int *sockfd) {
  uint8_t buffer[4];
  ...
  lorovrigida = buffer[0]*0x1000000LU + buffer[1]*0x10000LU + 
      buffer[2]*0x100u + buffer[3];
  ....
}

// Method 5 - successive or-ing
int readDate_2(int *sockfd) {
  uint8_t buffer[4];
  ...
  lorovrigida = buffer[0];
  lorovrigida = lorovrigida << 8 | buffer[1];
  lorovrigida = lorovrigida << 8 | buffer[2];
  lorovrigida = lorovrigida << 8 | buffer[3];

  ....
}

Method 1 - 3 suffer from portability should the system use 16-bit unsigned.

An optimizing compiler may generate similar code for these methods.

Further: Code may benefit with additional use of fixed widths like uint32_t rather than unsigned long (which is 64-bit) with my compiler.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM