安装必要工具:
yum install -y strace lsof pmap tcpdump
准备服务端代码
import java.io.*;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
/**
-
BIO Socket Server
*/
public class SocketServerBIOTest {
private static final int PORT = 9090;
private static final int BACK_LOG = 2;public static void main(String[] args) {
ServerSocket server = null;
try {
server = new ServerSocket();
server.bind(new InetSocketAddress(PORT), BACK_LOG);
System.out.println("server started , port : " + PORT);
} catch (IOException e) {
e.printStackTrace();
}
try {
// 接受客户端连接
while (true) {
// 先阻塞,这样客户端暂时无法连接进来
System.in.read();// 这个方法也是阻塞的,如果没有客户端连接进来,会一直阻塞在这里,除非设置了超时时间 Socket client = server.accept(); System.out.println("client " + client.getPort() + " connected!!!"); // 客户端连接进来后,开辟一个新的线程去接收并处理 new Thread(() -> { try { InputStream inputStream = client.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); 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 read nothing!"); continue; } else { System.out.println("client read -1..."); System.in.read(); client.close(); break; } } } catch (Exception e) { e.printStackTrace(); } }).start(); } } catch (Exception e) { e.printStackTrace(); } finally { try { server.close(); } catch (IOException e) { e.printStackTrace(); } }
}
}
死循环中,由于第一句:
System.in.read();
导致
Socket client = server.accept();
无法执行,即:服务端此时是无法接收客户端的。
准备客户端代码:
import java.io.*;
import java.net.Socket;
/**
-
Socket Client
*/
public class SocketClientTest {public static void main(String[] args) {
try { Socket client = new Socket("192.168.205.138", 9090); 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(); }
}
}
在实例io1中启动服务端代码:
javac SocketServerBIOTest.java && java SocketServerBIOTest
在io1中开启抓包工具:
tcpdump -nn -i ens33 port 9090
在io2中执行客户端代码:
javac SocketClientTest.java && java SocketClientTest
由于我们在服务端加了一段:
System.in.read()
方法,导致服务端其实没办法执行accept()
但是在服务端查看抓包信息:
[root@io socket]# tcpdump -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:19:56.021974 IP 192.168.205.149.56944 > 192.168.205.138.9090: Flags [S], seq 391962776, win 29200, options [mss 1460,sackOK,TS val 16515471 ecr 0,nop,wscale 7], length 0
15:19:56.022035 IP 192.168.205.138.9090 > 192.168.205.149.56944: Flags [S.], seq 2744580571, ack 391962777, win 28960, options [mss 1460,sackOK,TS val 16517545 ecr 16515471,nop,wscale 7], length 0
15:19:56.022349 IP 192.168.205.149.56944 > 192.168.205.138.9090: Flags [.], ack 1, win 229, options [nop,nop,TS val 16515472 ecr 16517545], length 0
内核已经为客户端和服务端建立了连接并完成了三次握手,在服务端使用netstat查看:
[root@io socket]# netstat -ntap
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
…
tcp6 0 0 192.168.205.138:9090 192.168.205.149:56944 ESTABLISHED -
…
显示已经建立了连接,只不过还没有分配:PID/Program name。
在客户端输入一些信息,
[root@io2 socket]# javac SocketClientTest.java && java SocketClientTest
asdfasdfasdfasf
在服务端再次执行netstat
[root@io socket]# netstat -ntap
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
…
tcp6 15 0 192.168.205.138:9090 192.168.205.149:56944 ESTABLISHED -
…
也显示出接收到了数据。
再服务端再次开启抓包:
tcpdump -nn -i ens33 port 9090
再次在客户端输入一些数据:
[root@io2 socket]# javac SocketClientTest.java && java SocketClientTest
asdfasdfasdfasf
dfasdfasdfasdas
可以看到抓包信息:
[root@io ~]# tcpdump -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:26:48.632564 IP 192.168.205.149.56944 > 192.168.205.138.9090: Flags [P.], seq 391962792:391962793, ack 2744580572, win 229, options [nop,nop,TS val 16928082 ecr 16757410], length 1
15:26:48.632609 IP 192.168.205.138.9090 > 192.168.205.149.56944: Flags [.], ack 1, win 227, options [nop,nop,TS val 16930156 ecr 16928082], length 0
15:26:48.632791 IP 192.168.205.149.56944 > 192.168.205.138.9090: Flags [P.], seq 1:15, ack 1, win 229, options [nop,nop,TS val 16928082 ecr 16930156], length 14
15:26:48.632825 IP 192.168.205.138.9090 > 192.168.205.149.56944: Flags [.], ack 15, win 227, options [nop,nop,TS val 16930156 ecr 16928082], length 0
以上实验主要说明了一个问题:
虽然在应用层面,服务端没有调用accept() 去接收客户端,但是,内核其实已经完成了客户端和服务端的三次握手以及数据传输。
接下来,我们触发服务端:
Socket client = server.accept();
这段逻辑
服务端可以显示客户端的数据
^C[root@io socket]# java SocketServerBIOTest
server started , port : 9090
client 56944 connected!!!
client read some data is :30 val :asdfasdfasdfasfdfasdfasdfasdas
服务端执行nestat -ntap,
[root@io ~]# netstat -ntap
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
…
tcp6 0 0 192.168.205.138:9090 192.168.205.149:56944 ESTABLISHED 2266/java
…
可以看到已经分配了一个PID,通过:
lsof -p 2266
查看这个java进程相关的文件描述符
[root@io ~]# lsof -p 2266
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
…
java 2266 root 6u IPv6 26479 0t0 TCP 192.168.205.138:websm->192.168.205.149:56944 (ESTABLISHED)
…
6u就对应了服务端和客户端连接的一个Socket。
在创建服务端的时候,我们指定了一个参数:backlog
// 指定了BACK_LOG = 2
server.bind(new InetSocketAddress(PORT), BACK_LOG);
backlog的解释是
requested maximum length of the queue of incoming connections.
重新启动我们的服务端
[root@io socket]# javac SocketServerBIOTest.java && java SocketServerBIOTest
server started , port : 9090
启动三个客户端连接这个服务端, 然后再服务端执行netstat -ntap
[root@io socket]# netstat -ntap
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
…
tcp6 0 0 192.168.205.138:9090 192.168.205.149:50532 ESTABLISHED -
tcp6 0 0 192.168.205.138:9090 192.168.205.149:50536 ESTABLISHED -
tcp6 0 0 192.168.205.138:9090 192.168.205.149:50534 ESTABLISHED -
…
可以看到,服务端创建了三个连接,但是,当我们再启动一个客户端连接进来的时候,新增的这个连接的状态为:SYN_RECV
[root@io socket]# netstat -ntap
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
…
tcp 0 0 192.168.205.138:9090 192.168.205.149:50538 SYN_RECV -
…
tcp6 0 0 192.168.205.138:9090 192.168.205.149:50532 ESTABLISHED -
tcp6 0 0 192.168.205.138:9090 192.168.205.149:50536 ESTABLISHED -
tcp6 0 0 192.168.205.138:9090 192.168.205.149:50534 ESTABLISHED -
USB Microphone https://www.soft-voice.com/
Wooden Speakers https://www.zeshuiplatform.com/
亚马逊测评 www.yisuping.cn
深圳网站建设www.sz886.com