NetTech
NetTech

Reputation: 11

ntohs() issue : Write Integer in C socket

I am trying to write and read Integer value into/from C socket. Sometimes ntohs() return very big values like 55000 , 32000 etc...Though client is always sending value <1500. If I run the program it happens after 10-15 minutes...Sometimes after 20-30 minutes.

Can you please check below code and tell me Why this line getting printed ?

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;
    }

}

Thank you

Upvotes: 1

Views: 499

Answers (1)

John Bollinger
John Bollinger

Reputation: 181008

We cannot tell you with certainty what's happening because you cannot provide a verifiable example. Additionally, you've not presented the implementation of read_n(), but supposing that it follows the same model as write_n() and send_n(), we can nevertheless perform some analysis.

Each of the data transfer functions returns a short count in the event that data transfer is interrupted by an error. The client code watches for this, and breaks out of its loop if it detects it. Well and good. The server code does not do this when reading plength, however. Since plength, as a uint16_t, is two bytes in size, a partial read is possible and would go unnoticed by your server code.

In your example, plength is modified only via the one read_n() call presented. Network byte order is big-endian, so the most-significant byte is read first. It is possible that the combination of that byte with the stale one left over from the previous read would represent a number exceeding 1500. For example, if a 221(0x00dd)-byte packet is followed by a 1280(0x0500)-byte packet, and a partial read occurs on the second packet size, then the combined result will be 1501(0x05dd).

I don't presently see any reason to think that the client sends data different in nature than you think it does, and I don't presently see any other way that your server code could give the appearance of receiving different data than the client sends, especially since client and server each abort at the first recognized sign of trouble.

Do note, however, that this code could still be made more robust. In particular, consider that read(), write(), and send() can fail even when there is no problem with the underlying socket or data transfer request. In particular, they can fail with EINTR if the call is interrupted by a signal, and if the socket is in non-blocking mode then they can fail with EAGAIN. There may be others. It does not seem useful to operate your socket in non-blocking mode, but you might indeed want to watch for EINTR and resume reading after receiving it.

I would also suggest that, at least during development, you emit more data about the nature of the error. Call perror(), for example, and afterward print the bad data. You might even consider logging data sent and received.

Upvotes: 2

Related Questions