TCP不可靠吗?

时间:2017-11-28 15:25:04

标签: c sockets tcp

我相信TCP是可靠的。如果write(socket, buf, buf_len)close(socket)没有错误地返回,则接收方将收到buf长度为buf_len的完全相同的数据。

但是this article说TCP不可靠。

  

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);
     

测验问题 - 完成后B程序会打印什么?

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

遗憾的是,正确的答案是“D”。但这怎么可能发生呢?计划A.   报告说所有数据都已正确发送!

如果这篇文章属实,我必须改变主意。但我不确定这篇文章是否属实。

这篇文章是真的吗?

4 个答案:

答案 0 :(得分:4)

TCP 可靠(至少在较低级协议时),但程序员可能以不可靠的方式使用它。

这里的问题是在另一方没有正确接收到所有发送的数据之前不应该关闭套接字:关闭信号可能会在最后一个数据仍在传输之前破坏连接。

确保同伴正确接收的正确方法是graceful shutdown

答案 1 :(得分:3)

TCP / IP是可靠的,对于单词&#34;可靠&#34;的非常特殊(和有限)的含义。

具体来说,当write()返回1000000时,它会使你获得以下承诺:

  1. 您的1000000字节数据已被复制到TCP套接字的传出数据缓冲区中,从现在开始,TCP / IP堆栈负责将这些字节传递给远程程序。
  2. 如果这些字节可以交付(费用合理),那么即使传输的某些TCP数据包在传送过程中丢失,它们也会被传送。
  3. 如果字节确实传递,它们将准确有序地传递(相对于彼此以及相对于传递给同一套接字上的write()的先前和后续调用的数据)。
  4. 但也有一些保证,write()不会(并且,通常,不能)提供。特别是:

    1. write()无法保证接收应用程序在调用recv()获取所有1000000个字节之前不会退出或崩溃。
    2. write()不能保证接收应用程序将使用它接收的字节做正确的事情(即它可能只是忽略它们或错误处理它们,而不是对它们采取行动)
    3. write()不能保证接收应用程序永远都会调用recv()来recv()那些字节(即它可能只是保持套接字打开但从不调用它的recv())
    4. write()不能保证您的计算机和远程计算机之间的网络基础设施能够传递字节 - 即,如果某些数据包被丢弃,没有问题,TCP层将重新发送它们,但是例如如果有人将电源线从电缆调制解调器中取出,那么字节无法到达目的地,并且在尝试几分钟并且未能获得ACK之后,TCP层将给出up并错误连接。
    5. write()不能保证接收应用程序100%正确处理套接字关闭问题(如果它没有,那么你可以在远程程序之前发送任何数据)关闭套接字以便无声地丢弃,因为没有接收器可以接收它)
    6. write()不保证接收应用程序以与您发送的相同的段大小接收字节。即仅仅因为您在一次调用中发送了1000000个字节来发送()并且#39; t表示接收应用程序在一次调用recv()时可以接收1000000个字节。他可能会以任何大小的块接收数据,例如1-byte-per-recv() - 调用,或1000-bytes-per-recv-call,或TCP层感觉提供的任何其他大小。
    7. 请注意,在上面列出的所有情况中,write()返回后都会出现问题,这就是为什么write()只能在发生错误代码时返回错误代码的原因。 (稍后调用write()可能会返回错误代码,当然,这不会特别帮助您知道传递字节和未传递字节之间的界限位于何处)

      TLDR:TCP / IP比UDP更可靠,但不是100%的铁壳保证不会出错。如果您确实希望确保在接收端处理了您的字节,那么您将要对您的接收应用程序进行编程,以便发回某些更高级别的确认,表明它已收到(并成功处理!)您发送的字节它

答案 2 :(得分:2)

TCP / IP 提供可靠性,这意味着它允许重传丢失的数据包,从而确保(最终)接收到的所有数据您获得超时错误。要么你的东西交付,要么你出现超时错误

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

和顺便说一下,TCP / IP在某种程度上是可靠的,它可以保证在网络正常工作的情况下交付......如果你拔掉电缆,TCP / IP将无法提供你的数据包

P.S。你应该至少将指针推进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;
    }
}

答案 3 :(得分:2)

  

计划A报告所有数据都已正确发送!

没有。程序A中write的返回仅表示返回的字节数已经传递给操作系统的内核。它并未声称数据已由本地系统发送,已被远程系统接收或甚至已由远程应用程序处理。

  

我相信TCP是可靠的。

TCP仅在传输层提供可靠性。这意味着它只能确保检测到数据丢失(并重新发送数据包),检测到数据复制(并丢弃重复数据),并检测并修复数据包重新排序。

TCP没有要求任何更高层的可靠性。如果应用程序需要它,则必须由应用程序本身提供。