每个linux的机器自多只有65535个端口, 为什么能做C1000K的连接?
因为 fd <-> tcb 是一一对应的关系, 而每个tcb都是由五元组来决定的,所以只要是五元组足够多,连接就可以建立;
五元组: remoteip, remoteport, localip, localport, proto
Posix API
TCB: TCP Control Block
linux中socket 都会被抽象成file(一切皆文件),TCB也会和file关联在一起,一个TCB就是一个TCP连接, TCB是一个抽象。TCB中包括 connection的状态、接收窗口、拥塞窗口、数据报文的序列号等等。
内核中为每个socket,都会创建2个缓冲区:
1. send buffer: 发送缓冲区
2. receive buffer: 接收缓冲区
1. socket()
socket会在内核创建一个fd 和一个TCB
2. bind(listenfd, struct sockaddr, sizeof(struct sockaddr)
绑定一个IP和端口(0.0.0.0:5678), 在内核中就和一个五元组关联在一个连接。
五元组: remoteip, remoteport, localip, localport, proto
3. listen()函数
listen(fd, backlog);
backlog的含义有2中说法:
1. 半连接队列+全连接队列的和不超过backlog
2. 全连接队列的和不超过backlog(一般情况下,backlog指的是这种说法)。
4. connect函数
connect函数会触发一个syn报文,在服务区端收到syn报文后,会为该连接建立一个TCB,并把TCB加入到半连接队列(syn队列),并发送syn + ack报文到客户端;
当客户端收到 syn + ack 报文后, 发送 ack 报文到服务器端,服务器端收到 ack 报文后会通过五元组从半连接队列中查找已经存在的TCB,找到后加入到全连接队列中。
- 半连接队列:也叫syn半连接队列
- syn半连接队列的大小是内核参数控制的,有些内核版本似乎也受listen(fd, backlog)函数的backlog参数的影响,取两者之间的最小值。 该内核参数是:
- linux@linux:~$ cat /proc/sys/net/ipv4/tcp_max_syn_backlog
-
256
linux@linux:~$
- syn半连接队列的大小是内核参数控制的,有些内核版本似乎也受listen(fd, backlog)函数的backlog参数的影响,取两者之间的最小值。 该内核参数是:
- 全连接队列:也叫accept半连接队列
- 该队列的大小是 backlog 和 内核参数共同决定的,取其最小值。该参数为:
-
linux@linux:~$ cat /proc/sys/net/core/somaxconn
128
linux@linux:~$
-
-
当accept队列满时,协议栈的行为有内核:/proc/sys/net/ipv4/tcp_abort_on_overflow 决定
-
tcp_abort_on_overflow 为 1:rver 在收到 SYN_ACK 的 ACK 包后,协议栈会丢弃该连接并回复 RST 包给对端,这个是 Client 会出现(connection reset by peer)错误。
-
tcp_abort_on_overflow 为 0:server 在收到 SYN_ACK 的 ACK 包后,直接丢弃该 ACK 包。这个时候 Client 认为连接已经建立了,一直在等 Server 的数据,直到超时出现 read timeout 错误。
-
- 该队列的大小是 backlog 和 内核参数共同决定的,取其最小值。该参数为:
acknum 1235, 表示确认已经收到1235之前的所有数据
TCP延迟ACK机制:
定义:TCP协议中,接收方成功接收到数据后,会回复一个ACK数据包,表示已经确认接收到ACK确认号前面的所有数据。ACK字段长度为32位,能表示0~2^32-1之间的值。
作用:发送方一定时间内,如果没有收到对端的ACK确认报文,会重传TCP报文。
ACK延迟确认机制:接收方收到数据后,并不会立即回复ACK,而是延迟一定时间。ACK延迟发送的时间一般200ms。通过系统的一个定时器每隔200ms来检查是否需要发送ACK,且可以合并发送或者伴随数据一起发送带有ACK的报文,已到达较低网络流量的目的。
5. accept函数
int clientfd = accept();
1. 从全连接队列中取出一个TCB结点
2. 为TCB结点分配一个fd返回。
6. send/recv 函数
UDP的使用场景:
1. 网络环境不好的情况下,重传较多的时候;
2. 数据的实时性要求较高的时候;
7. close 函数
close函数会触发tcp协议发送 fin 报文,
如果出现 fin_wait_1 状态,客户端会重传该报文;
如果服务器端出现大量的 close_wait 状态, 说明服务器端没有调用 close 函数,或者在接收处理过程和close 函数之间的处理过程时间比较长,修改把业务的逻辑处理抛到专门的线程去处理。
如果服务器出现 fin_wait_2 状态,如何解决?
设置TCP协议的keep alive,则可以解决该问题。
如果双方同时发送 fin 报文,
如果服务器出现大量的 time_wait 状态的报文,如何解决?
设置TCP连接复用 TCP Connection Reuse
vi /etc/sysctl.conf
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_tw_reuse = 1 表示开启重用TIME-WAIT sockets重新用于新的TCP连接
net.ipv4.tcp_tw_recycle = 1 TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。net.ipv4.tcp_timestamps = 1
net.ipv4.tcp_fin_timeout = 30 系統默认的TIMEOUT时间
sysctl -p