NIO开发Http服务器(5-完结):HttpServer服务器类

 

最近学习了Java NIO技术,觉得不能再去写一些Hello World的学习demo了,而且也不想再像学习IO时那样编写一个控制台(或者带界面)聊天室。我们是做WEB开发的,整天围着tomcatnginx转,所以选择了一个新的方向,就是自己开发一个简单的Http服务器,在总结Java NIO的同时,也加深一下对http协议的理解。

项目实现了静态资源(htmlcssjs和图片)和简单动态资源的处理,可以实现监听端口、部署目录、资源过期的配置。涉及到了NIO缓冲区、通道和网络编程的核心知识点,还是比较基础的。

 

本文是这个系列文章的最后一篇,主要介绍HttpServer类,该类作用是:

 

  • 打开SelectorServerSocketChannel,根据HttpServerConfig配置启动监听
  • 接收请求连接
  • 开启线程读取请求数据、处理请求

 

文章目录:

NIO开发Http服务器(1):项目下载、打包和部署

NIO开发Http服务器(2):项目结构

NIO开发Http服务器(3):核心配置和Request封装

NIO开发Http服务器(4):Response封装和响应

NIO开发Http服务器(5-完结):HttpServer服务器类

 

Github地址:

https://github.com/xuguofeng/http-server

 

1、启动监听
 1 selector = Selector.open();
 2 
 3 // 打开服务端socket通道
 4 ServerSocketChannel ssc = ServerSocketChannel.open();
 5 // 设置非阻塞
 6 ssc.configureBlocking(false);
 7 // 绑定本地端口
 8 ssc.bind(new InetSocketAddress(config.getServerPort()));
 9 // 把通道注册到Selector
10 ssc.register(selector, SelectionKey.OP_ACCEPT);

 

 

2、轮询
 1 while (true) {
 2 
 3     int s = selector.select();
 4     // 如果没有就绪的通道直接跳过
 5     if (s <= 0) {
 6         continue;
 7     }
 8     // 获取已经就绪的通道的SelectionKey的集合
 9     Iterator<SelectionKey> i = selector.selectedKeys().iterator();
10 
11     while (i.hasNext()) {
12 
13         // 获取当前遍历到的SelectionKey
14         SelectionKey sk = i.next();
15 
16         // 可连接状态
17         if (sk.isValid() && sk.isAcceptable()) {
18             
19         } else if (sk.isValid() && sk.isReadable()) {// 可读取状态
20             
21         }
22         i.remove();
23     }
24 }

 

 

3、接收和读取请求数据

接收请求

 1 ServerSocketChannel server = (ServerSocketChannel) sk.channel();
 2 SocketChannel clientChannel;
 3 try {
 4     // 获取客户端channel
 5     clientChannel = server.accept();
 6     // 设置非阻塞
 7     clientChannel.configureBlocking(false);
 8     // 把通道注册到Selector
 9     clientChannel.register(selector, SelectionKey.OP_READ);
10 } catch (Exception e) {
11 }

 

读取数据

1 // 获取通道
2 SocketChannel sChannel = (SocketChannel) sk.channel();
3 if (socketChannels.get(sChannel.hashCode()) == null) {
4     socketChannels.put(sChannel.hashCode(), sChannel);
5     tp.execute(new RequestHandler(sk));
6 }

 

 

4、请求处理

这是这个类的核心内容,使用RequestHandler处理请求

 

具体实现如下:

  • 从输入通道读取数据,根据配置的解码字符集进行解码
  • 创建Request对象
  • 尝试根据uri获取动态请求处理类,如果是动态请求,就实例化Servlet对象,调用service方法处理请求
  • 输出响应
  • 最后关闭客户端输出通道

 

 1 SocketChannel sChannel = null;
 2 try {
 3     // 获取通道
 4     sChannel = (SocketChannel) sk.channel();
 5     // 声明保存客户端请求数据的缓冲区
 6     ByteBuffer buf = ByteBuffer.allocate(8192);
 7     // 读取数据并解析为字符串
 8     String requestBody = null;
 9     int len = 0;
10     if ((len = sChannel.read(buf)) > 0) {
11         buf.flip();
12         requestBody = new String(buf.array(), 0, len);
13         buf.clear();
14     }
15     if (requestBody == null) {
16         return;
17     }
18 
19     // 请求解码
20     requestBody = URLDecoder.decode(requestBody, config.getRequestCharset());
21 
22     // 创建请求对象
23     Request req = new HttpRequest(requestBody);
24 
25     // 关闭输入
26     sChannel.shutdownInput();
27 
28     // 根据uri获取处理请求的Servlet类型
29     Class<? extends Servlet> servletClass = config.getServlet(req.getRequestURI());
30 
31     // 创建响应对象
32     Response resp = null;
33 
34     // 动态请求
35     if (servletClass != null) {
36         try {
37             Servlet servlet = servletClass.newInstance();
38             resp = new HttpResponse(sChannel);
39             servlet.service(req, resp);
40             resp.setResponseCode(ResponseUtil.RESPONSE_CODE_200);
41         } catch (Exception e) {
42             resp.setResponseCode(ResponseUtil.RESPONSE_CODE_500);
43         }
44     } else {
45         // 静态请求
46         resp = new HttpResponse(req, sChannel);
47     }
48 
49     // 测试,添加cookie
50     if (req.getCookies().isEmpty()) {
51         Cookie c = new Cookie("sessionId", UUID.randomUUID().toString(), 60000);
52         resp.addCookie(c);
53         Cookie c2 = new Cookie("sessionId2", UUID.randomUUID().toString(), 60000);
54         resp.addCookie(c2);
55     }
56 
57     // 输出响应
58     resp.response();
59 
60 } catch (IOException e) {
61 } finally {
62     // 关闭通道
63     try {
64         sChannel.finishConnect();
65         sChannel.close();
66         socketChannels.remove(sChannel.hashCode());
67     } catch (IOException e) {
68     }
69 }
View Code

 

 

5、Servlet接口

处理动态请求的接口,实现类需要在service方法中编写业务处理的程序

1 public interface Servlet {
2 
3     void service(Request request, Response response) throws Exception;
4 }

 

然后在server.properties文件配置

 

转载于:https://www.cnblogs.com/xugf/p/9603933.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我可以回答您的问题。Netty是一个开源的Java NIO框架,它可以用来开发高性能、高可靠性的网络服务器和客户端。下面是一个使用Netty编写HTTP服务器的示例代码: ``` import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.http.HttpObjectAggregator; import io.netty.handler.codec.http.HttpServerCodec; import io.netty.handler.stream.ChunkedWriteHandler; public class HttpServer { private final int port; public HttpServer(int port) { this.port = port; } public void start() throws InterruptedException { EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast("codec", new HttpServerCodec()); ch.pipeline().addLast("aggregator", new HttpObjectAggregator(512 * 1024)); ch.pipeline().addLast("chunked", new ChunkedWriteHandler()); ch.pipeline().addLast("handler", new HttpServerHandler()); } }) .option(ChannelOption.SO_BACKLOG, 128) .childOption(ChannelOption.SO_KEEPALIVE, true); ChannelFuture f = b.bind(port).sync(); f.channel().closeFuture().sync(); } finally { workerGroup.shutdownGracefully(); bossGroup.shutdownGracefully(); } } public static void main(String[] args) throws InterruptedException { HttpServer server = new HttpServer(8080); server.start(); } } ``` 这段代码创建了一个HTTP服务器,监听来自客户端的请求。其中,HttpServerCodec用于将HTTP请求和响应消息编码和解码,HttpObjectAggregator用于将HTTP请求和响应的多个部分合并成一个完整的HTTP消息,ChunkedWriteHandler用于处理HTTP消息的大文件和流数据。 在这个示例中,我们使用了Netty的NIO事件循环组。我们创建了两个事件循环组:一个用于处理传入的连接请求,一个用于处理连接请求后的I/O操作。 这是使用Netty编写HTTP服务器的基础,您可以根据您的具体需求进行更改和定制。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值