io-nio-selector-epoll

c10k problem

Proposed around 2000, 10K sockets under the BIO model deal with the problem of slow data transmission between the client and the server.
Single-threaded simulation of 10k clients

package io.bio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SocketChannel;
import java.util.LinkedList;
import java.util.List;

/**
 * Author: ljf
 * CreatedAt: 2021/3/31 下午2:24
 */
public class Ck10Client {
    
    
    public static void main(String[] args) {
    
    
        List<SocketChannel> clients = new LinkedList<>();
        InetSocketAddress serverAddr = new InetSocketAddress("192.168.172.3",9090);

        for(int i = 10000;i<65000;i++){
    
    
            try {
    
    
                SocketChannel client1 = SocketChannel.open();

                /**
                 * linux 中看到的是 :
                 * client1 ... port 10002
                 * client2 ... port 10002
                 */

                client1.bind(new InetSocketAddress("192.168.172.1",i));
                client1.connect(serverAddr);
                clients.add(client1);

                SocketChannel client2 = SocketChannel.open();
                client2.bind(new InetSocketAddress("192.168.8.103",i));
                client2.connect(serverAddr);
                clients.add(client2);

            } catch (IOException e) {
    
    
                e.printStackTrace();
            }
        }
        System.out.println("clients :" + clients.size());

        try {
    
    
            System.in.read();
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
    }
}

The server and the client communicate, there will be two sockets in the kernel, one is the socket of the server kernel listen client, and the other is the socket that communicates with each other after the client comes in (first netstat -natp to see the pid of java, then lsof- p pid can be seen)
1. BIO model

package io.bio;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * bio 慢的原因:
 * 1.accept阻塞, 后 new thread 那一步会发生系统调用 clone,这里用户态系统太切换
 * 2.客户端连进来后的io流也是阻塞的。
 * Author: ljf
 * CreatedAt: 2021/3/31 下午1:38
 */
public class SocketBIO {
    
    

    public static void main(String[] args) {
    
    
        try {
    
    
            ServerSocket server = new ServerSocket(9090, 5);
            System.out.println("step1: new ServerSocket(9090,5)");
            while (true) {
    
    
                Socket client = server.accept();
                System.out.println("step2:client \t" + client.getPort());

                new Thread(new Runnable() {
    
    
                    @Override
                    public void run() {
    
    
                        InputStream inputStream = null;
                        try {
    
    
                            inputStream = client.getInputStream();
                            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
                            String s = reader.readLine();
                            while (true) {
    
    
                                if (s != null) {
    
    
                                    System.out.println(s);
                                } else {
    
    
                                    inputStream.close();
                                    break;
                                }
                            }
                        } catch (IOException e) {
    
    
                            e.printStackTrace();
                        }
                    }
                }).start();
            }
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
    }
}

Blocking occurs in the server accepts the client and the server waits for the client data (kernel RECV(6) two places. So slow.

2.NIO model

package io.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.LinkedList;
import java.util.List;

/**
 * nio 的n 有两个意思:
 * 1.是accept(5 的时候 是 NON_BLOCKINg,和 RECV(6 的时候非阻塞
 * 2.java 的new io ,即新io的意思
 * <p>
 * Author: ljf
 * CreatedAt: 2021/3/31 下午3:16
 */
public class SocketNIO {
    
    

    public static void main(String[] args) {
    
    

        List<SocketChannel> clients = new LinkedList<>();
        try {
    
    
            ServerSocketChannel ss = ServerSocketChannel.open();// 服务端开启监听,接收客户端
            ss.bind(new InetSocketAddress(9090)); // 绑定本地的9090端口
            ss.configureBlocking(false); // 重点,这里是用非阻塞的方式接收客户端

            while (true) {
    
    
                // 接收客户端连接
//                Thread.sleep(1000);
                // accept 调用了内核的accept,没有客户端连进来返回值,在BIO的时候一直卡着,NIO不看着,返回-1,java 返回null
                // 有客户端连进来,accept 返回这个客户端的FD5,client Object
                // NONBLOCKING 就是代码能往下走了,但是往下走的情况要根据客户端是否连进来有不同
                SocketChannel client = ss.accept();
                if (client == null) {
    
    
                    System.out.println("null ...");
                } else {
    
    
                    client.configureBlocking(false); // 重点,socket(服务端的listen
                    // socket<连接请求三次握手后,往这里扔,我去通过accept得到连接的socket>,连接socket<往后的数据读写使用的>)
                    int port = client.socket().getPort();
                    System.out.println("client port : " + port);
                    clients.add(client);
                }

                ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1024);// 这种是直接在服务器内存分配,还有一种是allocate(int capacity)
// (这种是在jvm里分配,即堆内存)第一种会分配比较慢,第二种是发生系统内存到堆内存的复制,也不一定快,具体看运行情况

                // 遍历已经连进来的客户端能不能读写数据
                for (SocketChannel s : clients) {
    
    
                    int num = s.read(byteBuffer); // >0 -1 0 不会阻塞
                    if (num > 0) {
    
    
                        byteBuffer.flip();
                        byte[] bytes = new byte[byteBuffer.limit()];
                        byteBuffer.get(bytes);

                        String b = new String(bytes);
                        System.out.println(client.socket().getPort() + " : " + b);
                        byteBuffer.clear();
                    }
                }
            }
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
    }
}

The reason is in the code comments. At present, the code writing has a problem that the clients will be slow to traverse as the infinite loop increases, so they will run slower and slower, and finally either report the insufficient file descriptors error.

Guess you like

Origin blog.csdn.net/weixin_39370859/article/details/115357927