计算机网络:Socket编程-NIO进行优化

上篇写了计算机网络:Socket编程-多线程优化和请求响应对象封装

有留心的伙伴就会发现,我先是简单写了一个HTTP服务,然后对这个HTTP服务进行面向对象封装,接着又在封装的基础上进行多线程的优化和请求响应对象的封装,这样一步一步的由简入深。

这篇也是继续深入优化,之前的代码都是使用ServerSocket这个类,从代码中可以看出,它是每个请求都需要单独的线程进行处理,ServerSocket是基于阻塞I/O模型。那有没有非阻塞模式类?

有的,Java NIO库中提供了ServerSocketChannel这个类,它是通过Selector来管理多个通道的,一个线程可以处理多个请求。

1、NIO

NIO
  • 过程:

1(用户请求)-> 2(Pending Queue收集)-> 3、4、5(线程触发Accept,内核从Pending Queue获取一个请求,形成一个Socket文件,拿到文件句柄)-> 6(线程将Socket注册到Selector中,也将内容信息存入Buffer中,放入Channel中)-> 7(工作线程通过响应式获取Channel里的信息)-> 8(工作线程先读取Channel信息,处理完请求后将相关信息写入Buffer,通过Channel再传递给内核)-> 9(内核监听到Socket文件写入信息然后再返回给请求方)

代码实现

package com.hh.http;

import java.io.*;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Function;

/**
 * NIO进步优化
 *
 * @author hang.yuan 2022/3/8 19:50
 */
public class HttpServer4 {

    ServerSocketChannel ssc;

    public void listen(int port) throws IOException {
        ssc = ServerSocketChannel.open();
        ssc.bind(new InetSocketAddress(port));

        // Reactive / Reactor
        ssc.configureBlocking(false);

        Selector selector = Selector.open();

        ssc.register(selector,ssc.validOps(),null);

        ByteBuffer buffer = ByteBuffer.allocate(1024*16);

        for(;;){
            int numOfKeys = selector.select();
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> it = selectionKeys.iterator();
            while (it.hasNext()){
                SelectionKey key = it.next();
                if (key.isAcceptable()){
                    SocketChannel channel = ssc.accept();
                    if (channel == null){
                        continue;
                    }
                    // Kernel --> mmap(buffer) --> Channel --> User(buffer)
                    channel.configureBlocking(false);
                    channel.register(selector,SelectionKey.OP_READ);
                }else {
                    SocketChannel channel = (SocketChannel)key.channel();

                    //_ _ _ _ _ _ _
                    //        P(position)
                    //        L
                    buffer.clear();
                    channel.read(buffer);

                    String request = new String(buffer.array());

                    //Logic ...
                    buffer.clear();
                    buffer.put("HTTP/1.1 200 ok\n\nHello NIO!\n".getBytes());
                    // H T T P / 1 ... ! _ _
                    //                   P(L)
                    // P                 L

                    buffer.flip();//就是把指针反过来
                    channel.write(buffer);
                    channel.close();
                }
            }
        }
    }

    public static void main(String[] args) throws IOException {
        HttpServer4 server = new HttpServer4();
        server.listen(8000);

    }
}

自己也可以学着前几篇用ServerSocket写的HttpServer类优化步骤,依葫芦画瓢一样对ServerSocketChannel写的HttpServer类进一步优化,