NIONIO实现HTTP服务器

Posted 喜欢天文

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了NIONIO实现HTTP服务器相关的知识,希望对你有一定的参考价值。

  • NIO 实现的HTTP服务器
    • 该版本只处理了静态资源,如需要处理动态资源可参考上一篇文章
    • 本篇文章的代码注释比较少,详细的NIO的工作流程可参考
/**
 * NIO实现HTTP服务器
 *
 * @author futao
 * @date 2020/7/10
 */

@Slf4j
public class NioHttpServer {

    private static final ByteBuffer READ_BUFFER = ByteBuffer.allocate(1024 * 4);

    /**
     * 静态资源路径
     */

    private static final String STATIC_RESOURCE_PATH = System.getProperty("user.dir") + "/practice/src/main/resources/pages/";

    /**
     * 响应的基础信息
     */

    public static final String BASIC_RESPONSE = "HTTP/1.1 200 OK\r\n" +
            "Content-Type: text/html;charset=utf-8\r\n" +
            "Vary: Accept-Encoding\r\n";

    /**
     * 回车换行符
     */

    private static final String carriageReturn = "\r\n";


    public void start() {
        try {
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.configureBlocking(false);
            serverSocketChannel.bind(new InetSocketAddress("localhost", Constants.SERVER_PORT));

            Selector selector = Selector.open();

            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);


            while (true) {
                int eventCountTriggered = selector.select();
                if (eventCountTriggered == 0) {
                    continue;
                }
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                for (SelectionKey selectionKey : selectionKeys) {
                    handleSelectKey(selectionKey, selector);
                }
                selectionKeys.clear();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    public void handleSelectKey(SelectionKey selectionKey, Selector selector) {
        if (selectionKey.isAcceptable()) {
            ServerSocketChannel serverSocketChannel = (ServerSocketChannel) selectionKey.channel();
            try {
                SocketChannel socketChannel = serverSocketChannel.accept();
                socketChannel.configureBlocking(false);
                socketChannel.register(selector, SelectionKey.OP_READ);
                log.debug("客户端[{}]接入", socketChannel.socket().getPort());
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else if (selectionKey.isReadable()) {
            READ_BUFFER.clear();
            SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
            try {
                while (socketChannel.read(READ_BUFFER) > 0) {
                }
                READ_BUFFER.flip();
                String requestMessage = String.valueOf(Constants.CHARSET.decode(READ_BUFFER));
                log.info("接收到浏览器发来的数据:\n{} === request print end...", requestMessage);
                if (StringUtils.isBlank(requestMessage)) {
                    selectionKey.cancel();
                    selector.wakeup();
                }

                String requestUri = NioHttpServer.getRequestUri(requestMessage);
                staticHandler(requestUri, socketChannel);
                selectionKey.cancel();
                selector.wakeup();

            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 获取请求的资源地址
     *
     * @param request
     * @return
     */

    private static String getRequestUri(String request) {
        //GET /index.html HTTP/1.1
        int firstBlank = request.indexOf(" ");
        String excludeMethod = request.substring(firstBlank + 2);
        return excludeMethod.substring(0, excludeMethod.indexOf(" "));
    }


    /**
     * 静态资源处理器
     *
     * @return
     */

    public boolean staticHandler(String page, SocketChannel socketChannel) throws IOException {
        //资源的绝对路径
        String filePath = NioHttpServer.STATIC_RESOURCE_PATH + page;
        boolean fileExist = false;
        File file = new File(filePath);
        if (file.exists() && file.isFile()) {
            log.debug("静态资源[{}]存在", page);
            fileExist = true;
            //读取文件内容
            byte[] bytes = Files.readAllBytes(Paths.get(filePath));

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

            buffer.put(BASIC_RESPONSE.getBytes(Constants.CHARSET));
            buffer.put(("Server: futaoServerBaseNIO/1.1" + NioHttpServer.carriageReturn).getBytes(Constants.CHARSET));
            buffer.put(("content-length: " + bytes.length + NioHttpServer.carriageReturn).getBytes(Constants.CHARSET));
            buffer.put(NioHttpServer.carriageReturn.getBytes(Constants.CHARSET));
            buffer.put(bytes);
            buffer.flip();

            while (buffer.hasRemaining()) {
                socketChannel.write(buffer);
            }
        }
        return fileExist;
    }

    public static void main(String[] args) {
        new NioHttpServer().start();
    }
}

  • 测试

# 源代码

  • https://github.com/FutaoSmile/learn-IO/tree/master/practice/src/main/java/com/futao/practice/chatroom/nio/httpserver

# 系列文章







欢迎在评论区留下你看文章时的思考,及时说出,有助于加深记忆和理解,还能和像你一样也喜欢这个话题的读者相遇~

image.png


以上是关于NIONIO实现HTTP服务器的主要内容,如果未能解决你的问题,请参考以下文章

NIONIO和IO的比较以及缓冲区

BIONIOAIO 代码实战

BIONIOAIO 代码实战

IOS开发-OC学习-常用功能代码片段整理

12个用得着的 JQuery 代码片段

HTTP客户端代码片段