TCP建立连接
TCP(Transmission Control Protocol)传输控制协议
,是一种面向连接、确保数据在端到端间可靠传输的协议。
面向连接是指在发送数居前,需要先建立一条虚拟的链路,然后让数据在这个链路上“流动”完成传输。为了确保数据的可靠传输,不仅需要对发出的每一个字节进行编号确认,校验每一个数据包的有效性,在出现超时情况时进行重传,还需要通过实现滑动窗口和拥塞控制等机制,避免网络状况恶化而最终影响数据传输的极端情形;
每次进行传输数据前,都需要通过三次握手来建立连接:
- A机器发出一个数据包并将
SYN
置1
,表示希望建立连接。这个包中的序列号假设是X; - B机器收到A机器发过来的数据包后,通过
SYN
得到这是一个建立连接的请求,于是发送一个响应包并将SYN
和ACK
标记都置1
。- 假设这个包中的序列号是
y
,而确认序列号必须是x+1
,表示收到了A发过来的SYN
。在TCP
中,SYN
被当作数据部分的一个字节;
- 假设这个包中的序列号是
- A收到B的响应包后需进行确认,确认包中将
ACK
置1
,并将确认序列号设置为y+1
,表示收到了来自B的SYN
;
为什么需要3次握手?
-
信息对等
,握手操作对于通讯双方而言,是用于确认双方相应的通讯的能力(自己发报文
,自己收报文
,对方发报文
,对方收报文
); -
防止出现请求超时导致脏连接
,网络报文的TTL
生存时间往往会超过TCP请求超时时间,如果两次握手就可以创建连接,传输数据并释放连接后,第一个超时的连接请求才到到达目标机器的话,目标机器会以为是请求机器创建新连接的请求,然后确认同意创建连接.
3次握手中,第二次握手回传了ACK,为什么还要回传SYN?
- ACK告知客户端,服务端接收正常
- SYN为了建立并确定从服务端到客户端的通信
从编程的角度看,
TCP
连接的建立是通过文件描述符(File Descriptor,fd)
完成的。通过创建套接字获得一个fd,然后服务端和客户端需要基于所获得的fd调用不同的函数分别进入监听状态和发起连接请求,由于fd的数量将决定服务端进程所能建立连接的数量。对于大规模分布式服务来说,当fd不足时就会出现"open too many files"错误而使得无法建立更多的连接。为此,需要注意调整服务端进程和操作系统所支持的最大文件句柄数;
通过使用
ulimit -n
命令来查看单个进程可以打开文件句柄的数量.
查看当前系统各个进程产生了多少句柄:
lsof -n | awk '{print $2}' | sort | uniq -c | sort -nr | more
-
TCP
在协议层面支持Keep Alive
功能,即隔段时间通过向对方发送数据表示连接处于健康状态。 - 不少服务将确保连接健康的行为放到了应用层,通过定期发送心跳包检查连接的健康度。一旦心跳包出现异常不仅会主动关闭连接,还会回收与连接相关的其他用于提供服务的资源,确保系统资源最大限度地被有效利用;
TCP断开连接
TCP是全双工通信,双方都能作为数据的发送方和接收方,但TCP连接也会有断开的时间。建立连接只有三次,而挥手断开则需要四次。
由于断开连接,需要等待已接受的数据处理完毕,才能断开,因此双方都要处于半关闭状态,等待双方作答,方可真正的关闭连接。
A机器想要关闭连接,则待本方数据发送完毕后,传递
FIN(finish)
信号给B机器。-
B机器应答
ACK
,告诉A机器可以断开,但是需要等B机器处理完数据,再主动给A机器发送FIN
信号。- A机器处于半关闭状态(
FIN_WAIT_2
),无法再发送新的数据。
- A机器处于半关闭状态(
-
B机器做好连接关闭前的准备工作后,发送
FIN
给A机器。- B机器也进入半关闭状态(
CLOSE_WAIT
)
- B机器也进入半关闭状态(
-
A机器发送针对B机器
FIN
的ACK
后,进入TIME_WAIT
状态;- 经过
2MSL(Maximun Segement Lifetime)
后,没有收到B机器传来的报文,则确立B机器已经收到A机器最后发送的ACK
指令,此时TCP
连接正式释放。
- 经过
TIME_WAIT
: 主动要求关闭的机器表示收到了对方的FIN
报文,并发送出了ACK
报文,进入TIME_WAIT
状态,等2MSL
后即可进入到CLOSED
状态。如果
FIN_WAIT_1
状态下,同时收到带FIN
标志和ACK
标志的报文时,可以直接进入TIME_WAIT
状态,而无须经过FIN_WAIT_2
状态。-
CLOSE_WAIT
: 被动要求关闭的机器收到对方请求关闭连接的FIN
报文,在第一次ACK
应答后,马上进入CLOSE_WAIT
状态。- 这种状态其实表示在等待关闭,并且通知应用程序发送剩余数据,处理现场信息,关闭相关资源。
在TIME_WAIT
等待的2MSL
是报文在网络上生存的最长的时间,超过阈值报文则被丢弃。
一般说来,MSL
大于TTL
衰减至0的时间。在RFC793
中规定MSL
为2分钟,但是在当前的高速网络中,2分钟的等待时间会造成资源的极大浪费,在高并发服务器上通常会使用更小的值。
既然
TIME_WAIT
貌似是百害而无一利,为何不直接关闭,进入CLOSE
状态呢?
- 确认被动关闭方能够顺利进入
CLOSED
状态。 - 假设最后一个
ACK
由于网络原因导致无法到达B机器,处于LAST_ACK
的B机器通常"自信"地以为对方没有收到自己的FIN+ACK
报文,所以会重发; - A机器收到第二次的
FIN+ACK
报文,会重发一次ACK
,并且重新计时。 - 如果A机器收到B机器的
FIN+ACK
报文后,发送一个ACK
给B机器,就"自私"地立马进入CLOSE
状态,可能会导致B机器无法确认收到最后的ACK
指令,也无法进入CLOSED
状态; - 防止失效请求。
四次挥手后客户端为什么还需要等待2MSL才关闭?
- 保证客户端发送的最后一个
ACK
报文能够到达服务器,因为这个ACK
报文可能丢失,站在服务器的角度看来,已经发送了FIN+ACK
报文请求断开了,客户端还没有给回应,应该是发送的请求未收到,于是服务器又会重新发送一次,而客户端就能在这个2MSL
时间段内收到这个重传报文接着给出回应报文,并且会重启2MSL
计时器。 - 防止类似与“第三次握手”中提到了的“已经失效的连接请求报文段”出现在本连接中。
TCP协议如何保证可靠传输?
1、TCP拆分应用数据,对发送的每一个包进行编号
2、校验和,检测数据在传输过程中的任何变化
3、流量控制,TCP的接收端只允许发送端发送接收端缓冲区能接纳的数据(滑动窗口)
4、拥塞控制,当网络拥塞时,减少数据的发送(慢启动、拥塞避免、快重传、快恢复)
5、ARQ协议,等待确认(自动重传协议)
6、超时重传