[英]How to read bytes from socket with ctime() in C
目前,我正在做某種練習,嘗試通過TCP連接模擬時間協議RFC 868。 我已經用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);
}
}
服務器工作正常,但是在客戶端執行時出現問題。 客戶代碼:
#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);
}
主要問題是我真的不知道如何讀取接收到的4個字節。 當我打印出lorovrigida時,這似乎是一個巨大且隨機的數字。
我該如何正確閱讀? 是因為我使用的是無符號長型嗎? 我被困住了,因為我嘗試了很多不同的方法,但沒有任何效果。
謝謝您的幫助。
我不認為這是字節序問題,因為您的java server
已經處理了。
在您的客戶端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);
結果:
256
4294967040
32位結果的重建是一個問題,從可能簽名的4個char
開始。
假設char
被簽名並且2的補碼...
添加buffer[3]
,它首先會進行通常的整數提升。 這意味着類似1000_0000
(-128)的位模式變為1111_1111_...1000_0000
。 前1位的數量取決於int
位的寬度。
為了避免所有這些符號擴展,最好使用無符號類型。
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];
....
}
如果系統使用16位無符號,則方法1-3會具有可移植性。
優化的編譯器可能會為這些方法生成類似的代碼。
此外:在我的編譯器中,代碼還可以通過使用uint32_t
之類的固定寬度而不是unsigned long
(64位)來受益。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.