TCP不可靠吗?

N.F*_*.F. 2 c sockets tcp

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

但是这篇文章说TCP不可靠。

A:

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

乙:

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);
Run Code Online (Sandbox Code Playgroud)

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

A) 1000000 
B) something less than 1000000 
C) it will exit reporting an error 
D) could be any of the above
Run Code Online (Sandbox Code Playgroud)

遗憾的是,正确答案是“D”。但这怎么会发生呢?程序 A 报告所有数据都已正确发送!

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

这篇文章是真的吗?

Ser*_*sta 5

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

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

确保对等方正确接收的正确方法是正常关闭


Jer*_*ner 5

TCP/IP 是可靠的,因为“可靠”一词具有非常特殊(且有限)的含义。

具体来说,当 write() 返回 1000000 时,它向您做出以下承诺:

  1. 您的 1000000 字节数据已被复制到 TCP 套接字的传出数据缓冲区中,从现在开始,TCP/IP 堆栈负责将这些字节传送到远程程序。
  2. 如果这些字节可以被传送(通过合理的努力),那么它们就会被传送,即使某些传输的 TCP 数据包在传送过程中被丢弃。
  3. 如果字节确实被传递,它们将被准确且按顺序传递(相对于彼此,也相对于传递给同一套接字上的先前和后续调用 write() 的数据)。

但也有一些 write() 没有(并且通常不能)提供的保证。尤其:

  1. write() 无法保证接收应用程序在调用 recv() 获取全部 1000000 个字节之前不会退出或崩溃。
  2. write() 不能保证接收应用程序将对其接收到的字节执行正确的操作(即它可能只是忽略它们或错误处理它们,而不是对它们进行操作)
  3. write() 不能保证接收应用程序会调用 recv() 来接收这些字节(即它可能只是保持套接字打开,但永远不会对其调用 recv() )
  4. write() 不能保证您的计算机和远程计算机之间的网络基础设施能够正常传送字节——即,如果某些数据包丢失,没问题,TCP 层将重新发送它们,但例如,如果有人拔掉了电源线如果数据从电缆调制解调器中出来,那么字节根本无法到达目的地,并且在尝试几分钟但未能获得 ACK 后,TCP 层将放弃并错误地断开连接。
  5. write() 不能保证接收应用程序 100% 正确地处理套接字关闭问题(如果不能,那么您在远程程序关闭套接字之前发送的任何数据都可能会被静默删除,因为有没有剩余接收器来接收它)
  6. write() 不保证接收应用程序会以与您发送它们相同的段大小接收字节。即仅仅因为您在一次调用 send() 中发送了 1000000 字节并不意味着接收应用程序可以在一次调用 recv() 中接收 1000000 字节。他可能会以任意大小的块接收数据,例如每次recv() 调用1 字节或每次recv 调用1000 字节,或者TCP 层认为提供的任何其他大小。

请注意,在上面列出的所有情况下,问题都是在 write() 返回后发生的,这就是为什么 write() 在发生这些情况时不能只返回错误代码。(当然,稍后调用 write() 很可能会返回错误代码,但这并不能特别帮助您了解已交付字节和未交付字节之间的线位于何处)

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