Sockets – How to push (i.e. flush) data sent to a TCP stream

csocketstcpunix

RFC 793 says that TCP defines a "push" function that ensures that the receiver got the data:

Sometimes users need to be sure that all the data they have
submitted to the TCP has been transmitted. For this purpose a push
function is defined. To assure that data submitted to a TCP is
actually transmitted the sending user indicates that it should be
pushed through to the receiving user. A push causes the TCPs to
promptly forward and deliver data up to that point to the receiver.

However, I can't find a push system call. Using fsync on the file descriptor produces an invalid argument error.

I conducted an experiment with a simple server that accepts a connection from a client, waits, then sends 26 bytes to the client:

#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>

#define PORT 1234

int main(void)
{
    int server_fd;
    int client_fd;

    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        perror("socket");
        return 1;
    }

    {
        struct sockaddr_in addr;
        memset(&addr, 0, sizeof(addr));
        addr.sin_family = AF_INET;
        addr.sin_port = htons(PORT);
        addr.sin_addr.s_addr = INADDR_ANY;

        if (bind(server_fd, (struct sockaddr*)&addr, sizeof(addr)) != 0) {
            perror("bind");
            return 1;
        }
    }

    if (listen(server_fd, 20) != 0) {
        perror("listen");
        return 1;
    }

    {
        struct sockaddr_in addr;
        socklen_t addrlen = sizeof(addr);

        printf("Waiting for connection on port %d\n", PORT);

        if ((client_fd = accept(server_fd, (struct sockaddr*)&addr, &addrlen)) < 0) {
            perror("accept");
            return 1;
        }

        printf("%s:%d connected\n",
               inet_ntoa(addr.sin_addr),
               ntohs(addr.sin_port));
    }

    printf("Giving client time to close connection.\n");
    sleep(10);

    {
        ssize_t sent_length;

        if ((sent_length =
             send(client_fd, "abcdefghijklmnopqrstuvwxyz", 26, 0)) < 0)
        {
            perror("send");
            return 1;
        }

        printf("Sent %Zd bytes.\n", sent_length);
    }

    printf("Closing connection to client\n");
    if (close(client_fd) != 0) {
        perror("close(client_fd)");
        return 1;
    }

    printf("Shutting down\n");
    if (close(server_fd) != 0) {
        perror("server: close(server_fd)");
        return 1;
    }

    printf("Done!\n");
    return 0;
}

I found that the send call immediately returns 26, even after I close the connection client-side or unplug the network cable. In the latter case, the data appears on the client when I plug the cable back in and wait a few seconds (long after the server has shut down).

How do I ensure that data sent with send is received and acknowledged?

Best Answer

There is no push, says the late W. Richard Stevens; the standard sockets API doesn't provide it, and is not required to do so by RFC 1122. You can set the TCP_NODELAY option, but that's only a partial solution.

If you want to be sure the other end got your data, then let it send an acknowledgment over the TCP channel.

Related Topic