我正在尝试在C套接字中写入和读取Integer值。有时ntohs()返回非常大的值,如55000,32000等......虽然客户端总是发送值<1500。如果我运行程序,它会在10-15分钟后发生......有时候会在20-30分钟后发生。
请您检查以下代码并告诉我 为什么要打印这条线?
printf(“Garbage value - ntohs problem ..Exiting ...”);
// write exactly n byte
inline int write_n(int fd, char *buf, int n) {
int nwrite, left = n;
int totalwrite = 0;
while (totalwrite != n) {
if ((nwrite = write(fd, buf, left)) <= 0) {
break;
} else {
totalwrite = totalwrite + nwrite;
left -= nwrite;
buf += nwrite;
}
}
if (totalwrite == 0)
return nwrite;
return totalwrite;
}
// send exactly n byte
inline int send_n(int fd, char *buf, int n) {
int nwrite, left = n;
int totalwrite = 0;
while (totalwrite != n) {
if ((nwrite = send(fd, buf, left, MSG_NOSIGNAL)) <= 0) {
break;
} else {
totalwrite = totalwrite + nwrite;
left -= nwrite;
buf += nwrite;
}
}
if (totalwrite == 0)
return nwrite;
return totalwrite;
}
uint16_t nread, len, plength, nsend;
int MTU = 1500;
char buffer[2000];
// Server receive ( Linux 64 bit)
while (1) {
// read packet length
nread = read_n(TCP_SOCKFD, (char *) &plength, sizeof(plength));
if (nread <=0) {
break;
}
len = ntohs(plength);
if (len <=0 || len > 1500 ) {
**printf("Garbage value - ntohs problem ..Exiting... "); // WHY ?**
break;
}
// read packat data
nread = read_n(SOCKFD, buffer, len);
if (nread != len) {
break;
}
}
//---------------------
// CLIENT send ( Android 5 )
while (1) {
nread = read(tunfd, buffer, MTU);
if (nread <= 0 || nread > 1500) { // always <=1500
break;
}
plength = htons(nread);
// send packet lenght
nsend = send_n(TCP_SOCKFD, (char *) &plength, sizeof(plength));
if (nsend != sizeof(plength)) {
break;
}
// send packet data
nsend = send_n(TCP_SOCKFD, buffer, nread);
if (nsend != nread) {
break;
}
}
谢谢
答案 0 :(得分:2)
我们无法确切地告诉您发生了什么,因为您无法提供可验证的示例。此外,您尚未提供read_n()
的实现,但假设它遵循与write_n()
和send_n()
相同的模型,我们仍然可以执行一些分析。
如果数据传输因错误而中断,则每个数据传输函数都会返回一个短计数。客户端代码监视此情况,如果检测到它,则会中断其循环。好,好。但是,在阅读plength
时,服务器代码不会执行此操作。由于plength
(作为uint16_t
的大小为两个字节,因此可以进行部分读取,并且您的服务器代码不会注意到这一点。
在您的示例中,plength
仅通过显示的read_n()
次调用进行修改。网络字节顺序是big-endian,因此首先读取最重要的字节。该字节与前一次读取中剩余的旧字节的组合可能表示超过1500的数字。例如,如果221(0x00dd)字节数据包后跟一个1280(0x0500)字节数据包,并且在第二个数据包大小上发生部分读取,然后合并的结果将是1501(0x05dd)。
我目前没有任何理由认为客户端发送的数据性质与您认为的不同,我现在看不到任何其他方式,您的服务器代码可能会给出接收不同数据的外观。客户端发送,特别是因为客户端和服务器都在第一个被识别的故障迹象中止。
但是请注意,此代码仍然可以更加健壮。特别是,即使底层套接字或数据传输请求没有问题,也要考虑read()
,write()
和send()
可能会失败。特别是,如果呼叫被信号中断,它们可能会失败EINTR
,如果套接字处于非阻塞模式,那么它们可能会失败EAGAIN
。可能还有其他人。在非阻塞模式下操作套接字似乎没什么用处,但您可能确实想要注意EINTR
并在收到后继续阅读。
我还建议,至少在开发过程中,您会发出更多有关错误性质的数据。例如,调用perror()
,然后打印错误数据。您甚至可以考虑记录发送和接收的数据。