2

I am believing TCP is reliable. If write(socket, buf, buf_len) and close(socket) returns without error, the receiver will receive the exact same data of buf with length buf_len.

But this article says TCP is not reliable.

A:

  sock = socket(AF_INET, SOCK_STREAM, 0);  
  connect(sock, &remote, sizeof(remote));
  write(sock, buffer, 1000000);             // returns 1000000
  close(sock);

B:

int sock = socket(AF_INET, SOCK_STREAM, 0);
bind(sock, &local, sizeof(local));
listen(sock, 128);
int client=accept(sock, &local, locallen);
write(client, "220 Welcome\r\n", 13);

int bytesRead=0, res;
for(;;) {
    res = read(client, buffer, 4096);
    if(res < 0)  {
        perror("read");
        exit(1);
    }
    if(!res)
        break;
    bytesRead += res;
}
printf("%d\n", bytesRead);

Quiz question - what will program B print on completion?

A) 1000000 
B) something less than 1000000 
C) it will exit reporting an error 
D) could be any of the above

The right answer, sadly, is ‘D’. But how could this happen? Program A reported that all data had been sent correctly!

If this article is true, I have to change my mind. But I am not sure if this article is true or not.

Is this article true?

N.F.
  • 3,844
  • 3
  • 22
  • 53
  • 1
    For instance, the data might not yet have arrived at the receiver side while it is trying to read it, thus having read() return 0 and it prematurely breaking out of the loop. – marcolz Nov 28 '17 at 15:30

4 Answers4

5

TCP is reliable (at least when the lower level protocols are), but programmers may use it in a unreliable way.

The problem here is that a socket should not be closed before all sent data has been correctly received at the other side: the close signal may destroy the connection before while the last data is still in transfert.

The correct way to ensure proper reception by peer is a graceful shutdown.

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
5

TCP/IP is reliable, for a very particular (and limited) meaning of the word "reliable".

Specifically, when write() returns 1000000, it is making you the following promises:

  1. Your 1000000 bytes of data have been copied into the TCP socket's outgoing-data-buffer, and from now on, the TCP/IP stack is responsible for delivering those bytes to the remote program.
  2. If those bytes can be delivered (with a reasonable amount of effort), then they will be delivered, even if some of the transmitted TCP packets get dropped during delivery.
  3. If the bytes do get delivered, they will be delivered accurately and in-order (relative to each other and also relative to the data passed to previous and subsequent calls to write() on that same socket).

But there are also some guarantees that write() doesn't (and, in general, can't) provide. In particular:

  1. write() cannot guarantee that the receiving application won't exit or crash before it calls recv() to get all 1000000 of those bytes.
  2. write() cannot guarantee that the receiving application will do the right thing with the bytes it does receive (i.e. it might just ignore them or mishandle them, rather than acting on them)
  3. write() cannot guarantee that the receiving application ever will call recv() to recv() those bytes at all (i.e. it might just keep the socket open but never call recv() on it)
  4. write() cannot guarantee that the network infrastructure between your computer and the remote computer will work to deliver the bytes -- i.e. if some packets are dropped, no problem, the TCP layer will resend them, but e.g. if someone has pulled the power cable out of your cable modem, then there's simply no way for the bytes to get to their destination, and after a few minutes of trying and failing to get an ACK, the TCP layer will give up and error out the connection.
  5. write() can't guarantee that the receiving application handles socket-shutdown issues 100% correctly (if it doesn't, then it's possible for any data you send just before the remote program closes to the socket to be silently dropped, since there's no receiver left to receive it)
  6. write() doesn't guarantee that the bytes will be received by the receiving application in the same segment-sizes that you sent them in. i.e. just because you sent 1000000 bytes in a single call to send() doesn't mean that the receiving app can receive 1000000 bytes in a single call to recv(). He might instead receive the data in any-sized chunks, e.g. 1-byte-per-recv()-call, or 1000-bytes-per-recv-call, or any other size the TCP layer feels like providing.

Note that in all of the cases listed above, the problems occur after write() has returned, which is why write() can't just return an error code when these happen. (A later call to write() might well return an error code, of course, but that won't particularly help you know where the line between delivered-bytes and non-delivered bytes was located)

TLDR: TCP/IP is more reliable than UDP, but not a 100% iron-clad guarantee that nothing will ever go wrong. If you really want to make sure your bytes got processed on the receiving end, you'll want to program your receiving application to send back some sort of higher-level acknowledgement that it received (and successfully handled!) the bytes you sent it.

Jeremy Friesner
  • 70,199
  • 15
  • 131
  • 234
3

TCP/IP offers reliability which means it allows for the retransmission of lost packets, thereby making sure that all data transmitted is (eventually) received or you get a timeout error. Either your stuff get delivered or you are presented with a timeout error

What is the correct way of reading from a TCP socket in C/C++?

and btw, TCP/IP is reliable to some extent, it guarantees delivery assuming the network is working... if you unplug the cable TCP/IP will not deliver your packet

P.S. you should at least advance the pointer inside the buffer buffer

void ReadXBytes(int socket, unsigned int x, void* buffer)
{
    int bytesRead = 0;
    int result;
    while (bytesRead < x)
    {
        result = read(socket, buffer + bytesRead, x - bytesRead);
        if (result < 1 )
        {
            // Throw your error.
        }

        bytesRead += result;
    }
}
Gianluca Ghettini
  • 11,129
  • 19
  • 93
  • 159
  • Why `if (result < 1 )`? `read` returning zero doesn't necessarily imply an error. – u354356007 Nov 28 '17 at 15:41
  • 1
    because the call is blocking.. it exits with either something (>= 1) or connection close/error (<=0). To recap: >=1 data arrived, 0 = EOF, -1 = Error http://man7.org/linux/man-pages/man2/read.2.html – Gianluca Ghettini Nov 28 '17 at 15:43
2

Program A reported that all data had been sent correctly!

No. The return of write in program A only means that the returned number of bytes has been delivered to the kernel of operating system. It does not claim that the data have been send by the local system, have been received by the remote system or even have been processed by the remote application.

I am believing TCP is reliable.

TCP provides a reliability at the transport layer only. This means only that it will make sure that data loss gets detected (and packets re-transmitted), data duplication gets detected (and duplicates discarded) and packet reordering gets detected and fixed.

TCP does not claim any reliability of higher layers. If the applications needs this it has to be provided by the application itself.

Steffen Ullrich
  • 114,247
  • 10
  • 131
  • 172