网络IO

传统BIO

从系统调用看tcp三次握手

详细显示三次握手,服务端和客户端的系统操作流程
服务端代码
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; public class SocketIOPropertites { //server socket listen property: private static final int RECEIVE_BUFFER = 10; private static final int SO_TIMEOUT = 0; private static final boolean REUSE_ADDR = false; private static final int BACK_LOG = 2; //client socket listen property on server endpoint: private static final boolean CLI_KEEPALIVE = false; private static final boolean CLI_OOB = false; private static final int CLI_REC_BUF = 20; private static final boolean CLI_REUSE_ADDR = false; private static final int CLI_SEND_BUF = 20; private static final boolean CLI_LINGER = true; private static final int CLI_LINGER_N = 0; private static final int CLI_TIMEOUT = 0; private static final boolean CLI_NO_DELAY = false; /* StandardSocketOptions.TCP_NODELAY StandardSocketOptions.SO_KEEPALIVE StandardSocketOptions.SO_LINGER StandardSocketOptions.SO_RCVBUF StandardSocketOptions.SO_SNDBUF StandardSocketOptions.SO_REUSEADDR */ public static void main(String[] args) { ServerSocket server = null; try { server = new ServerSocket(); server.bind(new InetSocketAddress(9090), BACK_LOG); server.setReceiveBufferSize(RECEIVE_BUFFER); server.setReuseAddress(REUSE_ADDR); server.setSoTimeout(SO_TIMEOUT); } catch (IOException e) { e.printStackTrace(); } System.out.println("server up use 9090!"); try { while (true) { System.in.read(); //分水岭: Socket client = server.accept(); //阻塞的,没有 -1 一直卡着不动 accept(4, System.out.println("client port: " + client.getPort()); client.setKeepAlive(CLI_KEEPALIVE); client.setOOBInline(CLI_OOB); client.setReceiveBufferSize(CLI_REC_BUF); client.setReuseAddress(CLI_REUSE_ADDR); client.setSendBufferSize(CLI_SEND_BUF); client.setSoLinger(CLI_LINGER, CLI_LINGER_N); client.setSoTimeout(CLI_TIMEOUT); client.setTcpNoDelay(CLI_NO_DELAY); //client.read //阻塞 没有 -1 0 new Thread( () -> { try { InputStream in = client.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(in)); char[] data = new char[1024]; while (true) { int num = reader.read(data); if (num > 0) { System.out.println("client read some data is :" + num + " val :" + new String(data, 0, num)); } else if (num == 0) { System.out.println("client readed nothing!"); continue; } else { System.out.println("client readed -1..."); System.in.read(); client.close(); break; } } } catch (IOException e) { e.printStackTrace(); } } ).start(); } } catch (IOException e) { e.printStackTrace(); } finally { try { server.close(); } catch (IOException e) { e.printStackTrace(); } } } }
新增启动脚本server.sh 并授权chmod 775 server.sh
rm -fr *out* /usr/local/java/jdk8u292-b10/bin/javac SocketIOPropertites.java strace -ff -o out /usr/local/java/jdk8u292-b10/bin/java SocketIOPropertites
1、启动服务端
notion image
此时观察服务端所在主机的网络.发现 9090的端口已经处于listen状态, 利用tcpdump抓包发现并没有数据,
[root@0210ed315129 ~]# netstat -natp Active Internet connections (servers and established) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 31/sshd tcp 0 0 0.0.0.0:9090 0.0.0.0:* LISTEN 441/java tcp 0 0 172.17.0.3:22 172.17.0.1:57238 ESTABLISHED 32/sshd: root@pts/1 tcp 0 0 172.17.0.3:22 172.17.0.1:57842 ESTABLISHED 57/sshd: root@pts/2 tcp6 0 0 :::22 :::* LISTEN 31/sshd [root@0210ed315129 ~]# tcpdump -nn -i eth0 port 9090 tcpdump: verbose output suppressed, use -v or -vv for full protocol decode listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
观察服务端进程的文件描述符情况,发现已经有一个文件描述符6u 且处在监听状态
[root@0210ed315129 ~]# lsof -p 551 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME java 441 root mem REG 8,32 54949 /tmp/hsperfdata_root/441 (path dev=0,149) java 441 root 0u CHR 136,1 0t0 4 /dev/pts/1 java 441 root 1u CHR 136,1 0t0 4 /dev/pts/1 java 441 root 2u CHR 136,1 0t0 4 /dev/pts/1 java 441 root 3r REG 0,149 65491738 43314 /usr/local/java/jdk8u292-b10/jre/lib/rt.jar java 441 root 4r REG 0,149 888480 43336 /usr/local/java/jdk8u292-b10/jre/lib/jfr.jar java 441 root 5u unix 0x000000004428f950 0t0 42094 sock java 441 root 6u IPv4 42096 0t0 TCP *:websm (LISTEN)
查看系统调用生成的文件,调用了listen函数并绑定了文件描述符6,backlog参数为2,在控制台写入字符串语句
socket(AF_INET, SOCK_STREAM, IPPROTO_IP) = 6 fcntl(6, F_GETFL) = 0x2 (flags O_RDWR) fcntl(6, F_SETFL, O_RDWR|O_NONBLOCK) = 0 setsockopt(6, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 bind(6, {sa_family=AF_INET, sin_port=htons(9090), sin_addr=inet_addr("0.0.0.0")}, 16) = 0 listen(6, 2) = 0 setsockopt(6, SOL_SOCKET, SO_RCVBUF, [1024], 4) = 0 setsockopt(6, SOL_SOCKET, SO_REUSEADDR, [0], 4) = 0 poll([{fd=6, events=POLLIN|POLLERR}], 1, -1
客户端代码
import java.io.*; import java.net.Socket; public class SocketClient { public static void main(String[] args) { try { Socket client = new Socket("172.17.0.3",9090); client.setSendBufferSize(20); client.setTcpNoDelay(false); OutputStream out = client.getOutputStream(); InputStream in = System.in; BufferedReader reader = new BufferedReader(new InputStreamReader(in)); while(true){ String line = reader.readLine(); if(line != null ){ byte[] bb = line.getBytes(); for (byte b : bb) { out.write(b); } } } } catch (IOException e) { e.printStackTrace(); } } }
客户端启动脚本client.sh
rm -fr *out* /usr/local/java/jdk8u292-b10/bin/javac SocketClient.java strace -ff -o out /usr/local/java/jdk8u292-b10/bin/java SocketClient
2、启动客户端并发送数据
[root@97b2ba191ee7 io]# ./client.sh 111
再次在服务端服务器观察网络状态,发现之前9090监听的记录保留并新增了一条 tcp 0 0 172.17.0.3:9090 172.17.0.2:58032 ESTABLISHED - 新增了一条已经建立的连接,但是并没有绑定进程,并没有新增的文件描述符,原因是服务器端System.in.read() 阻塞住了,并没有调用accept方法
注意netstat -natp local address为9090的Recv-Q和Recv-Q分别为1和3,这两个参数的含义见全连接和半连接
[root@0210ed315129 ~]# netstat -natp Active Internet connections (servers and established) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 31/sshd tcp 1 0 0.0.0.0:9090 0.0.0.0:* LISTEN 551/java tcp 0 0 172.17.0.3:22 172.17.0.1:57238 ESTABLISHED 32/sshd: root@pts/1 tcp 0 0 172.17.0.3:22 172.17.0.1:57842 ESTABLISHED 57/sshd: root@pts/2 tcp 3 0 172.17.0.3:9090 172.17.0.2:58034 ESTABLISHED - tcp6 0 0 :::22 :::* LISTEN 31/sshd [root@0210ed315129 ~]# lsof -p 551 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME java 551 root 0u CHR 136,1 0t0 4 /dev/pts/1 java 551 root 1u CHR 136,1 0t0 4 /dev/pts/1 java 551 root 2u CHR 136,1 0t0 4 /dev/pts/1 java 551 root 5u unix 0x000000002377d684 0t0 23395 socket
 
再次查看服务端系统调用,发现仍然阻塞在read方法
# 查看系统调用文件 可以看到d阻塞 socket(AF_INET, SOCK_STREAM, IPPROTO_IP) = 6 # 创建用于监听的文件描述符 bind(6, {sa_family=AF_INET, sin_port=htons(9090), sin_addr=inet_addr("0.0.0.0")}, 16) = 0 #绑定端口号 listen(6, 2) = 0 # 设置监听状态,和backlog参数 setsockopt(6, SOL_SOCKET, SO_RCVBUF, [1024], 4) = 0 setsockopt(6, SOL_SOCKET, SO_REUSEADDR, [0], 4) = 0 lseek(3, 61693611, SEEK_SET) = 61693611 read(3, "PK\3\4\n\0\0\10\0\0\311\250\224R9\267\215\270R\6\0\0R\6\0\0;\0\0\0", 30) = 30 lseek(3, 61693700, SEEK_SET) = 61693700 read(3, "\312\376\272\276\0\0\0004\0:\7\0!\n\0\v\0\"\t\0\10\0#\n\0\1\0$\t\0\v\0"..., 1618) = 1618 write(1, "server up use 9090!", 19) = 19 write(1, "\n", 1) = 1
查看客户系统调用
2979:socket(AF_INET, SOCK_STREAM, IPPROTO_IP) = 6 # 新增了6这个socket文件描述符,并建立连接 3059-connect(6, {sa_family=AF_INET, sin_port=htons(9090), sin_addr=inet_addr("172.17.0.3")}, 16) = 0 3060-getsockname(6, {sa_family=AF_INET, sin_port=htons(58080), sin_addr=inet_addr("172.17.0.2")}, [16]) = 0 3061-setsockopt(6, SOL_SOCKET, SO_SNDBUF, [20], 4) = 0 3062-setsockopt(6, SOL_TCP, TCP_NODELAY, [0], 4) = 0 3063-setsockopt(6, SOL_SOCKET, SO_OOBINLINE, [0], 4) = 0
tcpdump已经抓到了三次握手的数据包
08:27:48.355396 IP 172.17.0.2.58048 > 172.17.0.3.9090: Flags [S], seq 4136966469, win 64240, options [mss 1460,sackOK,TS val 3098552358 ecr 0,nop,wscale 7], length 0 08:27:48.355427 IP 172.17.0.3.9090 > 172.17.0.2.58048: Flags [S.], seq 571347657, ack 4136966470, win 1152, options [mss 1460,sackOK,TS val 3752064771 ecr 3098552358,nop,wscale 0], length 0 08:27:48.355445 IP 172.17.0.2.58048 > 172.17.0.3.9090: Flags [.], ack 1, win 502, options [nop,nop,TS val 3098552358 ecr 3752064771], length 0
服务端输入点东西,让代码调用accept后再次观察网络和文件描述符
发现 新增的连接已经绑定了服务端代码进程,并且服务进程新增了一个socket文件描述符java 551 root 7u IPv4 23398 0t0 TCP 0210ed315129.0210ed315129:websm->172.17.0.2:58034 (ESTABLISHED)
[root@0210ed315129 io]# ./server.sh server up use 9090! 111 client port: 58034 [root@0210ed315129 ~]# netstat -natp Active Internet connections (servers and established) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 31/sshd tcp 0 0 0.0.0.0:9090 0.0.0.0:* LISTEN 551/java tcp 0 0 172.17.0.3:22 172.17.0.1:57238 ESTABLISHED 32/sshd: root@pts/1 tcp 0 0 172.17.0.3:22 172.17.0.1:57842 ESTABLISHED 57/sshd: root@pts/2 tcp 0 0 172.17.0.3:9090 172.17.0.2:58034 ESTABLISHED 551/java tcp6 0 0 :::22 :::* LISTEN 31/sshd [root@0210ed315129 ~]# lsof -p 551 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME java 551 root 0u CHR 136,1 0t0 4 /dev/pts/1 java 551 root 1u CHR 136,1 0t0 4 /dev/pts/1 java 551 root 2u CHR 136,1 0t0 4 /dev/pts/1 java 551 root 5u unix 0x000000002377d684 0t0 23395 socket java 551 root 6u IPv4 23397 0t0 TCP *:websm (LISTEN) java 551 root 7u IPv4 23398 0t0 TCP 0210ed315129.0210ed315129:websm->172.17.0.2:58034 (ESTABLISHED)
查看服务端系统调用
poll([{fd=6, events=POLLIN|POLLERR}], 1, -1) = 1 ([{fd=6, revents=POLLIN}]) accept(6, {sa_family=AF_INET, sin_port=htons(58080), sin_addr=inet_addr("172.17.0.2")}, [16]) = 7 #accpept函数执行 返回新的为7的socket文件描述符 fcntl(7, F_GETFL) = 0x2 (flags O_RDWR) fcntl(7, F_SETFL, O_RDWR) = 0 write(1, "client port: 58080", 18) = 18 write(1, "\n", 1) = 1 setsockopt(7, SOL_SOCKET, SO_KEEPALIVE, [0], 4) = 0 setsockopt(7, SOL_SOCKET, SO_OOBINLINE, [0], 4) = 0 setsockopt(7, SOL_SOCKET, SO_RCVBUF, [1024], 4) = 0 setsockopt(7, SOL_SOCKET, SO_REUSEADDR, [0], 4) = 0 setsockopt(7, SOL_SOCKET, SO_SNDBUF, [20], 4) = 0 setsockopt(7, SOL_SOCKET, SO_LINGER, {l_onoff=1, l_linger=0}, 8) = 0 setsockopt(7, SOL_TCP, TCP_NODELAY, [0], 4) = 0
3、客户端发送数据
[root@97b2ba191ee7 io]# ./client.sh 1234
# 服务端已经接收数据 server up use 9090! 111 client port: 58048 client read some data is :4 val :1234

socket四元组

在上述案例中,服务端多次输入内容,只会建立一个socket连接,原因就是tcp四元组唯一的确定一个连接,不会重复创建四元组相同的socket
while (true) { System.in.read(); //分水岭: Socket client = server.accept(); ;......... } ^C[root@0210ed315129 io]# ./server.sh server up use 9090! client port: 58086 11 2121 12121 1212 1212 121212 1212 2112
再启动一个客户端 发现又建立一个新的socket
^C[root@0210ed315129 io]# ./server.sh server up use 9090! client port: 58086 11 2121 12121 1212 1212 121212 1212 2112client port: 58088 netstat -natp Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 31/sshd tcp 0 0 0.0.0.0:9090 0.0.0.0:* LISTEN 1581/java tcp 0 0 172.17.0.3:22 172.17.0.1:57886 ESTABLISHED 1124/sshd: root@pts tcp 0 0 172.17.0.3:22 172.17.0.1:57892 ESTABLISHED 1126/sshd: root@pts tcp 0 0 172.17.0.3:9090 172.17.0.2:58086 ESTABLISHED 1581/java tcp 0 0 172.17.0.3:22 172.17.0.1:57872 ESTABLISHED 1107/sshd: root@pts tcp 0 0 172.17.0.3:9090 172.17.0.2:58088 ESTABLISHED 1581/java tcp6 0 0 :::22 :::* LISTEN 31/sshd
四元组包括如下:
  • 源地址
  • 源端口
  • 目的地址
  • 目的端口
源地址和目的地址的字段(32位)是在 IP 头部中,作用是通过 IP 协议发送报文给对方主机。
源端口和目的端口的字段(16位)是在 TCP 头部中,作用是告诉 TCP 协议应该把报文发给哪个进程。
假设有一个 IP 的服务器监听了一个端口,它的 TCP 的最大连接数是多少?
服务器通常固定在某个本地端口上监听,等待客户端的连接请求。
因此,客户端 IP 和 端口是可变的,其理论值计算公式如下:
notion image
对 IPv4,客户端的 IP 数最多为 232 次方,客户端的端口数最多为 216 次方,也就是服务端单机最大 TCP 连接数,约为 248 次方。
当然,服务端最大并发 TCP 连接数远不能达到理论上限。
  • 首先主要是文件描述符限制,Socket 都是文件,所以首先要通过 ulimit配置文件描述符的数目;
  • 另一个是内存限制,每个 TCP 连接都要占用一定内存,操作系统的内存是有限的
 

全连接和半连接

在 TCP 三次握手的时候,Linux 内核会维护两个队列,分别是:
  • 半连接队列,也称 SYN 队列;
  • 全连接队列,也称 accepet 队列;
服务端收到客户端发起的 SYN 请求后,内核会把该连接存储到半连接队列,并向客户端响应 SYN+ACK,接着客户端会返回 ACK,服务端收到第三次握手的 ACK 后,内核会把连接从半连接队列移除,然后创建新的完全的连接,并将其添加到 accept 队列,等待进程调用 accept 函数时把连接取出来。
netstat命令输出结果的官方解释
Recv-Q Established: The count of bytes not copied by the user program connected to this socket. Listening: Since Kernel 2.6.18 this column contains the current syn backlog. Send-Q Established: The count of bytes not acknowledged by the remote host. Listening: Since Kernel 2.6.18 this column contains the maximum size of the syn backlog. #这里的 backlog参数即listen系统调用的第二个传参即服务端listen的函数的backlog参数 查看 listen man listen The behavior of the backlog argument on TCP sockets changed with Linux 2.2. Now it specifies the queue length for completely established sockets waiting to be accepted, instead of the number of incomplete connection requests. The maximum length of the queue for incomplete sockets can be set using /proc/sys/net/ipv4/tcp_max_syn_backlog. When syncookies are enabled there is no logical maximum length and this setting is ignored. See tcp(7) for more information. If the backlog argument is greater than the value in /proc/sys/net/core/somaxconn, then it is silently truncated to that value; the default value in this file is 128. In kernels before 2.4.25, this limit was a hard coded value, SOMAXCONN, with the value 128.
  1. 状态为Established的socket: Recv-Q表示没有被用户程序从socket 缓存区(recv_buffer)拷贝到用户空间程序进程的字节数,Send-Q表示send_buffer已经发送但未收到ack+syn的字节数据
  1. 状态为Listen的socket : Recv-Q表示当前全连接队列(accept 队列)的连接数量,它的理论值是min(/proc/sys/net/core/somaxconn,backlog),实际上Recv-Q=min(/proc/sys/net/core/somaxconn,backlog)+1,原因 (OS内核在判断队列是否已满时,用的是>(应该用>=),这导致当已创建成功的连接数量正好等于min(backlog, somaxconn)时,还会再多创建一个tcp连接,最终结果就是:min(backlog, somaxconn)+1),Send-Q表示最大半连接队列数量,当启用syncookies ,该设置会被忽略
    1. notion image
 
ss命令和netstat略有不同
在「LISTEN 状态」时,Recv-Q/Send-Q 表示的含义如下:
[root@0210ed315129 io]# ss -lnt State Recv-Q Send-Q Local Address:Port Peer Address:Port LISTEN 1 2 *:9090 *:* LISTEN 0 128 *:22 *:* LISTEN 0 128
  • Recv-Q:当前全连接队列的大小,也就是当前已完成三次握手并等待服务端 accept() 的 TCP 连接;
  • Send-Q:当前全连接最大队列长度min(/proc/sys/net/core/somaxconn,backlog),上面的输出结果说明监听 9090端口的 TCP 服务,最大全连接长度为 2
演示全连接队列满
在上述案例中,开四个客户端去连接服务端,
客户端监控
[root@97b2ba191ee7 ~]# netstat -natp Active Internet connections (servers and established) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 172.17.0.2:58850 172.17.0.3:9090 ESTABLISHED 729/java tcp 0 0 172.17.0.2:58852 172.17.0.3:9090 ESTABLISHED 799/java tcp 0 1 172.17.0.2:58864 172.17.0.3:9090 SYN_SENT 893/java //发送SYN包但未收到服务的syn+ack tcp 0 0 172.17.0.2:58848 172.17.0.3:9090 ESTABLISHED 658/java
服务端监控
[root@0210ed315129 io]# netstat -natp Active Internet connections (servers and established) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 3 0 0.0.0.0:9090 0.0.0.0:* LISTEN 437/java # 全连接队列有三个连接等待accept()
03:39:06.281277 IP 172.17.0.2.58864 > 172.17.0.3.9090: Flags [S], seq 855343429, win 64240, options [mss 1460,sackOK,TS val 3137116033 ecr 0,nop,wscale 7], length 0 03:39:07.310132 IP 172.17.0.2.58864 > 172.17.0.3.9090: Flags [S], seq 855343429, win 64240, options [mss 1460,sackOK,TS val 3137117062 ecr 0,nop,wscale 7], length 0 03:39:09.390143 IP 172.17.0.2.58864 > 172.17.0.3.9090: Flags [S], seq 855343429, win 64240, options [mss 1460,sackOK,TS val 3137119142 ecr 0,nop,wscale 7], length 0 03:39:13.469960 IP 172.17.0.2.58864 > 172.17.0.3.9090: Flags [S], seq 855343429, win 64240, options [mss 1460,sackOK,TS val 3137123222 ecr 0,nop,wscale 7], length 0 03:39:21.950053 IP 172.17.0.2.58864 > 172.17.0.3.9090: Flags [S], seq 855343429, win 64240, options [mss 1460,sackOK,TS val 3137131702 ecr 0,nop,wscale 7], length 0 03:39:38.590045 IP 172.17.0.2.58864 > 172.17.0.3.9090: Flags [S], seq 855343429, win 64240, options [mss 1460,sackOK,TS val 3137148342 ecr 0,nop,wscale 7], length 0 03:40:11.229981 IP 172.17.0.2.58864 > 172.17.0.3.9090: Flags [S], seq 855343429, win 64240, options [mss 1460,sackOK,TS val 3137180982 ecr 0,nop,wscale 7], length 0
重发了7次SYN报文后客户端报错java.net.ConnectException: Connection timed out (Connection timed out)

tcp滑动窗口

服务端先开启,但不accept(),开启客户端发送数据
notion image
tcpdump抓包,发现滑动窗口三次握手时候定义的是1152,数据发送到服务端的窗口变为0,便不再发送
07:04:28.896156 IP 172.17.0.2.54520 > 172.17.0.4.9090: Flags [S], seq 1522197470, win 64240, options [mss 1460,sackOK,TS val 3648596943 ecr 0,nop,wscale 7], length 0 07:04:28.896231 IP 172.17.0.4.9090 > 172.17.0.2.54520: Flags [S.], seq 3882931220, ack 1522197471, win 1152, options [mss 1460,sackOK,TS val 732999587 ecr 3648596943,nop,wscale 0], length 0 07:04:28.896267 IP 172.17.0.2.54520 > 172.17.0.4.9090: Flags [.], ack 1, win 502, options [nop,nop,TS val 3648596943 ecr 732999587], length 0 ## 三次连接 服务端ack+syn 的窗口大小为1152 ..................... 07:07:40.854470 IP 172.17.0.4.9090 > 172.17.0.2.54520: Flags [.], ack 1153, win 0, options [nop,nop,TS val 733191545 ecr 3648788901], length 0 ##服务端ackl153的字节,窗口大小为0无法再接收报文 07:07:41.094352 IP 172.17.0.2.54520 > 172.17.0.4.9090: Flags [.], ack 1, win 502, options [nop,nop,TS val 3648789141 ecr 733191545], length 0
服务端网络,发现Recv-Q =1152 后不再增长,说明此时服务端的socket buffer已满
[root@4ec6570e47ce ~]# netstat -natp Active Internet connections (servers and established) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 1 0 0.0.0.0:9090 0.0.0.0:* LISTEN 465/java tcp 1152 0 172.17.0.4:9090 172.17.0.2:54518 ESTABLISHED - tcp6 0 0 :::22 :::* LISTEN 19/sshd
服务端调用accept. 服务端只接收了1152 字节的数据,其他数据暂时丢失了(只要服务端accept, 客户端可以重传这部分丢失数据)
notion image

BIO模型总结

notion image

C10K问题

NIO

 

多路复用器

select

POSIX规范 select 模型 ,基本上每个操作系统内核都有这个实

poll

 

NIO 和 select、Poll的区别

 

epoll

事件通知模型Linux实现是epoll,unix kqueue
 

epoll的中断处理

select,poll的弊端
  1. 每次都要重新,重复传递fds
  1. 每次,内核被调了之后,针对这次调用,触发一个全量的fds的遍历,复杂度为O(n)
 

java对多路复用的封装