NIO

引述

前文说了 BIO 无法克服上连接的问题,那么有什么模式可以支持长连接呢?

这里介绍下 NIO 模型,即同步非阻塞。每当我们去食堂点餐时,点好餐后还得要排队等厨师弄好取餐。在这个过程中,我们得一直在这条队伍里等待,不能上厕所也不能坐下休息。这里就相当于 BIO 模型中,当有一个连接建立后,就得要有一个线程去处理,直到连接处理完毕,并且这个线程在整个过程中都被占用。

于是有人提出了一种思路:我们下单后就给我们一个订单号,并且有个屏幕显示现在哪个订单已经做完了。于是,我们彻底被解放了,我们可以去做其他事情,甚至我们可以晚点取餐(做完手头上的事情再去取餐),且取餐的过程也几乎不用排队。

NIO 的思路也是这样,通过一个容器去记录下新建的连接,然后我们不断循环监听容器里的连接,当连接有数据传回时,我们让一个线程去处理。(处理完毕后就退出执行线程,但这时还并不是断开连接。)

代码

通过 Netty 来模拟 NIO 的过程。

服务端:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public class NIOServer extends Thread {
    
    LinkedList<SocketChannel> clients = new LinkedList<>();  // 存放连接的容器
    
    public static void main(Stringp[] args) throw IOException {
        NIOSocket socket = new NIOSocket();
        
        ServerSocketChannel ss = ServerSocketChannel.open();  // 服务端开启监听,接收客户端
        ss.bind(new InetSocketAddress(8080)); // 监听 8080 端口
        ss.configureBlocking(false);   // 设置非阻塞模式,即每个连接都不会一直占用线程
        socket.start();  // 开启这个线程,让他一直循环,判断有没有接收到连接发送的数据
         
        while (true) {
            // 接收客户端的连接
            SocketChannel client = ss.accept();  // 则个阶段不会阻塞,若没有连接,则返回 null
            if (client != null) {
                client.configureBlocking(false);  // 将客户端的连接也设置为非阻塞
                clients.add(client);  // 将此连接放入容器中
            }
        }
    }
    
    @Override
    public void run() {
        ByteBuffer buffer = ByteBuffer.allocateDirect(4096);   // 设置读取缓冲区
        int i = 0;
        while (true) {
            if (i == clients.size())
                i = 0;
            SocketChannel c = clients.get(i++);
            try {
                int num = c.read(buffer);   // -1:未写,0:断开,n:已写
                if (num > 0) {
                    buffer.flip();
                    byte[] aaa = new byte[buffer.limit()];
                    buffer.get(aaa);
                    String b = new String(aaa);
                    System.out.println(c.socket().getPort() + ":" + b);
                    buffer.clear();
                } else if (num == 0) {  // 改连接断开
                    c.close();
                    clients.remove(c);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

客户端

1
2
3
4
5
6
7
public class NIOClient {
    public static void main(String[] args) throw IOException {
        SocketChannel sc = SocketChannel.open();  // 开启客户端连接
        sc.connect(new InetSocketAddress("localhost", 8080));  // 设置服务端地址
        System.out.println("等待接收数据");
    }
}

优势

NIO 的最大优势就是不让一个连接独占线程,通过对每个连接打个标记,当连接需要线程时就给它线程,不需要时就释放线程。并且也因为有标记的存在,即它支持的模式是长连接的模式,每次发送数据是都不需要三次握手连接。

updatedupdated2022-06-232022-06-23