连接
1. TIME-WAIT
Linux 系统停留在 TIME_WAIT 的时间为固定的 60 秒。
#define TCP_TIMEWAIT_LEN (60*HZ)
/* how long to wait to destroy TIME-WAIT state, about 60 seconds */
只有发起连接终止的一方会进入 TIME_WAIT 状态。2MSL 的时间是从主机 1 接收到 FIN 后发送 ACK 开始计时的。
作用:
- 确保最后的报文可以让被动方关闭连接
- 为了让旧连接的重复分节在网络中自然消失
危害:
- 占用资源
- 消耗端口
如何优化:
- 调低 TCP_TIMEWAIT_LEN,重新编译系统
- net.ipv4.tcp_tw_reuse,重用
2. 半连接
两次挥手后,TCP连接进入半连接状态
close
int close(int sockfd)
这个函数会对套接字引用计数减一,一旦发现套接字引用计数到 0,就会对套接字进行彻底释放,并且会关闭TCP 两个方向的数据流。
系统内核分别在输入和输出方向close:
-
输入套接字设置为不可读
-
系统内核尝试将发送缓冲区的数据发送给对端,并最后向对端发送一个 FIN 报文。
如果对端没有检测到套接字已关闭,还继续发送报文,就会收到一个 RST 报文。
close是关闭了双向的连接
shutdown 函数
int shutdown(int sockfd, int howto)
howto:
- SHUT_RD(0):关闭连接的“读”这个方向
- SHUT_WR(1):关闭连接的“写”这个方向,进入半连接状态
- SHUT_RDWR(2):相当于 SHUT_RD 和 SHUT_WR 操作各一次,关闭套接字的读和写两个方向
3. 长连接
3.1 TCP Keep-Alive 选项
保活时间、保活时间间隔和保活探测次数,默认设置是 7200 秒(2 小时)、75 秒和 9 次探测。
net.ipv4.tcp_keepalive_time
net.ipv4.tcp_keepalive_intvl
net.ipv4.tcp_keepalve_probes
3.2 应用层探针
通过应用程序中模拟 TCP Keep-Alive 机制,来完成在应用层的连接探活。
需要保活的一方,比如客户端,在保活时间达到后,发起对连接的探测操作:
- 如果有回应,则重置保活时间
- 否则,计数探测次数(大于预先设置的值,认为连接已经断开)
4. 动态数据传输
在任何一个时刻,TCP 发送缓冲区的数据是否能真正发送出去,至少取决于两个因素,一个是当前的发送窗口大小,另一个是拥塞窗口大小,而 TCP 协议中总是取两者中最小值作为判断依据。
4.1 发送窗口和接收窗口
TCP 的生产者 - 消费者
发送窗口和接收窗口是 TCP 连接的双方,一个作为生产者,一个作为消费者,为了达到一致协同的生产 - 消费速率、而产生的算法模型实现。
4.2 拥塞控制
发送窗口和接收窗口是考虑了单个连接的数据传递,拥塞控制则是考虑多个连接共享在有限的带宽上,兼顾效率和公平性的控制。
常用算法:
- 慢启动:慢慢地将网络发送数据的速率增加到一个阈值
- 拥塞避免:到达阈值之后,进入拥塞避免,不断的调整拥塞窗口的大小
4.3 有效利用带宽
- 糊涂窗口综合症:接收端不能在接收缓冲区空出一个很小的部分之后,就向发送端读取数据
- 小数据: Nagle算法(限制大量小数据包同时发送),一个tcp连接上最多只能有一个未被确认的未完成的小分组
- 延时ACK:累计确认
5. UDP的connect
可以通过对 UDP 套接字进行 connect 操作,将 UDP 套接字建立了”上下文“,该套接字和服务器端的地址和端口产生了联系。
客户端通过 connect 绑定服务端的地址和端口,对 UDP 而言,可以有一定程度的性能提升。