🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
### 建立TCP连接的过程 > 三次握手建立TCP连接 - 第一次握手:客户端向服务器发送1个连接请求的报文段。 - 第二次握手:服务器收到请求连接报文段后,若同意建立链接,则向客户端发送连接确认的报文段。 - 第三次握手:客户端收到确认报文后,向服务器再次发出连接确认报文段。 - 成功进程TCP的三次握手后,就建立了一条TCP连接,即可传送应用层的数据了。 ![](https://img.kancloud.cn/84/3c/843cbddd926d5da53693df102e79ff54_1930x668.webp) > 为什么TCP建立连接时需要三次握手 - 结论:防止服务器端因接收了已经失效的链接请求报文,从而一直等待客户端请求。最终导致形成死锁,浪费资源。 - 解决方法:建立链接时采用三次握手,即关键的是第三次握手。客户端不会向服务端的确认报文再次发送确认。服务器由于收不到客户端的确认信息,即知道客户端并无要求建立TCP连接。故服务器不会一直等待客户端发送数据,即不会形成死锁状态。 ![](https://img.kancloud.cn/f1/59/f1597b48cba6de49945a2ba9eb5cfce1_857x335.webp) > SYN 洪泛攻击 > - 服务端TCP资源分配时刻是在完成第二次握手的时候。而客户端TCP资源分配时刻是在完成第一次握手的时候。 > - 这就是的服务器容易收到SYN洪泛攻击,即同时多个客户端发送连接请求,从而需要进行多个请求的TCP连接的资源分配。 ### 释放TCP连接的过程 > 在通信结束后,双方都可以释放连接,共需四次挥手。 - 客户端向服务端发送一个连接释放的报文。 - 服务器收到连接释放的报文后,则向客户端发回连接释放的确认报文。 - 若服务器已无要向客户端发送数据,则发出释放连接的报文。 - 客户端收到连接释放报文后,则向服务器发回连接释放确认的报文段。 ![](https://img.kancloud.cn/d3/38/d338f41a74b5e01281009cfe68870c09_950x843.webp) ![](https://img.kancloud.cn/32/cb/32cbcaffb29336225ea36740ea510018_1020x700.webp) > 为什么TCP释放连接需四次挥手 - 结了:为了双方都能通知对方需释放,断开连接。 ![](https://img.kancloud.cn/85/b6/85b6d892108fddc9ab52109352f7b84d_816x293.webp) > 为什么客户端关闭连接前需要等待2MSL时间? - 原因:为了保证客户端发送的最后1个连接释放确认报文能到达服务器,从而是的服务器能正常释放连接。 ![](https://img.kancloud.cn/37/1e/371e54cbe0d020dfc3abd2ebbe75854f_880x287.webp) ### 数据传输过程 应用层发送数据是把数据放到了操作系统的TCP发送缓存中。操作系统发送时,去TCP发送缓存中取数据组成TCP数据包。接收端接收解析后,把字节流按顺序放入TCP接收缓存,每次从缓存中取一定的字节数据交个应用层。 ### 如何实现TCP可靠传输 * **自动重传协议(ARQ)** 发送端发送一个数据之后,等待接收端响应成功接收的确认后,再发送下一个数据包(停止等协议) 一定时间内没有收到确认,发送端重传这个数据包(超时重传),最大等待时间略大于往返时延。所以重传是自动进行的。 如果接收端发送的确认丢失,或者确认迟到即确认没有在最大等待时间内到达发送端,这两种情况也都会导致发送端进行超时重传。这种情况下,接收端会丢弃重复的数据。所以TCP协议还支持**去重**的功能 通俗来说,ARQ协议就是,**只要你没在一定时间内告诉我收到了,我就认为你没收到。** 停止等待协议实现简单,但是信道利用率低。因为发送数据的时间要远小于等待的时间。 * **流水线传输** 上面提到的ARQ传输协议的信道利用率非常低,已经被淘汰。目前TCP协议中采用流水线传输。即发送端发送一个数据包后,不等待接收端的ACK就立即发送下一个准备好的数据包。当一个包发送了时间x之后,还没收到收到这个包的ACK,就会重传这个包。x的取值略大于网络的往返时延TTL。假设在时间x内,可以发送n个数据包,那么发送端必须要缓存n个还没有收到ack的数据包。即发送端维护一个发送窗口。 ![](https://img.kancloud.cn/d6/8c/d68c1940fbaba3a2b272f3fe5fd88cd4_588x131.png) 发送窗口内是发送了还没有收到ACK的数据包,当发送窗口内有数据收到ACK之后,就可以被移出窗口,并且窗口左边沿向前推进。当窗口内的数据大于窗口的最大长度时,就不能继续发送数据了,需要等待ACK或者进行重传。 举个例子,上图中假如滑动窗口的大小是5,此时窗口已经被占满。有两种情况,如果收到了数据包1的ACK那么数据包1被溢出窗口,窗口向前推进继续发送第6个包。如果没收到ACK,会重传数据包1,其实窗口也会向前推进,次数数据包6的内容跟数据包1的内容相同,即数据包1的重传。 **累计确认**是对上述机制的进一步优化,即接收端如果收到了连续的数据包1、2、3,只需要在ACK里回复收到了3,发送端就可以隐式的知道1、2、3都发送成功了。