ntohs()问题:在C套接字中写入整数

时间:2017-01-12 21:03:31

标签: c sockets endianness

我正在尝试在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;
    }

}

谢谢

1 个答案:

答案 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(),然后打印错误数据。您甚至可以考虑记录发送和接收的数据。