如何快速理解TCP协议
什么是TCP
TCP全称为”传输控制协议(Transmission Control Protocol)“。
1.TCP是在传输层中
2.TCP是有连接的协议;”三次握手和四次握手 “
3.TCP是可靠的
4.TCP是面向字节流的
5.沾包问题
TCP的报文格式
源端口号和目的端口号:表示数据从哪个进程来到哪个进程去。
32位序号和32位确认序号:TCP传输是面向字节流的且可靠的,会对每一个字节进行编号,确认序号;接收端收到了发送端的数据会返回该数据后一个序号,表示前面的数据以及收到,现在要从该序号开始发送(保证数据丢包之后可以知道);例如;发送端:发送了1001~2001的数据。接收端:应答的报文中的确认序号为1005,则表示1005之前的数据收到了,之后的数据没有收到,发送端则从1005序号的数据重新发送。
4位首部长度:描述TCP报头有都是个bit位(多少个字节)。比如:0101,表示该报头有15*4=60字节(可以通过4位首部长度对报头和有效载荷分离)
6位标志位:
URG:当发送端的报文中的URG标志位=1时,表示该数据非常紧急,则紧急指针有效
ACK:确认号是否有效
PSH:提示接收端应用程序立刻从TCP缓冲区把数据读走
RST:对方要求重新建立连接;我们把携带RST标识的称为复位报文段
SYN:请求建立连接;我们把携带SYN标识的为同步报文段
FIN:通知对方,本端要关闭了,我们称携带FIN标识位的为结束报文段。
16位窗口大小:发送端可发送的最大数据量。
16位校验和:提供额外的可靠性
16位紧急指针:标记紧急数据在数据字段中的位置
选项:参考资料
TCP的如何保证可靠性
确认应答(ACK)机制
超时重传机制
主机A发送给主机B的数据,可能因为网络拥堵等原因,数据无法到达主机B;
如果主机A在特定的时间间隔内没有收到主机B的的确认应答,则会重新发送;
但可能是主机A未收到主机B的确认应答。
那么主机A也会重新发送,主机B就会收到大量重复的数据。为了不占用资源,主机B要把重复的数据丢掉。这个时候我们可以利用序列号来去重。
TCP为了保证无论在什么环境下都能有比较高的性能通信,会动态计算最大超时时间。
Linux中(BSD Unix和Windows也是如此), 超时以500ms为一个单位进行控制, 每次判定超时重发的超时
时间都是500ms的整数倍.
如果重发一次之后, 仍然得不到应答, 等待 2*500ms 后再进行重传.
如果仍然得不到应答, 等待 4*500ms 进行重传. 依次类推, 以指数形式递增.
累计到一定的重传次数, TCP认为网络或者对端主机出现异常, 强制关闭连接
连接管理机制(三次握手和四次挥手)
三次握手
第一次握手:客户端发送SYN的报文给服务端,请求建立连接。(表示“可以听到吗?”)
第二次握手:服务端发送ACK+SYN报文给客户端(表示”我可以听的到,你呢?“)
第三次握手:客户端发送ACK报文给服务端(表示“我也可以听到”)
一旦三次握手成功,客户端和服务端就可以开始通信。
为何是三次握手呢?
三次握手是保证已经建立连接的最小次数。
减少服务端资源的浪费
客户端的状态转化:
CLOSED->SYN_SENT 客户端调用connect,发送同步报文段
SYN_SENT->ESTABLISHED connect调用成功,开始读写数据
服务端的状态转化:
CLOSED->LISTEN 服务端调用listen,等待客户端连接
LISTEN->SYN_RCVD 一旦监听到客户端的请求(同步报文段),就将该连接请求放入内核等待队列中,并向客户端发送SYN+ACK报文
SYN_RCVD->ESTABLISHED 服务端收到客户端的确认报文,可以进行读写数据
三次握手会不会出现丢包?
会,没有什么东西是一定可靠的。
第一次握手和第二次握手是不用担心的,它们有确认应答机制和超时重传机制。但是第三次握手是没有应答机制的,客户端不需要服务端对它发送确认应答报文,如果第三次握手的报文丢包了,那么服务端会对客户端发送含有RST的报文。让客户端重新请求连接。
四次挥手
第一次挥手:客户端;我要关闭连接了
第二次挥手:服务端;好的
第三次挥手:服务端;我要也压迫关闭连接了
第四次挥手:客户端;好的
前两次握手表示:客户端对服务端说“我已经没有什么可以给你的了”。服务端对客户端说“好的”。但是服务端可能还有一些数据没处理,这时候不能立即关闭服务端和客户端的通信管道。
后两次握手表示:服务端把数据处理好后,也对客户端说“那我也关闭了”。客户端对服务端说“好的”。等待一段时间后这时候双方的通信管道都关闭了。
四次挥手会不会出现丢包?
会,但是回应确认应答机制和超时重传机制。
TIME_WAIT状态:主动发起关闭通信管道一方在四次挥手完之后不会立即关闭通信管道。
会处于TIME_WAIT状态。因为,当被动关闭通信管道的一方在给主动一方发送FIN报文后,没有接收到主动方的ACK报文,那么被动方会触发超时重传机制。如果主动方立即关闭了通信管道,那么主动方就接收不到被动方因为丢包问题发送的FIN报文,就会导致被动方一直发送FIN报文,直到一定的时间后自动关闭通信管道,这样会浪费时间和资源。
TIME_WAIT会等待2MSL时间
MSL是TCP报文的最大生存时间, 因此TIME_WAIT持续存在2MSL的话
就能保证在两个传输方向上的尚未被接收或迟到的报文段都已经消失(否则服务器立刻重启, 可能会收到
来自上一个进程的迟到的数据, 但是这种数据很可能是错误的);
同时也是在理论上保证最后一个报文可靠到达(假设最后一个ACK丢失, 那么服务器会再重发一个FIN. 这
时虽然客户端的进程不在了, 但是TCP连接还在, 仍然可以重发LAST_ACK);
流量控制
TCP中有接收缓冲区和发送缓冲区。
接收端会把发送端发过来的报文先存放在接收缓存区中,接收端处理数据的速度是有限的,当接收端的接收缓冲区打满后,发送端继续发送数据会导致丢包等一系列的反应。
因此,TCP要对这种情况做出反应,要有处理能力。所以,接收端在对发送端做出响应的时候,也把该接收缓冲区的剩余大小也告诉对方,该接收缓冲区的剩余大小就是对应TCP头部“窗口大小”的字段。这个机制就叫做流量控制。
流量控制:发送端将自己可以接收的缓冲区的大小放入TCP头部的窗口大小字段中,通过ACK通知发送端。
在三次握手的时候,两端都会把自己的可以接收的接收缓冲区也填入窗口大小字段通过ACK通知对方。
如果接收缓冲区满了,就会将窗口大小设置为0,这是发送端不会再发送数据
但是需要在定期时间发送一个窗口探测数据包,使接收端把窗口大小告诉发送端
窗口更新的通知可能会丢包,为了防止这类事情造成的后果,发送端会时不时的发送窗口探测包。
拥塞控制
两端发送数据发生丢包是正常现象,每个协议都不可能保证百分百有效。但当发送端发送了1000份数据时,发生了900份数据丢包,这种情况就是网络状态比较拥堵,在不清楚网络状态的情况下,贸然发送大量的数据,很可能是雪上加霜。
TCP应对这个情况,采用了慢启动机制。
引入拥塞窗口的概念
发送开始的时候,拥塞窗口为1
每次收到ACK应答,拥塞窗口就会增加
每次发送数据包的时候, 将拥塞窗口和接收端主机反馈的窗口大小做比较, 取较小的值作为实际发送的窗口
拥塞窗口如何增加呢?
此处引入一个叫做慢启动的阈值
当拥塞窗口超过这个阈值的时候, 不再按照指数方式增长, 而是按照线性方式增长
少量的丢包, 我们仅仅是触发超时重传; 大量的丢包, 我们就认为网络拥塞
TCP如何提高性能
滑动窗口
TCP在保证了可靠性的同时,也要想办法把效率提高。
如果TCP传输数据时要等待应答了之后在传输的话,效率就太低了。我们要可以一次传输多份数据。
一次发送多份数据,其实就是将多个段的等待时间重叠在一起
滑动窗口无需等待确认应答可以继续发送数据,上面的窗口大小的4000字节
收到第一个应答后,滑动窗口向后移动
内核为了维护这个滑动窗口,开辟了发送缓冲区。
滑动窗口前部分:发送了,已经接收到了应答的数据。
滑动窗口部分:发送了,还没收到应答的数据
滑动窗口后部分:还没发送的数据
滑动窗口的大小也要看面对接收缓冲区剩余的大小和拥塞窗口的大小而确定,不要一定每次在收到确认应答时滑动窗口都会向后移动。
一次发送了多份数据,丢包了这么办?看下面的快重传
快速重传
情况一:ACK应答丢了。不要慌,根据后面的应答来判断是否收到。
情况二:当数据丢包了
超时重传是要等待一定的时间间隔后才开始。面对一次发送多份数据,明显性能不高。
而快重传面对一次发送多份数据的场景,更加有效。当收到三次一样的确认应答,就表示丢包了,再次重新发送一次丢包的数据。
延迟应答
如果接收数据的主机立刻返回ACK应答, 这时候返回的窗口可能比较小
假设接收端缓冲区为1M. 一次收到了500K的数据; 如果立刻应答, 返回的窗口就是500K;
但实际上可能处理端处理的速度很快, 10ms之内就把500K数据从缓冲区消费掉了;
在这种情况下, 接收端处理还远没有达到自己的极限, 即使窗口再放大一些, 也能处理过来;
如果接收端稍微等一会再应答, 比如等待200ms再应答, 那么这个时候返回的窗口大小就是1M;
一定要记得, 窗口越大, 网络吞吐量就越大, 传输效率就越高. 我们的目标是在保证网络不拥塞的情况下尽量提高传输效率
捎带应答
在两端进行通信的时候,也可以把数据捎带着ACK一并回给对方。
粘包问题
TCP和UDP不同,UDP的报文中有“报文长度”的字段,而TCP是没有的。所以,在把数据交付给应用层时,应用层看到的是一连串的字节数据,就不知道从哪个部分开始到哪个部分结束。着就有可能发生沾包问题。
TCP的沾包问题:
TCP中没有确定报文长度的字段
TCP是面向字节流的
TCP的一个一个报文是按序号排好序在缓冲区的,没有明确的分隔符
避免沾包问题:
对于定长的包,保证每次按照固定大小来读取
对于变长的包,约定一个包总长度的字段,从而直到结束的位置
对于变长的包, 还可以在包和包之间使用明确的分隔符
————————————————
版权声明:本文为CSDN博主「世_生」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。