UDP socket in C - recvfrom() doesn't seem to read data from socket as intended

69 views Asked by At

I've created an account just for this since I'm at my wits end here: The goal is for the server to receive and read exactly 2 bytes from the message "Hello from client" - 1 byte at a time with recvfrom(). This doesn't work - the first recvfrom() makes the program sleep indefinitely (I haven't set a timeout in purpose). For the record I am using a Linux (Ubuntu) virtual machine with Oracle VM VirtualBox, and I'm fairly new to programming in C. udp_server.c and udp_client.c codes pasted below, would appreciate any insight.

udp_server.c:

int main(void) {
    /* Socket */
    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock == -1) {
        perror("socket(2)");
        return 1;
    }

    /* Set socket option SO_REUSEADDR */
    int opt = 1;
    if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
        perror("setsockopt(2)");
        close(sock);
        return 1;
    }

    /* server sockaddr_in struct */
    struct sockaddr_in server;
    memset(&server, 0, sizeof(server));
    server.sin_addr.s_addr = INADDR_ANY;
    server.sin_family = AF_INET;
    server.sin_port = htons(5061);

    /* Bind */
    if (bind(sock, (struct sockaddr*)&server, sizeof(server)) < 0) {
        perror("bind(2)");
        close(sock);
        return 1;
    }

    /* client sockaddr_in struct (for recvfrom()) */
    struct sockaddr_in client;
    socklen_t client_len = sizeof(client);
    memset(&client, 0, sizeof(client));

    /* Receive data from socket */
    fprintf(stdout, "Listening on port %d...\n", 5061);
    char buffer[1024] = { 0 };
    int bytes_received = recvfrom(sock, buffer, 1, 0, (struct sockaddr*)&client, &client_len);
// PROGRAM FREEZES HERE ^^^
    if (bytes_received < 0) {
        printf("bytes_received %d, errno %d", bytes_received, errno);
        perror("recvfrom");
        close(sock);
        return 1;
    }
    printf("SUCCESS(1) bytes_received %d", bytes_received);
    bytes_received = recvfrom(sock, buffer, 1, 0, (struct sockaddr*)&client, &client_len);
    if (bytes_received < 0) {
        printf("bytes_received %d, errno %d", bytes_received, errno);
        perror("recvfrom");
        close(sock);
        return 1;
    }
    printf("SUCCESS(2) bytes_received %d", bytes_received);

    /* Shutdown */
    shutdown(sock, SHUT_RDWR);
    close(sock);
    return 0;
}

udp_client.c:

int main(void) {
    /* Socket */
    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock == -1) { // In case socket creation failed.
        fprintf(stdout, "ERROR: Socket creation failed.\n");
        return -1;
    }

    /* Server sockaddr_in struct (for sendto()) */
    struct sockaddr_in server;
    memset(&server, 0, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_port = htons(5061);
    if (inet_pton(AF_INET, "127.0.0.1", &server.sin_addr) <= 0) {
        perror("inet_pton(3)");
        close(sock);
        return 1;
    }

    /* Send data to socket */
    char* message = "Hello from client";
    int bytes_sent = sendto(sock, message, strlen(message), 0, (struct sockaddr*)&server, sizeof(server));
    if (bytes_sent <= 0) {
        perror("sendto(2)");
        close(sock);
        return 1;
    }
    fprintf(stdout, "Sent %d bytes to %s:%d - %s\n",
        bytes_sent, inet_ntoa(server.sin_addr), ntohs(server.sin_port), message);
    usleep(75000);

    /* Shutdown */
    shutdown(sock, SHUT_RDWR);
    close(sock);
    return 0;
}
1

There are 1 answers

2
Ian Abbott On

The udp_client program sends a 17-byte datagram to the udp_server program, which is expecting two datagrams of 1 byte each. The 16 excess bytes from the first datagram will be discarded.

If the udp_client program is run twice, then two 17-byte datagrams will be sent to the udp_server program. The 16 excess bytes from each received datagram will be discarded, so udp_server will write the following to the standard output stream as a result of reading the two datagrams:

Listening on port 5061...
SUCCESS(1) bytes_received 1SUCCESS(2) bytes_received 1

Note: there is no newline after the output. The only printf call in "udp_server.c" that includes a newline character at the end of the format string is the one that prints the Listening on port 5061... message. If the standard output stream is line buffered, the output from the other printf calls will remain buffered until the program exits, when the buffered contents will be output to the terminal.

For Unix, the usual convention is to initialize the standard input, standard output, and standard error streams as follows:

  • For the standard input and standard output streams, if it can be determined that the stream is not connected to a terminal, it will be fully buffered, else it will be line buffered.
  • The standard error stream will be unbuffered.