NIO开发Http服务器(5

Stella981
• 阅读 612

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

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

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

  • 打开Selector和ServerSocketChannel,根据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方法处理请求
  • 输出响应
  • 最后关闭客户端输出通道

NIO开发Http服务器(5 NIO开发Http服务器(5

 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文件配置

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
3个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Eclipse插件开发_学习_00_资源帖
一、官方资料 1.eclipseapi(https://www.oschina.net/action/GoToLink?urlhttp%3A%2F%2Fhelp.eclipse.org%2Fmars%2Findex.jsp%3Ftopic%3D%252Forg.eclipse.platform.doc.isv%252Fguide%2
Wesley13 Wesley13
3年前
35岁是技术人的天花板吗?
35岁是技术人的天花板吗?我非常不认同“35岁现象”,人类没有那么脆弱,人类的智力不会说是35岁之后就停止发展,更不是说35岁之后就没有机会了。马云35岁还在教书,任正非35岁还在工厂上班。为什么技术人员到35岁就应该退役了呢?所以35岁根本就不是一个问题,我今年已经37岁了,我发现我才刚刚找到自己的节奏,刚刚上路。
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
9个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这