Linux-网络IO之TCP三次握手阶段

BIO的连接建立及文件描述符分配

首先准备两台linux:node01和node02,用来做client和server
需要用到的几个命令:

lsof -p 可以看到某一个进程里的文件描述符是否已经有了,以及什么时候才能看到这个文件描述符
netstat -antp 查看内核里面socket的建立过程
tcpdump 抓取网络通信数据包

BIO一些参数介绍:

	//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;
    //使用netstat -antp时可以看到Recv-Q
    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;
    //client读取的时候是阻塞的,可以加一个超时
    private static final int CLI_TIMEOUT = 0;
    //TCP的一个优化算法,在发送数据比较少的时候可以利用缓冲
    private static final boolean CLI_NO_DELAY = false;

服务端代码:测试的时候依靠System.in.read();来监测连接的状态

/**
 * BIO  多线程的方式
 */
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();
            //监听9090端口,
            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!");
        while (true) {
            try {
                System.in.read();  //分水岭:

                Socket client = server.accept();
                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);

                new Thread(
                        () -> {
                            while (true) {
                                try {
                                    InputStream in = client.getInputStream();
                                    BufferedReader reader = new BufferedReader(new InputStreamReader(in));
                                    char[] data = new char[1024];
                                    int num = reader.read(data);

                                    if (num > 0) {//读到了client的数据
                                        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 {//client断开连接了                                        System.out.println("client readed -1...");
                                        client.close();
                                        break;
                                    }
                                } catch (IOException e) {
                                    e.printStackTrace();
                                }
                            }
                        }
                ).start();
            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                try {
                    server.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

client代码

public class SocketClient {

    public static void main(String[] args) {

        try {
            Socket client = new Socket("192.168.100.1",9090);
			//缓冲区20个字节
            client.setSendBufferSize(20);
            client.setTcpNoDelay(true);
            OutputStream out = client.getOutputStream();

            InputStream in = System.in;
            BufferedReader reader = new BufferedReader(new InputStreamReader(in));

            while(true){
            	//注意这一步,此时是阻塞的,client还没有发送数据,在此步骤观察client和server的连接情况
                String line = reader.readLine();
                if(line != null ){
                    byte[] bb = line.getBytes();
                    for (byte b : bb) {
                    	//读到数据就给服务端发送,注意:没有flush,依靠kernel
                        out.write(b);
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

首先先看下服务端目前监听的端口号,用于和操作后进行对比,可以看到监听的都是22,25

[root@optimize-node01 ~]# netstat -antp
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      6690/sshd           
tcp        0      0 127.0.0.1:25            0.0.0.0:*               LISTEN      6822/master         
tcp        0      0 10.0.0.5:22             10.0.0.1:7687           ESTABLISHED 7478/sshd: root@pts 
tcp        0     52 10.0.0.5:22             10.0.0.1:8031           ESTABLISHED 7555/sshd: root@pts 
tcp6       0      0 :::22                   :::*                    LISTEN      6690/sshd           
tcp6       0      0 ::1:25

服务端新开启一个窗口监测tcp抓包详情

[root@optimize-node01 opt]# 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

tcpdump
tcpdump -i 网卡
tcpdump -nn 数字的方式显示IP和端口。一个n是ip
tcpdump -c x 抓包数量,x为数字
tcpdump port xx 抓指定端口的包,xx为端口号
tcpdump tcp and port xx 指定协议和端口,xx为端口号,and可以省略不写
tcpdump host xx.xx.xx.xx 指定来源IP或目标IP的包 xx.xx.xx.xx为IP地址。
tcpdump -w xx.txt 把抓的包写入一个文件,xx.txt为文件名
tcpdump -s0 -w xx.txt 抓包时防止包截断,s0的0为数字0,抓一个完整的包必须加s0。
tcpdump -r xx.txt 用户查看-w抓的包,xx.txt为文件名
-w抓的包实际是包的内容,非简单的流向。如果访问一张图片,用-w可以把这张图片抓出来。只看流向的话,可以使用重定向。

开启服务端程序:

[root@optimize-node01 netio]# javac SocketIOPropertites.java && java SocketIOPropertites
server up use 9090!

再次查看网络连接,并与之前的网络连接进行比较

[root@optimize-node01 ~]# netstat -antp
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      6690/sshd           
tcp        0      0 127.0.0.1:25            0.0.0.0:*               LISTEN      6822/master         
tcp        0     52 10.0.0.5:22             10.0.0.1:9230           ESTABLISHED 7599/sshd: root@pts 
tcp        0      0 10.0.0.5:22             10.0.0.1:7687           ESTABLISHED 7478/sshd: root@pts 
tcp        0      0 10.0.0.5:22             10.0.0.1:8031           ESTABLISHED 7555/sshd: root@pts 
tcp6       0      0 :::22                   :::*                    LISTEN      6690/sshd           
tcp6       0      0 ::1:25                  :::*                    LISTEN      6822/master         
[root@optimize-node01 ~]# netstat -antp
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      6690/sshd           
tcp        0      0 127.0.0.1:25            0.0.0.0:*               LISTEN      6822/master         
tcp        0     52 10.0.0.5:22             10.0.0.1:9230           ESTABLISHED 7599/sshd: root@pts 
tcp        0      0 10.0.0.5:22             10.0.0.1:7687           ESTABLISHED 7478/sshd: root@pts 
tcp        0      0 10.0.0.5:22             10.0.0.1:8031           ESTABLISHED 7555/sshd: root@pts 
tcp6       0      0 :::22                   :::*                    LISTEN      6690/sshd           
tcp6       0      0 ::1:25                  :::*                    LISTEN      6822/master         
tcp6       0      0 :::9090                 :::*                    LISTEN      7634/java         

可以看到服务端监听了9090端口,并把这个监听分配给了7634的java进程
所有client的连接分为两类:第一类三次握手是走LISTEN状态的这条9090连接;第二类三次握手之后肯定会分配文件描述符交给其他的线程,这个可以在后续步骤看到
有了9090这个监听之后client就可以连接进来了

查看抓包窗口,此时是任何事情也没有发生的

[root@optimize-node01 opt]# 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

使用jps看下java进程,并使用lsof -p pid查看进程使用的文件描述符

[root@optimize-node01 ~]# jps
7634 SocketIOPropertites
7652 Jps
[root@optimize-node01 ~]# lsof -p 7634
COMMAND  PID USER   FD   TYPE             DEVICE  SIZE/OFF     NODE NAME
java    7634 root  cwd    DIR              253,0        17 51428137 /opt/io/netio
java    7634 root  rtd    DIR              253,0       256       64 /
java    7634 root  txt    REG              253,0      8984    19770 /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.252.b09-2.el7_8.x86_64/jre/bin/java
java    7634 root  mem    REG              253,0 106075056 50566106 /usr/lib/locale/locale-archive
java    7634 root  mem    REG              253,0    123192 33637617 /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.252.b09-2.el7_8.x86_64/jre/lib/amd64/libnet.so
java    7634 root  mem    REG              253,0    100064 33637618 /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.252.b09-2.el7_8.x86_64/jre/lib/amd64/libnio.so
java    7634 root  mem    REG              253,0  73844502 17289271 /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.252.b09-2.el7_8.x86_64/jre/lib/rt.jar
java    7634 root  mem    REG              253,0     44248 33637625 /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.252.b09-2.el7_8.x86_64/jre/lib/amd64/libzip.so
java    7634 root  mem    REG              253,0     61624    42280 /usr/lib64/libnss_files-2.17.so
java    7634 root  mem    REG              253,0    219000 33637607 /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.252.b09-2.el7_8.x86_64/jre/lib/amd64/libjava.so
java    7634 root  mem    REG              253,0     66440 33637624 /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.252.b09-2.el7_8.x86_64/jre/lib/amd64/libverify.so
java    7634 root  mem    REG              253,0     43776    42292 /usr/lib64/librt-2.17.so
java    7634 root  mem    REG              253,0     88776       84 /usr/lib64/libgcc_s-4.8.5-20150702.so.1
java    7634 root  mem    REG              253,0   1137016    42270 /usr/lib64/libm-2.17.so
java    7634 root  mem    REG              253,0    991616    42323 /usr/lib64/libstdc++.so.6.0.19
java    7634 root  mem    REG              253,0  17265304 17289251 /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.252.b09-2.el7_8.x86_64/jre/lib/amd64/server/libjvm.so
java    7634 root  mem    REG              253,0   2151672    42262 /usr/lib64/libc-2.17.so
java    7634 root  mem    REG              253,0     19288    42268 /usr/lib64/libdl-2.17.so
java    7634 root  mem    REG              253,0     71136    19781 /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.252.b09-2.el7_8.x86_64/jre/lib/amd64/jli/libjli.so
java    7634 root  mem    REG              253,0     90248    42321 /usr/lib64/libz.so.1.2.7
java    7634 root  mem    REG              253,0    141968    42288 /usr/lib64/libpthread-2.17.so
java    7634 root  mem    REG              253,0    163400    42255 /usr/lib64/ld-2.17.so
java    7634 root  mem    REG              253,0     32768 17314831 /tmp/hsperfdata_root/7634
java    7634 root    0u   CHR              136,1       0t0        4 /dev/pts/1
java    7634 root    1u   CHR              136,1       0t0        4 /dev/pts/1
java    7634 root    2u   CHR              136,1       0t0        4 /dev/pts/1
java    7634 root    3r   REG              253,0  73844502 17289271 /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.252.b09-2.el7_8.x86_64/jre/lib/rt.jar
java    7634 root    4u  unix 0xffff903e39160000       0t0    39936 socket
java    7634 root    5u  IPv6              63490       0t0      TCP *:websm (LISTEN)

得到的服务端进程id是7634
可以看到7634分配了一个文件描述符5在LISTEN状态
到此为止关注两点:server开启了9090端口监听,分配了一个文件描述符5

客户端开启demo程序

[root@optimize-node02 netio]# javac SocketClient.java && java SocketClient

client程序启动后且被System.in.read();阻塞住

此时去观察server端的tcpdump情况

[root@optimize-node01 opt]# 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
06:03:39.345640 IP 10.0.0.6.34396 > 10.0.0.5.9090: Flags [S], seq 796655830, win 29200, options [mss 1460,sackOK,TS val 19727488 ecr 0,nop,wscale 7], length 0
06:03:39.345672 IP 10.0.0.5.9090 > 10.0.0.6.34396: Flags [S.], seq 3217094740, ack 796655831, win 1152, options [mss 1460,sackOK,TS val 19604751 ecr 19727488,nop,wscale 0], length 0
06:03:39.345904 IP 10.0.0.6.34396 > 10.0.0.5.9090: Flags [.], ack 1, win 229, options [nop,nop,TS val 19727489 ecr 19604751], length 0

观察到建立3次握手的情况
1.10.0.0.6.34396向10.0.0.5.9090发送了标志位为SYN的数据包
2.10.0.0.5.9090 给 10.0.0.6.34396 回了一个标志位SYN和ACK的数据包
3.10.0.0.6.34396 又给 10.0.0.5.9090 回了一个标志位为ACK的数据包
至此3次握手建立完成

再去看server的netstat -antp监控

[root@optimize-node01 ~]# netstat -antp
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      6690/sshd           
tcp        0      0 127.0.0.1:25            0.0.0.0:*               LISTEN      6822/master         
tcp        0     52 10.0.0.5:22             10.0.0.1:9230           ESTABLISHED 7599/sshd: root@pts 
tcp        0      0 10.0.0.5:22             10.0.0.1:7687           ESTABLISHED 7478/sshd: root@pts 
tcp        0      0 10.0.0.5:22             10.0.0.1:8031           ESTABLISHED 7555/sshd: root@pts 
tcp6       0      0 :::22                   :::*                    LISTEN      6690/sshd           
tcp6       0      0 ::1:25                  :::*                    LISTEN      6822/master         
tcp6       1      0 :::9090                 :::*                    LISTEN      7634/java           
tcp6       0      0 10.0.0.5:9090           10.0.0.6:34396          ESTABLISHED -  

可以看到曾经的监控只有9090的LISTEN
而且此时server还没有执行accept()
现在多了9090的ESTABLISHED,已经和client建立起了连接,且没有分配给任何进程使用

此时在client发送数据看看,发送1111

[root@optimize-node02 netio]# javac com/bjmashibing/system/io/SocketClient.java && java com/bjmashibing/system/io/SocketClient
1111

再来看server的tcpdump

[root@optimize-node01 opt]# 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
06:03:39.345640 IP 10.0.0.6.34396 > 10.0.0.5.9090: Flags [S], seq 796655830, win 29200, options [mss 1460,sackOK,TS val 19727488 ecr 0,nop,wscale 7], length 0
06:03:39.345672 IP 10.0.0.5.9090 > 10.0.0.6.34396: Flags [S.], seq 3217094740, ack 796655831, win 1152, options [mss 1460,sackOK,TS val 19604751 ecr 19727488,nop,wscale 0], length 0
06:03:39.345904 IP 10.0.0.6.34396 > 10.0.0.5.9090: Flags [.], ack 1, win 229, options [nop,nop,TS val 19727489 ecr 19604751], length 0
06:21:16.659497 IP 10.0.0.6.34396 > 10.0.0.5.9090: Flags [P.], seq 1:2, ack 1, win 229, options [nop,nop,TS val 20784800 ecr 19604751], length 1
06:21:16.659520 IP 10.0.0.5.9090 > 10.0.0.6.34396: Flags [.], ack 2, win 1151, options [nop,nop,TS val 20662065 ecr 20784800], length 0
06:21:16.659562 IP 10.0.0.6.34396 > 10.0.0.5.9090: Flags [P.], seq 2:3, ack 1, win 229, options [nop,nop,TS val 20784800 ecr 19604751], length 1
06:21:16.659786 IP 10.0.0.6.34396 > 10.0.0.5.9090: Flags [P.], seq 3:4, ack 1, win 229, options [nop,nop,TS val 20784800 ecr 20662065], length 1
06:21:16.670546 IP 10.0.0.6.34396 > 10.0.0.5.9090: Flags [P.], seq 3:4, ack 1, win 229, options [nop,nop,TS val 20784811 ecr 20662065], length 1
06:21:16.670565 IP 10.0.0.5.9090 > 10.0.0.6.34396: Flags [.], ack 4, win 1149, options [nop,nop,TS val 20662076 ecr 20784800,nop,nop,sack 1 {3:4}], length 0
06:21:16.670866 IP 10.0.0.6.34396 > 10.0.0.5.9090: Flags [P.], seq 4:5, ack 1, win 229, options [nop,nop,TS val 20784812 ecr 20662076], length 1
06:21:16.710345 IP 10.0.0.5.9090 > 10.0.0.6.34396: Flags [.], ack 5, win 1148, options [nop,nop,TS val 20662116 ecr 20784812], length 0

多了3次握手下面的数据包接收

再看看server的natstat -antp

[root@optimize-node01 ~]# netstat -antp
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      6690/sshd           
tcp        0      0 127.0.0.1:25            0.0.0.0:*               LISTEN      6822/master         
tcp        0     52 10.0.0.5:22             10.0.0.1:9230           ESTABLISHED 7599/sshd: root@pts 
tcp        0      0 10.0.0.5:22             10.0.0.1:7687           ESTABLISHED 7478/sshd: root@pts 
tcp        0      0 10.0.0.5:22             10.0.0.1:8031           ESTABLISHED 7555/sshd: root@pts 
tcp6       0      0 :::22                   :::*                    LISTEN      6690/sshd           
tcp6       0      0 ::1:25                  :::*                    LISTEN      6822/master         
tcp6       1      0 :::9090                 :::*                    LISTEN      7634/java           
tcp6       4      0 10.0.0.5:9090           10.0.0.6:34396          ESTABLISHED - 

可以看到虽然此时还没有accept();但是已经接收到client发送过来的数据,10.0.0.5:9090 -> 10.0.0.6:34396 这条连接的Recv-Q为4,这4个字节已经被kernel收到了,kernel已经完成了资源的初步使用,所以说tcp是面向连接的(因为此时只是建立了连接,这个连接在server端并没有分配给任何进程使用)

查看java程序的文件描述符

[root@optimize-node01 ~]# lsof -p 7634
COMMAND  PID USER   FD   TYPE             DEVICE  SIZE/OFF     NODE NAME
java    7634 root  cwd    DIR              253,0        17 51428137 /opt/io/netio
java    7634 root  rtd    DIR              253,0       256       64 /
java    7634 root  txt    REG              253,0      8984    19770 /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.252.b09-2.el7_8.x86_64/jre/bin/java
java    7634 root  mem    REG              253,0 106075056 50566106 /usr/lib/locale/locale-archive
java    7634 root  mem    REG              253,0    123192 33637617 /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.252.b09-2.el7_8.x86_64/jre/lib/amd64/libnet.so
java    7634 root  mem    REG              253,0    100064 33637618 /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.252.b09-2.el7_8.x86_64/jre/lib/amd64/libnio.so
java    7634 root  mem    REG              253,0  73844502 17289271 /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.252.b09-2.el7_8.x86_64/jre/lib/rt.jar
java    7634 root  mem    REG              253,0     44248 33637625 /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.252.b09-2.el7_8.x86_64/jre/lib/amd64/libzip.so
java    7634 root  mem    REG              253,0     61624    42280 /usr/lib64/libnss_files-2.17.so
java    7634 root  mem    REG              253,0    219000 33637607 /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.252.b09-2.el7_8.x86_64/jre/lib/amd64/libjava.so
java    7634 root  mem    REG              253,0     66440 33637624 /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.252.b09-2.el7_8.x86_64/jre/lib/amd64/libverify.so
java    7634 root  mem    REG              253,0     43776    42292 /usr/lib64/librt-2.17.so
java    7634 root  mem    REG              253,0     88776       84 /usr/lib64/libgcc_s-4.8.5-20150702.so.1
java    7634 root  mem    REG              253,0   1137016    42270 /usr/lib64/libm-2.17.so
java    7634 root  mem    REG              253,0    991616    42323 /usr/lib64/libstdc++.so.6.0.19
java    7634 root  mem    REG              253,0  17265304 17289251 /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.252.b09-2.el7_8.x86_64/jre/lib/amd64/server/libjvm.so
java    7634 root  mem    REG              253,0   2151672    42262 /usr/lib64/libc-2.17.so
java    7634 root  mem    REG              253,0     19288    42268 /usr/lib64/libdl-2.17.so
java    7634 root  mem    REG              253,0     71136    19781 /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.252.b09-2.el7_8.x86_64/jre/lib/amd64/jli/libjli.so
java    7634 root  mem    REG              253,0     90248    42321 /usr/lib64/libz.so.1.2.7
java    7634 root  mem    REG              253,0    141968    42288 /usr/lib64/libpthread-2.17.so
java    7634 root  mem    REG              253,0    163400    42255 /usr/lib64/ld-2.17.so
java    7634 root  mem    REG              253,0     32768 17314831 /tmp/hsperfdata_root/7634
java    7634 root    0u   CHR              136,1       0t0        4 /dev/pts/1
java    7634 root    1u   CHR              136,1       0t0        4 /dev/pts/1
java    7634 root    2u   CHR              136,1       0t0        4 /dev/pts/1
java    7634 root    3r   REG              253,0  73844502 17289271 /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.252.b09-2.el7_8.x86_64/jre/lib/rt.jar
java    7634 root    4u  unix 0xffff903e39160000       0t0    39936 socket
java    7634 root    5u  IPv6              63490       0t0      TCP *:websm (LISTEN)

可以发现在内核里的socket没有分配给任何进程,java进程里的文件描述符5还是指向LISTEN状态,也就是说程序其实不是通过socket要使用socket,程序其实是在等一个东西,等一个文件描述符,但是server还没有调用accept();还没有返回文件描述符,所以只要让我们的server跳过System.in.read();阻塞,继续执行就会分配一个文件描述符。

此时在server端按下回车,跳过阻塞

[root@optimize-node01 netio]# javac SocketIOPropertites.java && java SocketIOPropertites
server up use 9090!

client port: 34396
client read some data is :4 val :1111

看到已经接收到了client,并且kernel里缓冲的那4各字节也读到了
关键还要看server的监控

[root@optimize-node01 ~]# netstat -antp
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      6690/sshd           
tcp        0      0 127.0.0.1:25            0.0.0.0:*               LISTEN      6822/master         
tcp        0      0 10.0.0.5:22             10.0.0.1:9230           ESTABLISHED 7599/sshd: root@pts 
tcp        0    180 10.0.0.5:22             10.0.0.1:7687           ESTABLISHED 7478/sshd: root@pts 
tcp        0      0 10.0.0.5:22             10.0.0.1:11023          ESTABLISHED 7717/sshd: root@pts 
tcp        0     52 10.0.0.5:22             10.0.0.1:11030          ESTABLISHED 7771/sshd: root@pts 
tcp        0   2224 10.0.0.5:22             10.0.0.1:8031           ESTABLISHED 7555/sshd: root@pts 
tcp        0      0 10.0.0.5:22             10.0.0.1:11028          ESTABLISHED 7751/sshd: root@pts 
tcp6       0      0 :::22                   :::*                    LISTEN      6690/sshd           
tcp6       0      0 ::1:25                  :::*                    LISTEN      6822/master         
tcp6       0      0 10.0.0.5:9090           10.0.0.6:34396          ESTABLISHED 7838/java   

可以看到刚才没有分配的那个socket 10.0.0.5:9090 ->10.0.0.6:34396此时已经分配给了进程号为7838的java进程

且看lsof -p 7838

[root@optimize-node01 ~]# lsof -p 7838
COMMAND  PID USER   FD   TYPE             DEVICE  SIZE/OFF     NODE NAME
java    7838 root  cwd    DIR              253,0        17 51428137 /opt/io/netio
java    7838 root  rtd    DIR              253,0       256       64 /
java    7838 root  txt    REG              253,0      8984    19770 /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.252.b09-2.el7_8.x86_64/jre/bin/java
java    7838 root  mem    REG              253,0 106075056 50566106 /usr/lib/locale/locale-archive
java    7838 root  mem    REG              253,0    123192 33637617 /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.252.b09-2.el7_8.x86_64/jre/lib/amd64/libnet.so
java    7838 root  mem    REG              253,0    100064 33637618 /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.252.b09-2.el7_8.x86_64/jre/lib/amd64/libnio.so
java    7838 root  mem    REG              253,0  73844502 17289271 /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.252.b09-2.el7_8.x86_64/jre/lib/rt.jar
java    7838 root  mem    REG              253,0     44248 33637625 /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.252.b09-2.el7_8.x86_64/jre/lib/amd64/libzip.so
java    7838 root  mem    REG              253,0     61624    42280 /usr/lib64/libnss_files-2.17.so
java    7838 root  mem    REG              253,0    219000 33637607 /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.252.b09-2.el7_8.x86_64/jre/lib/amd64/libjava.so
java    7838 root  mem    REG              253,0     66440 33637624 /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.252.b09-2.el7_8.x86_64/jre/lib/amd64/libverify.so
java    7838 root  mem    REG              253,0     43776    42292 /usr/lib64/librt-2.17.so
java    7838 root  mem    REG              253,0     88776       84 /usr/lib64/libgcc_s-4.8.5-20150702.so.1
java    7838 root  mem    REG              253,0   1137016    42270 /usr/lib64/libm-2.17.so
java    7838 root  mem    REG              253,0    991616    42323 /usr/lib64/libstdc++.so.6.0.19
java    7838 root  mem    REG              253,0  17265304 17289251 /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.252.b09-2.el7_8.x86_64/jre/lib/amd64/server/libjvm.so
java    7838 root  mem    REG              253,0   2151672    42262 /usr/lib64/libc-2.17.so
java    7838 root  mem    REG              253,0     19288    42268 /usr/lib64/libdl-2.17.so
java    7838 root  mem    REG              253,0     71136    19781 /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.252.b09-2.el7_8.x86_64/jre/lib/amd64/jli/libjli.so
java    7838 root  mem    REG              253,0     90248    42321 /usr/lib64/libz.so.1.2.7
java    7838 root  mem    REG              253,0    141968    42288 /usr/lib64/libpthread-2.17.so
java    7838 root  mem    REG              253,0    163400    42255 /usr/lib64/ld-2.17.so
java    7838 root  mem    REG              253,0     32768 17314831 /tmp/hsperfdata_root/7838
java    7838 root    0u   CHR              136,3       0t0        6 /dev/pts/3
java    7838 root    1u   CHR              136,3       0t0        6 /dev/pts/3
java    7838 root    2u   CHR              136,3       0t0        6 /dev/pts/3
java    7838 root    3r   REG              253,0  73844502 17289271 /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.252.b09-2.el7_8.x86_64/jre/lib/rt.jar
java    7838 root    4u  unix 0xffff903e1ef9fc00       0t0    59015 socket
java    7838 root    6u  IPv6              59021       0t0      TCP bogon:websm->bogon:34396 (ESTABLISHED)

也得到了一个新的文件描述符
6u IPv6 59021 0t0 TCP bogon:websm->bogon:34396

总结一下:

  • 1.TCP是面向连接的,可靠的传输协议
    • 连接要通过3次握手,抓包抓到了
    • 内核要开辟资源,资源就是所谓的连接,上面例子中Recv-Q为4的时候已经看到了
  • 2.Socket
    • socket是一个四元组(CIP CPORT + SIP SPORT)
    • socket是在内核级的,是在传输层
    • 即便你不调用accept,内核也会完成连接的建立和数据的接收过程
    • 面试题:
      • 一个client连接到server,server要不要为client分配一个随机port?
        • 不需要,不管是服务端还是客户端,只要用四元组作为唯一标识就可以了,不用再多加一个随机端口号
  • 3.四元组是唯一的,四元组是内核级的,将四元组分配给某个进程后,进程分配给四元组的文件描述符fd也是唯一的,这个fd就是流的抽象
  • 4.只要内存资源够,可以建立很多连接,这个连接就是socket四元组,服务端能够监听的端口就是65535个,也就是我们之前使用netstat -antp命令时看到的处于LISTEN状态的连接;只要socket四元组不同,就可以建立连接

演示backlog

将上面演示所用的server和client关闭,恢复到初始状态

  • 1.开启server
[root@optimize-node01 netio]# javac SocketIOPropertites.java && java SocketIOPrortites
server up use 9090!

  • 2.查看server监听端口
[root@optimize-node01 ~]# netstat -antp
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      6690/sshd           
tcp        0      0 127.0.0.1:25            0.0.0.0:*               LISTEN      6822/master         
tcp        0      0 10.0.0.5:22             10.0.0.1:9230           ESTABLISHED 7599/sshd: root@pts 
tcp        0      0 10.0.0.5:22             10.0.0.1:11023          ESTABLISHED 7717/sshd: root@pts 
tcp        0      0 10.0.0.5:22             10.0.0.1:11030          ESTABLISHED 7771/sshd: root@pts 
tcp        0     52 10.0.0.5:22             10.0.0.1:11028          ESTABLISHED 7751/sshd: root@pts 
tcp6       0      0 :::22                   :::*                    LISTEN      6690/sshd           
tcp6       0      0 ::1:25                  :::*                    LISTEN      6822/master         
tcp6       0      0 :::9090                 :::*                    LISTEN      7912/java  

此时只有9090端口的LISTEN

  • 3.开启server tcpdump抓包
[root@optimize-node01 ~]# 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

  • 4.开启client
[root@optimize-node02 netio]# javac SocketClient.java && java SocketClient

  • 5.我们的server设置的BACK_LOG是2
## 第一次三次握手的包收到了
[root@optimize-node01 ~]# 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
09:33:46.852061 IP 10.0.0.6.34398 > 10.0.0.5.9090: Flags [S], seq 1640085439, win 29200, options [mss 1460,sackOK,TS val 26528106 ecr 0,nop,wscale 7], length 0
09:33:46.852085 IP 10.0.0.5.9090 > 10.0.0.6.34398: Flags [S.], seq 2795791007, ack 1640085440, win 1152, options [mss 1460,sackOK,TS val 26405372 ecr 26528106,nop,wscale 0], length 0
09:33:46.852353 IP 10.0.0.6.34398 > 10.0.0.5.9090: Flags [.], ack 1, win 229, options [nop,nop,TS val 26528106 ecr 26405372], length 0

##再启动一个client,再次查看tcpdump
[root@optimize-node01 ~]# 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
09:33:46.852061 IP 10.0.0.6.34398 > 10.0.0.5.9090: Flags [S], seq 1640085439, win 29200, options [mss 1460,sackOK,TS val 26528106 ecr 0,nop,wscale 7], length 0
09:33:46.852085 IP 10.0.0.5.9090 > 10.0.0.6.34398: Flags [S.], seq 2795791007, ack 1640085440, win 1152, options [mss 1460,sackOK,TS val 26405372 ecr 26528106,nop,wscale 0], length 0
09:33:46.852353 IP 10.0.0.6.34398 > 10.0.0.5.9090: Flags [.], ack 1, win 229, options [nop,nop,TS val 26528106 ecr 26405372], length 0
## 第二次启动client对应的握手包
09:38:24.140244 IP 10.0.0.6.34400 > 10.0.0.5.9090: Flags [S], seq 1863433434, win 29200, options [mss 1460,sackOK,TS val 26805394 ecr 0,nop,wscale 7], length 0
09:38:24.140266 IP 10.0.0.5.9090 > 10.0.0.6.34400: Flags [S.], seq 1708229072, ack 1863433435, win 1152, options [mss 1460,sackOK,TS val 26682660 ecr 26805394,nop,wscale 0], length 0
09:38:24.140555 IP 10.0.0.6.34400 > 10.0.0.5.9090: Flags [.], ack 1, win 229, options [nop,nop,TS val 26805394 ecr 26682660], length 0

## 第三次启动client对应的握手包
09:39:42.697724 IP 10.0.0.6.34402 > 10.0.0.5.9090: Flags [S], seq 1369513206, win 29200, options [mss 1460,sackOK,TS val 26883950 ecr 0,nop,wscale 7], length 0
09:39:42.697748 IP 10.0.0.5.9090 > 10.0.0.6.34402: Flags [S.], seq 3522848273, ack 1369513207, win 1152, options [mss 1460,sackOK,TS val 26761218 ecr 26883950,nop,wscale 0], length 0
09:39:42.697981 IP 10.0.0.6.34402 > 10.0.0.5.9090: Flags [.], ack 1, win 229, options [nop,nop,TS val 26883952 ecr 26761218], length 0

## 第四次启动client对应的握手包并且一直在刷新
09:43:51.035253 IP 10.0.0.6.34406 > 10.0.0.5.9090: Flags [F.], seq 1, ack 1, win 229, options [nop,nop,TS val 27132288 ecr 26972844], length 0
09:43:56.488116 IP 10.0.0.6.34408 > 10.0.0.5.9090: Flags [S], seq 2158248665, win 29200, options [mss 1460,sackOK,TS val 27137740 ecr 0,nop,wscale 7], length 0
09:43:56.488141 IP 10.0.0.5.9090 > 10.0.0.6.34408: Flags [S.], seq 2953317749, ack 2158248666, win 1152, options [mss 1460,sackOK,TS val 27015008 ecr 27137740,nop,wscale 0], length 0
09:43:56.488484 IP 10.0.0.6.34408 > 10.0.0.5.9090: Flags [.], ack 1, win 229, options [nop,nop,TS val 27137742 ecr 27015008], length 0
09:43:57.484337 IP 10.0.0.6.34406 > 10.0.0.5.9090: Flags [F.], seq 1, ack 1, win 229, options [nop,nop,TS val 27138737 ecr 26972844], length 0
09:43:57.544723 IP 10.0.0.5.9090 > 10.0.0.6.34408: Flags [S.], seq 2953317749, ack 2158248666, win 1152, options [mss 1460,sackOK,TS val 27016065 ecr 27137742,nop,wscale 0], length 0
09:43:57.545678 IP 10.0.0.6.34408 > 10.0.0.5.9090: Flags [.], ack 1, win 229, options [nop,nop,TS val 27138798 ecr 27015008], length 0
09:43:59.545761 IP 10.0.0.5.9090 > 10.0.0.6.34408: Flags [S.], seq 2953317749, ack 2158248666, win 1152, options [mss 1460,sackOK,TS val 27018066 ecr 27138798,nop,wscale 0], length 0
09:43:59.546058 IP 10.0.0.6.34408 > 10.0.0.5.9090: Flags [.], ack 1, win 229, options [nop,nop,TS val 27140799 ecr 27015008], length 0

此时再来看看server的netstat -antp

[root@optimize-node01 ~]# netstat -antp
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      6690/sshd           
tcp        0      0 127.0.0.1:25            0.0.0.0:*               LISTEN      6822/master         
tcp        0      0 10.0.0.5:9090           10.0.0.6:34408          SYN_RECV    -                                    
tcp        0      0 10.0.0.5:22             10.0.0.1:9230           ESTABLISHED 7599/sshd: root@pts 
tcp        0      0 10.0.0.5:22             10.0.0.1:11023          ESTABLISHED 7717/sshd: root@pts 
tcp        0     52 10.0.0.5:22             10.0.0.1:11030          ESTABLISHED 7771/sshd: root@pts 
tcp        0      0 10.0.0.5:22             10.0.0.1:11028          ESTABLISHED 7751/sshd: root@pts 
tcp6       0      0 :::22                   :::*                    LISTEN      6690/sshd           
tcp6       0      0 ::1:25                  :::*                    LISTEN      6822/master         
tcp6       3      0 :::9090                 :::*                    LISTEN      7912/java           
tcp6       0      0 10.0.0.5:9090           10.0.0.6:34398          ESTABLISHED -                   
tcp6       0      0 10.0.0.5:9090           10.0.0.6:34402          ESTABLISHED -                   
tcp6       0      0 10.0.0.5:9090           10.0.0.6:34400          ESTABLISHED -

有三个ESTABLISHED状态的连接(已经建立完3次握手且分配好了资源)
有一个SYN_RECV状态的连接,这个状态代表:我收到了对方的握手信息,但是我没有回复握手信息或者是我回复的握手信息发丢了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值