1 LISTEN state: indicates the number of connections in the queue. The non-Listen state is the number of bytes.

The LISTEN state

  • Recv -q Indicates the number of bytes in the receive queue.
  • Send-q Indicates the bytes in the Send queue.

2 Recv -q Indicates the number of connections waiting for acceptance. Send-q indicates the capacity of the full connection queue

Represents the Listen Backlog value currently waiting for the server to call Accept for three handshakes, which is the value of the full connection queue in the figure (Send_Q+1 Max). When a client connects to a server that is listening (), the connection passes through the half-connection queue. After receiving an ACK, it enters the Accept queue and waits in it until it is accepted () by the server.

Send – Q says is the largest listen backlog value, this is min (backlog, / proc/sys/net/core/somaxconn) values

3 The detailed process is shown below

4. In actual operation: observe the change of RECV-Q

Start server: (note: these commands in the * * * * connection after the incident | accept action execution Thread before sleep. Sleep (100000);)

Listen on TCP port 9779 using NIO as the server

~/recv_q_send_q# /usr/bin/java NIOServer
Copy the code

NOIServer start run in port 9779
Copy the code

You can see that the default send-q value is 50.

If the number of concurrent requests exceeds 50, what about the excess? Those interested can test Mark by modifying the code themselves

If the waiting time is too long, the client will also receive rejection feedback from the server. In normal cases, if the number of connections exceeds too much and the waiting time is not long, the client can receive normal response from the server

Start the client and run the following command:

/usr/bin/java NIOClient
Copy the code

After 100 seconds:

5 This explains why recV-Q blocks subsequent links when it is debug on the server side, because the full connection queue is full and subsequent links can only wait

Refer to NIO code:

server端

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

public class NIOServer {

    private int num;

    private static final int BLOCK = 4096;

    private static final ByteBuffer sendB = ByteBuffer.allocate(BLOCK);

    private static final ByteBuffer receB = ByteBuffer.allocate(BLOCK);

    private Selector selector;

    public NIOServer(int port) throws IOException {
        //开启ServerSocketChannel
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        //设置为非阻塞
        serverSocketChannel.configureBlocking(false);
        //获取ServerSocket
        ServerSocket serverSocket = serverSocketChannel.socket();
        //绑定ServerSocket提供服务的端口
        serverSocket.bind(new InetSocketAddress(port));
        //开启选择器
        selector = Selector.open();
        //将ServerSocketChannel注册到选择器上
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        System.out.println("NOIServer start run in port " + port);
    }

    /**
     * 监听选择器的数据
     *
     * @throws IOException
     */
    private void listen() throws IOException {
        //循环监听,事件驱动模式
        while (true) {
            //select()阻塞,等待有事件发生时唤醒
            selector.select();
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            while (iterator.hasNext()) {
                SelectionKey selectionKey = iterator.next();
                //处理完后移除该事件
                iterator.remove();
                //处理该事件
                handleKey(selectionKey);

            }
        }
    }

    /**
     * 处理选择器的监听事件
     *
     * @param selectionKey 选择器的监听事件key
     * @throws IOException
     */
    private void handleKey(SelectionKey selectionKey) throws IOException {
        ServerSocketChannel serverSocketChannel = null;
        SocketChannel socketChannel = null;
        int count = 0;

        //客户端新连接
        if (selectionKey.isAcceptable()) {
            //开启通道连接
            serverSocketChannel = (ServerSocketChannel) selectionKey.channel();
            try {
                System.out.println(System.currentTimeMillis() + "收到请求我在睡眠100秒...");
                Thread.sleep(100000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            socketChannel = serverSocketChannel.accept();
            System.out.println(System.currentTimeMillis() + "accept...");

            //设置为非阻塞
            socketChannel.configureBlocking(false);
            //将通道注册到选择器
            socketChannel.register(selector, SelectionKey.OP_READ);

        } else if (selectionKey.isReadable()) {
            //获取读事件通道
            socketChannel = (SocketChannel) selectionKey.channel();
            //清除原先读缓存
            receB.clear();
            //读取通道缓存
            count = socketChannel.read(receB);
            if (count > 0) {
                //解析通道缓存数据
                String receMsg = new String(receB.array(), 0, count);
                System.out.println("receive from client " + receMsg);
                //注册切到写事件
                socketChannel.register(selector, SelectionKey.OP_WRITE);
            }
        } else if (selectionKey.isWritable()) {
            //获取写事件通道
            socketChannel = (SocketChannel) selectionKey.channel();
            //清除发送缓存数据
            sendB.clear();
            String sendMsg = "num " + num++;
            //设置待发送的数据
            sendB.put(sendMsg.getBytes());
            //准备写
            sendB.flip();
            int write = socketChannel.write(sendB);
            System.out.println("send to client " + sendMsg);
            //注册切到读事件
            socketChannel.register(selector, SelectionKey.OP_READ);
        }
    }

    public static void main(String[] args) throws Exception {
        new NIOServer(9779).listen();
    }
}
客户端代码:

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

public class NIOClient {

    private static final int BLOCK = 4096;

    private static final ByteBuffer sendB = ByteBuffer.allocate(BLOCK);

    private static final ByteBuffer receB = ByteBuffer.allocate(BLOCK);

    private SocketChannel socketChannel;

    private Selector selector;

    public NIOClient(String ip, int port) throws IOException {
        //开启通道
        socketChannel = SocketChannel.open();
        //设置为非阻塞
        socketChannel.configureBlocking(false);
        //开启选择器
        selector = Selector.open();
        //将通道注册到选择器
        socketChannel.register(selector, SelectionKey.OP_CONNECT);
        //连接服务端
        socketChannel.connect(new InetSocketAddress(ip, port));

    }

    /**
     * 连接服务器
     */
    public void connect() throws IOException {
        Set<SelectionKey> selectionKeys;
        Iterator<SelectionKey> iterator;
        SelectionKey selectionKey;
        int index = 0;

        while (true && index < 1000) {
            index++;
            selector.select();
            selectionKeys = selector.selectedKeys();
            iterator = selectionKeys.iterator();
            while (iterator.hasNext()) {
                selectionKey = iterator.next();
                handleKey(selectionKey, index);
            }
            selectionKeys.clear();
        }
        //交互10次后关闭连接
        socketChannel.close();
    }

    /**
     * 处理选择器监听事件
     *
     * @param selectionKey
     */
    public void handleKey(SelectionKey selectionKey, int index) throws IOException {
        SocketChannel client;
        int count = 0;
        // 连接事件
        if (selectionKey.isConnectable()) {
            System.out.println("client connect......");
            client = (SocketChannel) selectionKey.channel();
            if (client.isConnectionPending()) {
                client.finishConnect();
                sendB.clear();
                sendB.put("Hello, Server".getBytes());
                sendB.flip();
                client.write(sendB);
            }
            client.register(selector, SelectionKey.OP_READ);
        } else if (selectionKey.isReadable()) {
            // 读事件
            client = (SocketChannel) selectionKey.channel();
            receB.clear();
            count = client.read(receB);
            if (count > 0) {
                String receMsg = new String(receB.array(), 0, count);
                System.out.println("receive from server " + receMsg);
                client.register(selector, SelectionKey.OP_WRITE);
            }
        } else if (selectionKey.isWritable()) {
            // 给客户端注册写事件
            client = (SocketChannel) selectionKey.channel();
            sendB.clear();
            String sendMsg = "index " + index;
            sendB.put(sendMsg.getBytes());
            sendB.flip();
            client.write(sendB);
            System.out.println("send to server " + sendMsg);
            client.register(selector, SelectionKey.OP_READ);
        }
    }

    // 模拟10个请求,并发10秒后同时执行connection
    public static void main(String[] args) throws Exception {
        for(int i = 0; i<10;i++) {
            final int finalI = i;
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("========"+ finalI);
                    try {
                        Thread.sleep(10000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    try {
                        new NIOClient("127.0.0.1", 9779).connect();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }


        System.in.read();

    }

}
Copy the code