1

I am doing some test with TCP client application in a Raspberry Pi (server in the PC), with PPP (Point to Point Protocol) using a LTE Modem. I have used C program with sockets, checking system call's response. I wanted to test how socket works in a bad coverage area so I did some test removing the antenna.

I have followed the next steps:

  1. Connect to server --> OK
  2. Start sending data (write system call) --> OK (I also check in the server)
  3. I removed the LTE modem's antenna (There is no network, it can't do ping)
  4. Continue sending data (write system call) --> OK (server does not receive anything!!!)
  5. It finished sending data and closed socket --> OK (connection is still opened and there is no data since the antenna was removed)
  6. Program was finished
  7. I put the antenna again

Some time later, the data has been uploaded and the connection closed. But I did another test following this steps but with more data, and it did not upload this data...

I do not know if there any way to ensure that the data written to TCP server is received by the server (I thought that TCP layer ensured this..). I could do it manually using an ACK but I guess that it has to be a better way to do.

Sending part code:

    while(i<100)
    {
        sprintf(buf, "Message %d\n", i);
        Return = write(Sock_Fd, buf, strlen(buf));
        if(Return!=strlen(buf))
        {
            printf("Error sending data to TCP server. \n");
            printf("Error str: %s \n", strerror(errno));
        }
        else
        {
            printf("write successful %d\n", i);
            i++;
        }
        sleep(2);
    }

Many thanks for your help.

Julen Uranga
  • 757
  • 1
  • 5
  • 17
  • Are you using non-blocking sockets? How are you writing the data and checking the results? Can you please try to create a [Minimal, Complete, and Verifiable Example](http://stackoverflow.com/help/mcve) of the client program and show us? – Some programmer dude Jan 24 '17 at 09:53
  • I am using the default mode, blocking mode I guess (when I connect to the server it waits until connection has been made). – Julen Uranga Jan 24 '17 at 10:00

3 Answers3

4

The write()-syscall returns true, since the kernel buffers the data and puts it in the out-queue of the socket. It is removed from this queue when the data was sent and acked from the peer. When the OutQueue is full, the write-syscall will block.

To determine, if data has not been acked by the peer, you have to look at the size of the outqueue. With linux, you can use an ioctl() for this:

ioctl(fd, SIOCOUTQ, &outqlen);

However, it would be more clean and portable to use an inband method for determining if the data has been received.

Ctx
  • 17,064
  • 24
  • 33
  • 48
  • What is an "inband method"? I checked at google but I did not find any clear explanation – Julen Uranga Jan 24 '17 at 11:04
  • 2
    @JulenUranga This means, that the acknowledgement is sent through the tcp-connection itself. Client: "DO STH" - Server: "DONE". – Ctx Jan 24 '17 at 11:07
1

TCP/IP is rather primitive technology. Internet may sound newish, but this is really antique stuff. TCP is needed because IP gives almost no guarantees, but TCP doesn't actually add that many guarantees. Its chief function is to turn a packet protocol into a stream protocol. That means TCP guarantees a byte order; no bytes will arrive out of order. Don't count on more than that.

You see that protocols on top of TCP add extra checks. E.g. HTTP has the famous HTTP error codes, precisely because it can't rely on the error state from TCP. You probably have to do the same - or you can consider implementing your service as a HTTP service. "RESTful" refers to an API design methodology which closely follows the HTTP philosophy; this might be relevant to you.

MSalters
  • 159,923
  • 8
  • 140
  • 320
1

The short answer to your 4th and 5th topics was taken as a shortcut from this answer (read the whole answer to get more info)

A socket has a send buffer and if a call to the send() function succeeds, it does not mean that the requested data has actually really been sent out, it only means the data has been added to the send buffer. For UDP sockets, the data is usually sent pretty soon, if not immediately, but for TCP sockets, there can be a relatively long delay between adding data to the send buffer and having the TCP implementation really send that data. As a result, when you close a TCP socket, there may still be pending data in the send buffer, which has not been sent yet but your code considers it as sent, since the send() call succeeded. If the TCP implementation was closing the socket immediately on your request, all of this data would be lost and your code wouldn't even know about that. TCP is said to be a reliable protocol and losing data just like that is not very reliable. That's why a socket that still has data to send will go into a state called TIME_WAIT when you close it. In that state it will wait until all pending data has been successfully sent or until a timeout is hit, in which case the socket is closed forcefully.

The amount of time the kernel will wait before it closes the socket, regardless if it still has pending send data or not, is called the Linger Time.

BTW: that answer also refers to the docs where you can see more detailed info

Community
  • 1
  • 1
Yuriy Ivaskevych
  • 818
  • 8
  • 16
  • "If the TCP implementation was closing the socket immediately on your request, all of this data would be lost". What does this mean? The server closes the connection or the client close the socket? Or both? – Julen Uranga Jan 24 '17 at 10:14
  • 1
    @JulenUranga It means that the default implementation will not close a connection immediately: it would try to send all the data still present in buffer if any and then trying to close the socket _gracefully_ . But if you prefer to close the socket **immediately** (by disabling the _Linger Time_ for example) it would be closed _forcefully_ meaning that all pending data in the buffer will be ignored and connection immediately closed. – Yuriy Ivaskevych Jan 24 '17 at 10:26