网络编程实战
盛延敏
前大众点评云平台首席架构师
44207 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 40 讲
网络编程实战
15
15
1.0x
00:00/00:00
登录|注册

17 | TCP并不总是“可靠”的?

对端有FIN包发出,需要通过增强read或write操作的异常处理,帮助我们发现此类异常
对端无FIN包,需要通过巡检或超时来发现
阻塞的read操作在完成正常接收的数据读取之后,FIN包会通过返回一个EOF来完成通知
系统在崩溃之后又重启,会返回一个RST重置分节
TCP程序只能通过read和write调用得到网络连接异常的信息
可以通过给read操作设置超时来解决
TCP程序并不能及时感知到异常信息
服务器主机正常关闭,已连接的程序会发生什么
重新模拟实验
故障分为两大类
TCP并不是那么“可靠”的
对端有FIN包发出
系统崩溃造成的对端无FIN包
网络中断造成的对端无FIN包
TCP协议实现并没有提供给上层应用程序过多的异常处理细节
接收端无法保证ACK过的数据部分可以被应用程序处理
发送端无法获取对应数据流的ACK情况
思考题
总结
向一个已关闭连接连续写,最终导致SIGPIPE
通过write产生RST,read调用感知RST
read直接感知FIN包
故障模式总结
TCP是可靠的?
TCP并不总是“可靠”的?

该思维导图由 AI 生成,仅供参考

你好,我是盛延敏,这里是网络编程实战第 17 讲,欢迎回来。
在前面一讲中,我们讲到如何理解 TCP 数据流的本质,进而引出了报文格式和解析。在这一讲里,我们讨论通过如何增强读写操作,以处理各种“不可靠”的场景。

TCP 是可靠的?

你可能会认为,TCP 是一种可靠的协议,这种可靠体现在端到端的通信上。这似乎给我们带来了一种错觉,从发送端来看,应用程序通过调用 send 函数发送的数据流总能可靠地到达接收端;而从接收端来看,总是可以把对端发送的数据流完整无损地传递给应用程序来处理。
事实上,如果我们对 TCP 传输环节进行详细的分析,你就会沮丧地发现,上述论断是不正确的。
前面我们已经了解,发送端通过调用 send 函数之后,数据流并没有马上通过网络传输出去,而是存储在套接字的发送缓冲区中,由网络协议栈决定何时发送、如何发送。当对应的数据发送给接收端,接收端回应 ACK,存储在发送缓冲区的这部分数据就可以删除了,但是,发送端并无法获取对应数据流的 ACK 情况,也就是说,发送端没有办法判断对端的接收方是否已经接收发送的数据流,如果需要知道这部分信息,就必须在应用层自己添加处理逻辑,例如显式的报文确认机制。
从接收端来说,也没有办法保证 ACK 过的数据部分可以被应用程序处理,因为数据需要接收端程序从接收缓冲区中拷贝,可能出现的状况是,已经 ACK 的数据保存在接收端缓冲区中,接收端处理程序突然崩溃了,这部分数据就没有办法被应用程序继续处理。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文深入探讨了TCP协议的可靠性问题,并指出了一些常见的“不可靠”情况。作者首先解释了TCP协议并非完全可靠的原因,包括发送端无法获取对端数据流的ACK情况,以及接收端无法保证ACK过的数据部分可以被应用程序处理等问题。文章总结了网络中断和系统崩溃造成的对端无FIN包的情况,并提出了相应的解决方法。此外,还详细讨论了对端有FIN包发出的情形,并给出了相关的代码示例。通过实验验证,作者展示了通过read和write操作来感知异常情况的方式,以及对应的处理方式。总的来说,本文通过具体的案例和技术细节,深入剖析了TCP协议的可靠性问题,为读者提供了深入了解TCP协议的实用知识。文章内容丰富,对TCP协议的异常情况进行了详细分析,对读者了解TCP协议的实际应用具有重要参考价值。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《网络编程实战》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(36)

  • 最新
  • 精选
  • 传说中的成大大
    看到后面我好像理解了我上面那个提问,当崩溃重启过后是重新三次握手建立连接,创建新的套接字,只是在网络上传输的包,因为是通过ip地址和端口方式进行的寻址,所以新连接上去的客户端会接收到之前还没接收到的包,然后新连接的客户端没有这些包的tcp分组信息所以就会给服务器端(对端)发送一个RST

    作者回复: 正解。

    2019-09-09
    4
    18
  • melon
    最后的例子没有触发SIGPIPE,是因为老师例子设计的有点儿瑕疵。client 在不断的发送数据,server 则每次接受数据之后都 sleep 一会,也就导致接收速度小于发送速度,进而导致 server 终止的时候接收缓冲区还有数据没有被读取,server 终止触发 close 调用,close 调用时如果接收缓冲区有尚未被应用程序读取的数据时,不发 FIN 包,直接发 RST 包。client 每次发送数据之后 sleep 2秒,再试就会出SIGPIPE 了。

    作者回复: 所以需要在客户端程序里加sleep就可以了?

    2020-07-15
    3
    17
  • linker
    我的电脑,结果也是一样的, 第二个问题,服务器正常关闭,客户端应该是受到了fin包,read返回eOF,wirte返回rst

    作者回复: 说明新版的内核在这些特性上有所改进。

    2020-03-21
    2
    9
  • 盘尼西林
    没有理解 reset by peer 和 broken pipe 的区别。。

    作者回复: 一个是TCP RST的状态异常,一个是SIGPIPE 信号将进程进行回收。

    2020-01-06
    9
  • tim
    >>"但是,发送端并无法获取对应数据流的 ACK 情况" 对上面这段话不理解,TCP 的 ACK不是带着序号的吗?发送端根据这个序号能计算出是哪次发送的ACK。 哪位大牛能解释一下吗?

    作者回复: 这里是从应用层报文数据角度出发来说的,因为数据是一个流,没有办法判断数据的哪些部分没对端收到。 从TCP角度来说,确实是通过ACK来感知TCP包的接收情况的。

    2019-10-09
    4
    5
  • geraltlaush
    老师,你文章的案例默认fd都是阻塞的吧,如果是非阻塞的话,返回的n < 0 不一定是错误啊

    作者回复: 到现在为止,都是阻塞的,后面会切成非阻塞的。

    2019-10-13
    2
    3
  • Jason
    error(1, 0, "usage: reliable_client01 <IPaddress>");这个error函数,具体是什么作用?

    作者回复: 根据错误码打印错误信息,并且可以退出当前程序。

    2020-07-06
    2
  • yang
    老师 我提一个read直接感知FIN包的疑问哈: 我停留在 stdin这里 等我输入完之后,就能调用read感知到对端已经关闭了呀? 是因为等到stdin之后,再感知是不是太晚了呀?

    作者回复: 这就是为什么需要使用select、poll等事件分发机制,正确的解法是一旦有事件发生,比如这里read读到EOF,就应用直接去处理此类事件,而不是阻塞在这里等待用户的输入。好消息是,很快我们就会详细学习这部分内容了。

    2019-09-16
    2
  • 徐凯
    第二题 客户端--------服务器 1.  客户端发送FIN包,处于发送缓冲区的数据会逐一发送(可能通过一次或多次write操作发送),FIN包处于这段数据的末尾,当数据到达接收端的接收缓冲区时,FIN起到了一个结束符的作用,当接收端接收数据时遇到FIN包,read操作返回EOF通知应用层。然后接收端返回一个ACK表示对这次发送的确认。(此时客户端进入FIN_WAIT1,服务端进入CLOSE_WAIT状态) 2.  客户端接收到ACK之后,关闭自己的发送通道,客户端此时处于半关闭状态。等待服务器发送FIN包。   (客户端进入FIN_WAIT2状态) 3.  服务端发送FIN包,同上类似处于发送缓冲区的内容会连同FIN包一起发过去,当客户端接收成功后同时将FIN解析为EOF信号使得上层调用返回。(客户端进入TIME_WAIT状态 服务端进入LAST_ACK状态) 4. 客户端等待2MSL的时间,在此期间向服务器发送ACK。如果丢包进行重传。如果服务器收到ACK后 服务器进入CLOSED状态 客户端也进入CLOSED状态。 5. 连接关闭 我想问一下 如果最后一次挥手一直丢包 在2MSL的时间内都没到 TCP会咋办 会重置计时器么 还是就不管了直接关闭呢

    作者回复: 我猜想是会直接关闭的,没有对ACK的ACK包。

    2019-09-11
    2
    1
  • yusuf
    # uname -a Linux tst 3.10.0-957.21.3.el7.x86_64 #1 SMP Tue Jun 18 16:35:19 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux # # ./reliable_client01 127.0.0.1 good peer connection closed # # ./reliable_client01 127.0.0.1 bad bad bad2 peer connection closed # # ./reliable_client02 127.0.0.1 send into buffer 19 send into buffer -1 send error: Connection reset by peer (104)

    作者回复: 基本和我的Linux下结果一致。

    2019-09-09
    1
收起评论
显示
设置
留言
36
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部