手写tomcat(四):实现NIO模型的tomcat

手写tomcat(三)中,我实现了基于BIO模型的多线程tomcat,而这次我实现了Nio的多线程模型, 2020年4月17日,我将该文章进行了修改,原先写的是什么**玩意儿.

以下是基于NIO模型监听请求以及开启线程处理收到的请求。

首先是服务器,负责接收请求和断开连接等。

HttpServer.java:

package com.tomcat.nio;

import com.tomcat.baseservlet.AbstractServlet;

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.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.*;

/**
 * NIO版本的tomcat
 * 监听请求,调用request和response对请求作出反应
 * @author 申劭明
 * @date 2020/4/18 17:21
 * @version 5.1
 */
public class HttpServer {

    /**
     * 监听端口
     */
    private static int port = 8080;

    /**
     * Key值为Servlet的别名(uri),value为该Servlet对象
     * default权限
     */
    private static HashMap<String, AbstractServlet> map;

    /**
     * 监听通道
     */
    private ServerSocketChannel serverSocketChannel;
    /**
     * NIO负责轮询的Selector
     */
    private Selector selector;

    /**
     * @Description : nio监听数据请求
     * @author : 申劭明
     * @date : 2019/9/17 10:29
     */
    public void acceptWait() {
        try {
            serverSocketChannel = ServerSocketChannel.open();
            // 设置ServerSocketChannel为非阻塞
            serverSocketChannel.configureBlocking(false);
            serverSocketChannel.bind(new InetSocketAddress(port));
            //selector获取不同操作系统下不同的TCP连接动态
            selector = Selector.open();

            //给当前的serverSocketChannel注册选择器,根据条件查询符合情况的TCP连接
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        } catch (IOException e) {
            e.printStackTrace();
        }

        while (true) {
            try {
                if (selector.select(1000) == 0) {
                    // 阻塞式获取请,有几个被selector发现了网络请求,这个方法的返回值就是多少
                    // 没有网络请求
                    continue;
                }
                // 获取当前时间点的所有事件
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                // 遍历所有有事件发生的通道
                while (iterator.hasNext()) {
                    SelectionKey key = iterator.next();
                    // 判断当前通道是否已经做好socket连接的准备
                    if (key.isAcceptable()) {
                        //拿到新的对象
                        SocketChannel channel = serverSocketChannel.accept();
                        if (channel != null) {
                            // 注册连接对象,进行关注,no-Blocking
                            channel.configureBlocking(false);
                            // 将该 socketChannel 通道注册到selector中
                            // 注意,上面的注册是ServerSocketChannel

                            channel.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(1024));
                        }
                    } else if (key.isReadable()) {
                        //如果当前通道是可读的
                        SocketChannel socketChannel = (SocketChannel) key.channel();

                        //处理过程中,先取消selector对应连接的注册,避免重复
                        key.cancel();
                        ByteBuffer buffer = (ByteBuffer) key.attachment();
                        socketChannel.read(buffer);
                        // 处理消息
                        RequestHandler.handler(socketChannel, buffer);
                        // 关闭连接
                        socketChannel.close();

                    }
                    iterator.remove();
                }
                // 检查过程就绪,清除之前的调用效果
                selector.selectNow();
            } catch (IOException e) {
                // 避免因为某一个请求异常而导致程序终止
                e.printStackTrace();
            }

        }
    }

}

 

接下来是对于请求的处理,时间关系我就不再像之前一样,将请求分发给Servlet了。

RequestHandler.java: 

package com.tomcat.nio;


import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;

/**
 * @author 申劭明
 * @date 2020/4/17 15:12
 */
public class RequestHandler {

    /**
     * @Description : nio中的消息处理
     *
     * @param socketChannel socket通道对象,相当于一次socket连接
     * @param buffer 读取到的请求内容
     * @Return : void
     * @Author : 申劭明
     * @Date : 2020/4/17 15:48
    */
    static void handler(SocketChannel socketChannel, ByteBuffer buffer) throws IOException {
        // 时间关系就不接之前的Servlet处理逻辑啦
        System.out.println("Receive message: " + new String(buffer.array()));
        String resultData = "HelloWorld";
        socketChannel.write(ByteBuffer.wrap(resultData.getBytes()));

    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值