TCP(Transmission Control Protocol)
传输控制协议
🔎TCP协议的特点
(1)有连接
(2)可靠传输
(3)面向字节流
(4)全双工
🔎TCP协议段格式
图片来自网络
URG: 确认紧急指针是否有效
ACK: 确认序号是否有效
PSH: 提示接收端应用程序立刻从 TCP 缓冲区把数据读走
RST: 对方要求重新建立连接, 就把携带 RST 标识的称为 复位报文段
SYN: 对方请求建立连接, 就把携带 SYN 标识的称为 同步报文段
FIN: 通知对方, 本端要关闭了, 就把携带 FIN 标识的称为 结束报文段
🔎TCP协议的相关特性
确认应答(ACK)
主机A 向主机B 发送数据, 主机B 收到数据后返回给主机A一个响应(确认应答)
举个栗子🥝
滑稽老哥约女神出去吃饭
于是就给女神发消息说, 女神女神, 我请你吃饭好吗
这时候女神收到滑稽老哥发送到的消息并返回一个好呀好呀(确认应答)
滑稽老哥一琢磨, 既然女神上次都同意和我一起吃饭了, 那这次我约女神是不是也会和我一起吃饭呢
滑稽老哥想着想着, 既然女神上次都同意和我一起吃饭了, 那是不是也同意做我女朋友了呢
于是滑稽老哥就给女神发送消息说
女神女神, 我能请你吃饭吗?
女神女神,你能做我女朋友吗?
结果不出所料, 女神发送的是一个滚
但是由于网络的原因
滑稽老哥最先收到的是滚, 后面才收到好呀好呀
也就是说
滑稽老哥接收的情况是
女神, 我能请你吃饭吗? 滚
女神, 你能做我女朋友吗? 好呀好呀
但实际情况却是
女神, 我能请你吃饭吗? 好呀好呀
女神, 你能做我女朋友吗? 滚
为了解决上述问题, 就需要针对发送的消息进行编号
给发送的消息分配一个序号, 同时应答报文, 给出确认序号
这样滑稽老哥就不会错解女神的意思了
TCP 将每个字节的数据都进行了编号, 即为序列号
(针对每个字节进行编号)
超时重传
有两种情况会导致超时重传
(1)数据直接丢了, 接收方没有收到, 自然不会确认应答(ACK)
(2)接收方收到了数据, 返回的 ACK 丢了
情况1
数据直接丢了, 接收方没有收到, 自然不会确认应答(ACK)
情况2
接收方收到了数据, 返回的 ACK 丢了
针对上述情况
如果接收方收到重复数据
TCP 会在接收缓冲区根据收到的数据的序号, 自动去重
那么, 如果重传的数据也丢了呢?
这种情况就属于第一种情况(数据丢了, 接收方没有收到)
TCP 会继续超时重传
数据每丢失一次, 等待重传的时间都会变长(重传的频率降低了)
如果多次重传, 都无法得到 ACK, 此时 TCP 就会尝试重置连接
如果重置连接也失效, TCP 就会关闭连接, 放弃网络通信
三次握手
握手指的是通信双方, 进行一次网络交互
三次握手, 相当于客户端与服务器之间, 进行了三次交互, 建立了连接关系
syn: 同步报文段
(一方向另一方申请建立连接)
ack: 确认应答
(一方收到另一方的请求后给予的响应)
举个栗子🥝
滑稽老哥和女神开黑打游戏
为了确保能够开黑顺利, 双方需要判断通信设备是否正常
(1)
滑稽老哥向女神发出"喵喵喵"(syn)
(判断自己的麦克风🎤及女神的耳机🎧是否正常)
(2)
女神向滑稽老哥发出"喵喵"(ack + syn)
(表示自己的耳机🎧及滑稽老哥的麦克风🎤正常)(ack)
(判断自己的麦克风🎤及滑稽老哥的耳机🎧是否正常)(syn)
(3)
滑稽老哥再次向女神发出"喵"(ack)
(表示女神的麦克风🎤及自己的耳机🎧正常)
经过上面的三次连接, 可以判断通信双方的通信设备是否正常
三次握手本质上就是验证客户端和服务器各自的发送能力和接收能力是否正常
为后续的可靠传输奠定基础
注
三次握手的过程由内核自动完成, 应用程序无法进行干预
建立连接的过程一定是客户端发起的
四次挥手
挥手是指通信双方, 各自给对方发送一个 fin, 再各自给对方返回 ack
fin: 结束报文
(一方向另一方表示断开连接)
ack: 确认应答
(一方收到另一方的请求后给予的响应)
举个栗子🥝
滑稽老哥终于如愿追到了女神
但过了一些日子, 滑稽老哥还是觉得单身的日子比较快乐
于是他就向女神提出分手
(1)
滑稽老哥向女神提出分手(fin)
(滑稽老哥想恢复单身的快乐)
(2)
女神对滑稽老哥表示震惊(ack)
(女神表示当初你追我时的那些话都是骗人的, 一脸震惊的看待提出分手的滑稽老哥)
女神对滑稽老哥表示那就分手!(fin)
(女神表示既然你说分手, 那就分, 反正追我的人有很多)
滑稽老哥看着同意分手的女神表示很快乐(ack)
(滑稽老哥如愿恢复单身)
注
断开连接可以是客户端向服务器断开连接, 也可以是服务器向客户端断开连接
四次挥手有一定几率变成三次挥手
(也就是 ack 和 fin 合并发送)
三次挥手与四次握手的注意事项
为什么三次握手能将 ack 和 syn 合并
而四次挥手不一定能将 ack 和 fin 合并
因为三次挥手的过程中, ack 和 syn 是由内核来完成的
而四次挥手过程中
ack 是由内核完成的, fin 是由应用程序代码控制的
ack 会在收到 fin 的时候第一时间返回
fin 则是由代码调用到 socket.close() 的时候才会触发
(如果此时的 close 方法前面还有其他任务需要执行, 比如 sleep() 等, 就会造成不能及时的发送 fin)
🔎结尾
创作不易,如果对您有帮助,希望您能点个免费的赞👍
大家有什么不太理解的,可以私信或者评论区留言,一起加油