提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
TCP半连接队列
TCP三次握手的过程中,操作系统内核会维护两个队列
- 半连接队列,syn队列
- 全连接队列,accept队列
- 服务端收到客户端发起的 SYN 请求后,内核会把该连接存储到半连接队列,并向客户端响应 SYN+ACK
- 接着客户端会返回 ACK,服务端收到第三次握手的 ACK 后,内核会把连接从半连接队列移除,然后创建新的完全的连接,并将其添加到 accept 队列,等待进程调用 accept 函数时把连接取出来。
accept()函数则是从全连接队列中取出连接
不管是半连接队列还是全连接队列,都有最大长度限制,超过限制时,内核会直接丢弃,或返回 RST 包。
backlog
在Linux内核2.2之前,backlog大小包括半连接状态和全连接状态两种队列大小。
在Linux内核2.2之后,分离为两个backlog来分别限制半连接(SYN_RCVD状态)队列大小和全连接(ESTABLISHED状态)队列大小。
SYN queue 队列长度由 /proc/sys/net/ipv4/tcp_max_syn_backlog 指定,默认为2048。
Accept queue 队列长度由 /proc/sys/net/core/somaxconn 和使用listen函数时传入的参数,二者取最小值。默认为128。
服务器所能建立的最大TCP连接数
建立TCP连接,实际是两个进程之间 通过套接字进行通信,即socket1 <——> socket2
套接字信息如下
socket1<源ip、源port>
socket2<目标ip、目标端口>
连接数分析如下:
1、进程首次建立tcp连接之后,操作系统会返回一个文件描述符 socketfd,该进程的一切读写对文件进行操作
2、该进程重复对 同一个目标主机 同一个目标端口发起tcp连接建立,此操作中,源端口是变化的
3、直到报错,如下,此时说明本机上的可用端口被占据完了
常用端口号是 0-1023
网络编程可分配端口为 1024-65535 ,这里查看下linux中端口的范围,发现网络编程可创建端口为 61000-32768个
可以对配置文件进行修改,扩大可用端口范围
4、继续建立tcp,直到所有可用端口都被使用
5、此时换个目标端口继续建立tcp,因为socket 这套绑定关系构成的四元组不重复即可,刚刚端口号不够用了,是因为我一直对同一个目标IP和端口建立连接,那我换一个目标端口号试试
6、继续不同的建立tcp,直到出现
表示,可以使用的文件描述符已经达到了最大限制
linux 对可打开的文件描述符的数量分别作了三个方面的限制
-
系统级:当前系统可打开的最大数量,通过 cat /proc/sys/fs/file-max 查看
-
用户级:指定用户可打开的最大数量,通过 cat /etc/security/limits.conf 查看
-
进程级:单个进程可打开的最大数量,通过 cat /proc/sys/fs/nr_open 查看
可以对文件描述符限制进行修改
7、继续建立tcp,此时发现从发起tcp连接请求,到OS返回文件描述符,这个过程所使用的时间越来越长。
原因:每个tcp连接都需要一个线程进行服务,tcp连接过多,线程过多,线程之间的切换时间开销大,此时采用IO多路复用的方式管理tcp连接。
8、继续建立tcp连接,直到报错
原因:每个tcp连接需要使用socket,socket的缓冲区需要占用内存。由于socket数量过大,导致内存溢出
解决:kill 无用进程,腾出内存
9、继续建立tcp连接,CPU 使用率急剧变高,导致用户执行响应时间变长
从以上分析可以看出,TCP连接所占用的资源