Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c

unix

tcp

sockets

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?

like image 806
Joey Adams Avatar asked Jul 26 '11 19:07

Joey Adams


People also ask

How do you transfer data to TCP ports?

The TCP Open Connection node opens a connection to the server at the port and address you specify. The address must match the IP address of the server, and the port you specify on the client must match the port you specify on the server. TCP Write nodes write data to the specified port and send commands to the server.

How do TCP streams work?

TCP is a connection-oriented protocol meaning it first sets up a connection to the receiver then sends the data in segments (PDU for transport layer) which is carried by IP packets. This way it's called stream because it keeps the stream of data between to ends during transfer.

Does TCP use streams?

The TCP protocol is modeled on the concept of a single continuous stream of unlimited length. This is a very important concept to understand, and is the number one cause of confusion that we see.

What does networkstream Flush do?

Flushes data from the stream. This method is reserved for future use.


1 Answers

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.

like image 78
Fred Foo Avatar answered Sep 28 '22 11:09

Fred Foo