1

I'm trying to read data from a UDP socket, but after reading the first 255 bytes, read() seems to drop the rest of the data on the socket and block until another data-gram comes in.

Here's the network code I'm using:

int sock;
struct sockaddr_in remote_addr, self_addr;

uint8_t network_init(uint16_t port)
{
    memset((char *) &remote_addr, 0, sizeof(remote_addr));
    remote_addr.sin_family = AF_INET;
    remote_addr.sin_addr.s_addr = inet_addr("192.168.1.22");
    remote_addr.sin_port = htons(3001);

    memset((char *) &self_addr, 0, sizeof(self_addr));
    self_addr.sin_family = AF_INET;
    self_addr.sin_addr.s_addr = INADDR_ANY;
    self_addr.sin_port = htons(3001);

    if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
    {
        fprintf(stderr, "Could not create socket.");
        return 1;
    }
    else if (bind(sock, (struct sockaddr *) &self_addr, sizeof(self_addr)) != 0)
    {
        fprintf(stderr, "Could not bind to socket.");
        return 1;
    }

    return 0;
}

void network_send(uint8_t *data, uint8_t len)
{
    sendto(sock, data, len, 0, (struct sockaddr *) &remote_addr, sizeof(remote_addr));
}

void read_data()
{
    int len = 0;
    ioctl(sock, FIONREAD, &len);

    // We have data
    if (len > 0)
    {
        char *buffer = (char *) malloc(256);
        uint8_t buflen;
        printf("==== %d | Data:\n", len);

        while (len > 0)
        {
            buflen = min(255, len);
            len = len - buflen;

            buffer[buflen] = '\0';

            printf("len: %d, buflen: %d,\n",len, buflen);

            read(sock, buffer, buflen);
            printf("%s\n", buffer);
        }
        printf("\n");
    }
}

Here's the command I'm using to send data:

echo -n '12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567' | nc -u localhost 3001

And here's the output:

==== 257 | Data:
len: 2, buflen: 255,
123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345
len: 0, buflen: 2,
^C

Also, after performing this read, ioctl(sock, FIONREAD, &len); produces a length result of 0. My suspicion is that for some reason, read() is clearing out the rest of the data before it has a chance to be read, but I can't seem to find any reference to this behaviour in any documentation.

I'm developing on an Ubuntu linux machine (x86_64).

Tyzoid
  • 1,072
  • 13
  • 31
  • 2
    almost always, when you think about what text to put in an error message, don't. Instead of `fprintf( stderr, "some random string that is probably wrong")`, try `perror("socket")` (or some other string that will be used as the header of the message, followed by the reason for the failure). – William Pursell Jan 27 '15 at 05:30
  • @WilliamPursell Thanks for the advice `perror` seems like a useful function. Apart from the difference in function calls, however, I don't really see the difference between my text and your recommendation. I simply preferred to write it in a complete sentence, but don't try and interpret the problem. Perhaps you could clarify? – Tyzoid Jan 27 '15 at 05:35
  • You don't need to allocate a new buffer per `recv()`. Just use a buffer on the stack, allocated to one larger than thre largest datagram you expect to receive. Then if you get one that long, it's an application protocol error, and may also have been truncated. And you don't have the possibility of memory leaks. Re the error messages, it is always preferable to use the one provided by the system. That way it is recognizable to other people, for example here, and indeed to yourself, without having to search your program text for it. You don't need any buffer preparation before `recv()`. – user207421 Jan 27 '15 at 05:59
  • For a clearer picture of why `perror` is better than writing your own strings, consider failures for `fopen`. `perror` will tell you "permission denied" or "file not found" or some other string that indicates the reason for the failure, which is far more useful than "unable to open file" with no explanation. I'll agree that the error messages are often less useful with socket functions, but `perror` is the way to go. – William Pursell Jan 27 '15 at 06:15

1 Answers1

3

With UDP sockets, each call to read() reads a whole datagram out of the kernel. If the read buffer isn't big enough for the entire datagram, the rest of it will be discarded. It's not like a stream socket, where you can keep calling until you get everything.

Since FIONREAD tells you the number of bytes in the message, you should use that as the size to malloc() rather than using 256:

if (len > 0) {

    char *buffer = malloc(len);
    ...

P.S. Do I cast the result of malloc?

Community
  • 1
  • 1
Barmar
  • 741,623
  • 53
  • 500
  • 612
  • Thanks! That makes more sense now. After a little more research with that new information it seems that `read()` will only return data out of the oldest packet not read yet, and will not queue up data from multiple packets together. http://www.microhowto.info/howto/listen_for_and_receive_udp_datagrams_in_c.html gives more information as to what is going on. – Tyzoid Jan 27 '15 at 05:44
  • 2
    That's the whole point of using a datagram socket instead of a stream socket. Datagrams are separate messages. – Barmar Jan 27 '15 at 05:45