1. TCP协议
传输控制协议(TCP,Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793 定义。 TCP旨在适应支持多网络应用的分层协议层次结构。 连接到不同但互连的计算机通信网络的主计算机中的成对进程之间依靠TCP提供可靠的通信服务。
2. TCP协议的交互过程
3. 关于TCP协议的一些常见问题
3.1 为什么连接的时候是三次握手,断链的时候需要四次挥手?
很多人是不是这样想?为什么服务端的ACK
报文和FIN
报文都是分开发送的,但是在三次握手的时候却是ACK
报文和SYN
报文是一起发送的?
因为在三次握手的过程中,当服务端收到客户端的SYN
连接请求报文后,可以直接发送SYN+ACK
报文。其中ACK
报文是用来应答的,SYN
报文是用来同步的。
但是在关闭连接时,当服务端接收到FIN
报文时,很可能并不会立即关闭SOCKET
,所以只能先回复一个ACK
报文,告诉客户端,你发的FIN
报文我收到了,只有等到服务端所有的数据都处理完了,才发送FIN
报文,因此ACK
报文和FIN
报文不能一起发送。所以断开连接的时候才需要四次挥手来完成。
3.2 什么是半连接队列?
服务器第一次收到客户端的 SYN
之后,就会处于 SYN_RCVD
状态,此时双方还没有完全建立其连接,服务器会把此种状态下请求连接放在一个队列里,我们把这种队列称之为半连接队列。
当然还有一个全连接队列,就是已经完成三次握手,建立起连接的就会放在全连接队列中。如果队列满了就有可能会出现丢包现象。
这里在补充一点关于SYN-ACK
重传次数的问题:
服务器发送完SYN-ACK
包,如果未收到客户确认包,服务器进行首次重传,等待一段时间仍未收到客户确认包,进行第二次重传。如果重传次数超过系统规定的最大重传次数,系统将该连接信息从半连接队列中删除。
注意,每次重传等待的时间不一定相同,一般会是指数增长,例如间隔时间为 1s,2s,4s,8s…
server
重发SYN+ACK
包的次数,可以通过设置/proc/sys/net/ipv4/tcp_synack_retries
修改,默认值为5.
如果重发指定次数之后,仍然未收到 client
的ACK
应答,那么一段时间后,server
自动关闭这个连接。
3.3 如果第三次握手丢失了,客户端服务端会如何处理?
服务端发送完SYN-ACK
包,如果未收到客户确认包,服务器进行首次重传,等待一段时间仍未收到客户确认包,进行第二次重传。如果重传次数超过系统规定的最大重传次数,系统将该连接信息从半连接队列中删除。
客户端:在linux c 中,client 一般是通过 connect()
函数来连接服务器的,而connect()
是在 TCP
的三次握手的第二次握手完成后就成功返回值。也就是说 client
在接收到 SYN+ACK
包,它的TCP
连接状态就为 established
(已连接),表示该连接已经建立。那么如果 第三次握手中的ACK包丢失的情况下,Client
向 server
端发送数据,Server
端将以 RST
包响应,方能感知到Server
的错误。
3.4 什么是SYN攻击?
为了创建拒绝服务,攻击者利用这样的漏洞,即在接收到初始SYN
数据包之后,服务器将用一个或多个SYN / ACK
数据包进行响应,并等待握手中的最后一步。这是它的工作原理:
攻击者向目标服务器发送大量SYN
数据包,通常会使用欺骗性的IP地址。
然后,服务器响应每个连接请求,并留下开放端口准备好接收响应。
当服务器等待从未到达的最终ACK
数据包时,攻击者继续发送更多的SYN
数据包。每个新的SYN
数据包的到达导致服务器暂时维持新的开放端口连接一段时间,一旦所有可用端口被使用,服务器就无法正常工作。
3.5 四次挥手释放连接时,等待2MSL的意义
MSL
是Maximum Segment Lifetime
的英文缩写,可译为“最长报文段寿命”,它是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。
为了保证客户端发送的最后一个ACK
报文段能够到达服务器。因为这个ACK
有可能丢失,从而导致处在LAST-ACK
状态的服务器收不到对FIN-ACK
的确认报文。服务器会超时重传这个FIN-ACK
,接着客户端再重传一次确认,重新启动时间等待计时器。最后客户端和服务器都能正常的关闭。假设客户端不等待2MSL
,而是在发送完ACK
之后直接释放关闭,一旦这个ACK
丢失的话,服务器就无法正常的进入关闭连接状态。总结一下就是:
- 保证客户端发送的最后一个ACK报文段能够到达服务端。
- 防止“已失效的连接请求报文段”出现在本连接中。
4. 总结
《TCP/IP详解 卷1:协议》有一张TCP状态变迁图,很具有代表性,有助于大家理解三次握手和四次挥手的状态变化。如下图所示,粗的实线箭头表示正常的客户端状态变迁,粗的虚线箭头表示正常的服务器状态变迁。