简体   繁体   English

如何在C中使用ctime()从套接字读取字节

[英]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. 目前,我正在做某种练习,尝试通过TCP连接模拟时间协议RFC 868。 I've done the server in Java and I'm doing the client in C. Here are the main code lines for the server. 我已经用Java完成了服务器,并且已经用C完成了客户端。这是服务器的主要代码行。

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. 主要问题是我真的不知道如何读取接收到的4个字节。 When I'm printing out lorovrigida seems like it is a huge and random number. 当我打印出lorovrigida时,这似乎是一个巨大且随机的数字。

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. 我不认为这是字节序问题,因为您的java server已经处理了。

In your client char buffer[4]; 在您的客户端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]; ,则将其声明为有signed ,进行移位时,当某个字节的最高位为1时,就不会获得正确的结果。定义为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. 32位结果的重建是一个问题,从可能签名的4个char开始。

Assuming char is signed and 2's complement... 假设char被签名并且2的补码...

When buffer[3] is added, it first goes through the usual integer promotions. 添加buffer[3] ,它首先会进行通常的整数提升。 This means a bit pattern like 1000_0000 (-128) becomes 1111_1111_...1000_0000 . 这意味着类似1000_0000 (-128)的位模式变为1111_1111_...1000_0000 The number of leading 1 bits depends on int bit width. 前1位的数量取决于int位的宽度。


To avoid all this sign extension, best to work with unsigned types. 为了避免所有这些符号扩展,最好使用无符号类型。

IMO: recommend #4 IMO:建议#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. 如果系统使用16位无符号,则方法1-3会具有可移植性。

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. 此外:在我的编译器中,代码还可以通过使用uint32_t之类的固定宽度而不是unsigned long (64位)来受益。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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