网络IO-tcp是怎么建立通信的
学习马士兵课程-内存与IO,通过 netstat、lsof、tcpdump 观察tcp连接时内核在每一步都就行了什么相应的操作。
本文资料来自于马士兵MAC课程-内存与IO。
通过 netstat、lsof、tcpdump 观察 tcp 连接时内核在每一步都就行了什么相应的操作。
阅读本文,你可以知道在编写 java 代码时的每一步对应内核会产生哪些操作,其探究范围在客户端、服务端代码启动到客户端、服务端建立连接。
案例代码
服务器端
1 | /** |
客户端
1 | /** |
在服务器端启动但客户端未连接时
启动服务器端代码
1
2javac SocketIOPropertites.java
java SocketIOPropertites此时客户端创建了 serverSocket 并绑定了端口9090,阻塞在 System.in.read() 处
netstat
1
2
3
4
5
6
7-a 显示所有状态的socket
-n 不做名字解析,不加此参数,80端口会显示成http,127.0.0.1显示成localhost,uid为0显示成root等等
-e 显示更多信息如用户,inode
-p 显示pid和程序名字
-t 显示tcp链接
-u 显示udp链接
-x 显示unix套接字通过 netstat 可以看出系统中多了一行监听9090端口的tcp连接
1
2
3
4
5
6
7
8
9netstat -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 7631/sshd
tcp 0 0 192.168.203.133:22 192.168.203.1:3137 ESTABLISHED 7846/sshd: root@pts
tcp 0 0 192.168.203.133:22 192.168.203.1:7070 ESTABLISHED 15711/sshd: root@pt
tcp 0 36 192.168.203.133:22 192.168.203.1:6022 ESTABLISHED 7942/sshd: root@pts
tcp6 0 0 :::22 :::* LISTEN 7631/sshd
tcp6 0 0 :::9090 :::* LISTEN 15853/javalsof
通过 lsof 观察到运行java代码的进程出现了一个文件描述符处于Listen状态
1
2
3
4lsof -p 15853
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
...
java 15853 root 6u IPv6 53184 0t0 TCP *:websm (LISTEN)
客户端请求连接但服务器端未accept
启动客户端
1
2javac SocketClient.java
java SocketClient解决异常java.net.NoRouteToHostException: 没有到主机的路由
1
2关闭防火墙
systemctl stop firewalld.servicetcpdump
1
2
3
4
5
6tcpdump -nn -i ens33 port 9090
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ens33, link-type EN10MB (Ethernet), capture size 262144 bytes
15:00:49.829735 IP 192.168.203.132.37058 > 192.168.203.133.9090: Flags [S], seq 493363808, win 29200, options [mss 1460,sackOK,TS val 14267030 ecr 0,nop,wscale 7], length 0
15:00:49.829764 IP 192.168.203.133.9090 > 192.168.203.132.37058: Flags [S.], seq 2693402151, ack 493363809, win 1152, options [mss 1460,sackOK,TS val 15102986 ecr 14267030,nop,wscale 0], length 0
15:00:49.829979 IP 192.168.203.132.37058 > 192.168.203.133.9090: Flags [.], ack 1, win 229, options [nop,nop,TS val 14267031 ecr 15102986], length 0可以发现已经发生了三次握手
netstat
1
2
3
4
5
6
7
8
9
10netstat -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 7631/sshd
tcp 0 0 192.168.203.133:22 192.168.203.1:3137 ESTABLISHED 7846/sshd: root@pts
tcp 0 0 192.168.203.133:22 192.168.203.1:7070 ESTABLISHED 15711/sshd: root@pt
tcp 0 36 192.168.203.133:22 192.168.203.1:6022 ESTABLISHED 7942/sshd: root@pts
tcp6 0 0 :::22 :::* LISTEN 7631/sshd
tcp6 1 0 :::9090 :::* LISTEN 16423/java
tcp6 0 0 192.168.203.133:9090 192.168.203.132:37058 ESTABLISHED -在内核层面已经建立起了socket连接,并且当客户端发出信息后,服务器端是已经被接受了,即使这个连接没有分配给任何一个进程
当服务器端accept后
netstat
1
2
3
4
5
6
7
8
9
10netstat -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 7631/sshd
tcp 0 0 192.168.203.133:22 192.168.203.1:3137 ESTABLISHED 7846/sshd: root@pts
tcp 0 0 192.168.203.133:22 192.168.203.1:7070 ESTABLISHED 15711/sshd: root@pt
tcp 0 36 192.168.203.133:22 192.168.203.1:6022 ESTABLISHED 7942/sshd: root@pts
tcp6 0 0 :::22 :::* LISTEN 7631/sshd
tcp6 0 0 :::9090 :::* LISTEN 16423/java
tcp6 0 0 192.168.203.133:9090 192.168.203.132:37058 ESTABLISHED 16423/java该连接已经分配给了对应进程
lsof
1
2
3
4lsof -p 16423
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
...
java 16423 root 7u IPv6 70977 0t0 TCP CentOS7.6:websm->192.168.203.132:37058 (ESTABLISHED)可以发现该进程已经被分配了文件描述符
总结
- socket本质是一个四元组,通过 CIP_CPORT + SIP_SPORT 能确认唯一socket
- new ServerSocket() 在内核层面的操作就是开启了一个监听 port 的 socket
- 当两台服务器之间建立连接时并未将此 socket 分配给进程,即进程并未分配对应的文件描述符 fd,但服务器之间已经可以就行信息传递