'linux socket lose data when a delay is added before read

I am learning linux socket programming, I expect that server can read data, even I add a delay but it just drops the buffer data, and receive the recent data, that is why, Thanks. The code has been presented. By the way, Could you show a common practice to deal with this kind of situation?

Server side C/C++ program to demonstrate Socket programming

#include <unistd.h>
#include <stdio.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <string.h>
#define PORT 8080
int main(int argc, char const *argv[])
{
    int server_fd, new_socket, valread;
    struct sockaddr_in address;
    int opt = 1;
    int addrlen = sizeof(address);
    char buffer[1024] = {0};
    const char hello[] = "Hello from server";

    // Creating socket file descriptor
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0)
    {
        perror("socket failed");
        exit(EXIT_FAILURE);
    }

    // Forcefully attaching socket to the port 8080
    if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT,
                   &opt, sizeof(opt)))
    {
        perror("setsockopt");
        exit(EXIT_FAILURE);
    }
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(PORT);

    // Forcefully attaching socket to the port 8080
    if (bind(server_fd, (struct sockaddr *)&address,
             sizeof(address)) < 0)
    {
        perror("bind failed");
        exit(EXIT_FAILURE);
    }
    if (listen(server_fd, 3) < 0)
    {
        perror("listen");
        exit(EXIT_FAILURE);
    }
    if ((new_socket = accept(server_fd, (struct sockaddr *)&address,
                             (socklen_t *)&addrlen)) < 0)
    {
        perror("accept");
        exit(EXIT_FAILURE);
    }
    for (int i = 0;; i++)
    {
        sleep(5);
        valread = read(new_socket, buffer, 1024);
        printf("%s\n", buffer);
    }
    send(new_socket, hello, strlen(hello), 0);
    printf("Hello message sent\n");
    return 0;
}

Client side C/C++ program to demonstrate Socket programming

#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string>
#include <string.h>
#define PORT 8080

int main(int argc, char const *argv[])
{
    int sock = 0, valread;
    struct sockaddr_in serv_addr;
    const char data[] = "Hello from client";
    char buffer[1024] = {0};
    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        printf("\n Socket creation error \n");
        return -1;
    }

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(PORT);

    // Convert IPv4 and IPv6 addresses from text to binary form
    if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0)
    {
        printf("\nInvalid address/ Address not supported \n");
        return -1;
    }

    if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
    {
        printf("\nConnection Failed \n");
        return -1;
    }
    for (int i = 0;; i++)
    {
        sleep(1);
        std::string hello = std::string(data) + std::to_string(i);
        if (send(sock, hello.c_str(), hello.length() + 1, 0) != hello.length() + 1)
        {
            printf("error send %d \n", i);
        }
        printf("Hello message sent %d\n", i);
    }
    valread = read(sock, buffer, 1024);
    printf("%s\n", buffer);
    return 0;
}


Solution 1:[1]

The problem is, that the messages get concatenated in the socket. The socket represents a byte stream. Your sender puts bytes into the stream every second. On the first iteration, it writes "Hello from client0\0" (19 bytes) to the stream.

After one second, it writes "Hello from client1\0", and then "Hello from client2\0", "Hello from client3\0" and "Hello from client4\0", Now, after 5 Seconds, 5*19 = 95 bytes are written to the byte stream.

Now, the receiver calls valread = read(new_socket, buffer, 1024);. Guess what, it reads all 95 bytes (because you specified 1024 as buffer size) and sets valread to 95.

Then you call printf("%s\n", buffer);, which only prints the first 18 bytes of buffer, because there is a '\0' as 19th byte, which terminates '%s' format. Allthough 95 bytes are received, 76 bytes are missing in the output of your program.

If you use '\n' instead of '\0' as message separator and use write(1, buffer, valread) instead of printf("%s\n") on the receiving side, you will see all your data.

std::string hello = std::string(data) + std::to_string(i) + "\n";
if (send(sock, hello.c_str(), hello.length(), 0) != hello.length()) ...

Conclusion:

Stream sockets realize byte sreams, the do not preserve message boundaries.

If message bounaries must be preserved, you need to use a protocol on top of the stream to mark your message boundaries. The proptocol could be as simple as using '\n' as a message seaparator, as long as '\n' is not part of your message payload (e.g. when unsign a simple text protocol).

Solution 2:[2]

You block the server for 5 seconds and it cannot receive some messages from the client.

    for (int i = 0;; i++)
    {
        sleep(5);
        valread = read(new_socket, buffer, 1024);
        printf("%s\n", buffer);
    }

How can a client check if the server is receiving a message? I think this was discussed in Linux socket: How to make send() wait for recv()

P.S. It looks like there is a synchronizing piece of code, but you pulled it out of the loop.

Server:

}
send(new_socket, hello, strlen(hello), 0);

Client:

}
valread = read(sock, buffer, 1024);

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Mario Klebsch
Solution 2