【学习笔记】手写 Tomcat 八
目录
一、NIO
1. 创建 Tomcat NIO 类
2. 启动 Tomcat
3. 测试
二、解析请求信息
三、响应数据
创建响应类
修改调用的响应类
四、完整代码
五、测试
六、总结
七、获取全部用户的功能
POJO
生成 POJO
1. 在 Dao 层定义接口
2. 获取用户数据
3. 在 Service 层定义接口
4. Service 层的实现方法
5. 创建 Servlet
6. 测试
八、作业
优化NIO
一、NIO
Non-Blocking I/O,非阻塞IO。我们之前使用的是 BIO 阻塞IO
NIO 是同步非阻塞的,服务器的实现模式是一个线程处理多个连接
关于 NIO ,可以看看这位博主写的文章 Java NIO 详解-CSDN博客
1. 创建 Tomcat NIO 类

package com.shao.net;import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.URLDecoder;
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.Iterator;public class TomcatNIO {public TomcatNIO() {// 1. 创建通信管道try {ServerSocketChannel ssc = ServerSocketChannel.open();// 2. 绑定端口ssc.bind(new InetSocketAddress(8080));// 3. 配置非阻塞通信管道ssc.configureBlocking(false);// 4. 创建一个选择器Selector selector = Selector.open();// 5. 将通信管道注册到选择器上,监听客户端连接请求的事件ssc.register(selector, SelectionKey.OP_ACCEPT);// 循环监听客户端请求并处理相应事件while (true) {System.out.println("等待连接...");// 6. 从 selectors 中选择并返回已就绪的通道数int number = selector.select();if (number == 0) continue;// 7. 这些 SelectionKey 对象代表了就绪的通道及其相应的注册事件。Iterator<SelectionKey> it = selector.selectedKeys().iterator();while (it.hasNext()) {SelectionKey key = it.next();// 8. 判断就绪事件类型if (key.isAcceptable()) {System.out.println("有客户端连接了...");// 1. 接受新的客户端连接SocketChannel sc = ssc.accept();// 2. 设置连接的通道为非阻塞模式sc.configureBlocking(false);// 3. 将新连接的通道注册到选择器上,监听读事件sc.register(selector, SelectionKey.OP_READ);System.out.println("客户端连接成功");} else if (key.isReadable()) {// 读事件// 1. 创建缓冲区ByteBuffer buffer = ByteBuffer.allocate(1024);// 2. 将 key 关联的通道 (channel) 转换为 SocketChannel 类型。转换后的 socketChannel 可用于进行网络读写操作。SocketChannel socketChannel = (SocketChannel) key.channel();// 3. 读取数据到缓冲区int read = socketChannel.read(buffer);if (read > 0) {// 获取缓冲区的数据byte[] array = buffer.array();// 转成字符串String msg = new String(array, 0, read);// 将请求信息进行 URL 解码,然后使用 UTF-8 进行编码String message = URLDecoder.decode(msg, "UTF-8");System.out.println("客户端发送了:" + message);// 清空缓冲区buffer.clear();// 响应数据String content = "OK";String HttpResponse = "HTTP/1.1 200 OK\r\n" +"Content-Type: text/html;charset=utf-8\r\n" +"Content-Length: " + content.getBytes().length + "\r\n" +"\r\n" +content;// 写入到缓冲区buffer.put(HttpResponse.getBytes());// 将缓冲区的界限设置为当前位置,然后再把当前位置重置为0buffer.flip();// 响应数据到客户端socketChannel.write(buffer);} else if (read == -1) {// 关闭通道socketChannel.close();// 取消 key 关联的通道在 selector 上的注册key.cancel();}}// 移除已经处理的 SelectionKey,防止下次循环再处理这个键it.remove();}}} catch (IOException e) {e.printStackTrace();}}
}
2. 启动 Tomcat
在入口类启动 NIO 的 Tomcat

3. 测试


二、解析请求信息
因为请求信息和之前是一样的,所以可以使用 HttpRequest 解析类
三、响应数据
创建响应类
因为响应数据的方式不一样,所以需要创建一个 NIO 方式的响应类

package com.shao.net;import com.shao.Servlet.BaseServlet;
import com.shao.Utils.ServletByAnnoUtil;import java.io.FileInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;public class HttpResponseNIO {/*** 连接的通道*/private SocketChannel sc;/*** 缓冲区*/private ByteBuffer buffer;/*** 解析类的对象*/private HttpRequest httpRequest;public HttpResponseNIO(SocketChannel sc, ByteBuffer buffer, HttpRequest httpRequest) {this.sc = sc;this.buffer = buffer;this.httpRequest = httpRequest;}public void response(String filePath) {//判断请求的是否为静态文件if (StaticResourceHandler.isLikelyStaticResource(httpRequest.getRequestModule())) {// 获取静态资源一般是 GET 请求方法if (httpRequest.getRequestMethod().equals("GET")) {// 响应静态资源responseStaticResource(filePath);}} else {// 处理动态请求System.out.println("请求动态资源");// 获取 Servlet 对象,参数是请求的模块名BaseServlet servlet = ServletByAnnoUtil.getServletClass(httpRequest.getRequestModule());// 如果没有找到对应的 Servlet ,返回 404if (servlet == null) {responseStaticResource("webs/pages/not_Found404.html");return;}// 调用 service 方法servlet.service(httpRequest, this);}}/*** 响应静态资源*/private void responseStaticResource(String filePath) {// 读取文件byte[] fileContents = StaticResourceHandler.getFileContents(filePath);// 判断文件是否存在,不存在则返回 404 的页面if (fileContents == null) {try {FileInputStream fis = new FileInputStream("webs/pages/not_Found404.html");fileContents = new byte[fis.available()];fis.read(fileContents);fis.close();} catch (Exception e) {e.printStackTrace();}}// 响应协议String protocol = httpRequest.getRequestProtocol();// 文件媒体类型String fileMimeType = StaticResourceHandler.getFileMimeType(filePath);// 清空缓冲区buffer.clear();// 写入数据到缓冲区buffer.put((protocol + " 200 OK\r\n").getBytes());buffer.put(("Content-Type: " + fileMimeType + "\r\n").getBytes());buffer.put(("Content-Length: " + fileContents.length + "\r\n").getBytes());buffer.put(("\r\n").getBytes());buffer.put(fileContents);// 将缓冲区的界限设置为当前位置,然后再把当前位置重置为0buffer.flip();try {// 响应数据到客户端sc.write(buffer);System.out.println("响应成功");} catch (IOException e) {e.printStackTrace();}}public void send(byte[] content) {// 获取请求协议String protocol = httpRequest.getRequestProtocol();// 清空缓冲区buffer.clear();// 往缓冲区写入数据buffer.put((protocol + " 200 OK\r\n").getBytes());buffer.put(("Content-Type: text/html;charset=utf-8\r\n").getBytes());buffer.put(("Content-Length: " + content.length + "\r\n").getBytes());buffer.put("\r\n".getBytes());buffer.put(content);// 设置缓冲区的界限为当前指针的位置,然后把指针指向缓冲区的起始位置buffer.flip();try {// 响应数据sc.write(buffer);System.out.println("响应成功");} catch (IOException e) {e.printStackTrace();}}
}
修改调用的响应类


四、完整代码
package com.shao.net;import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.URLDecoder;
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.Iterator;public class TomcatNIO {public TomcatNIO() {// 1. 创建通信管道try {ServerSocketChannel ssc = ServerSocketChannel.open();// 2. 绑定端口ssc.bind(new InetSocketAddress(8080));// 3. 配置非阻塞通信管道ssc.configureBlocking(false);// 4. 创建一个选择器Selector selector = Selector.open();// 5. 将通信管道注册到选择器上,监听客户端连接请求的事件ssc.register(selector, SelectionKey.OP_ACCEPT);// 循环监听客户端请求并处理相应事件while (true) {System.out.println("等待连接...");// 6. 从 selectors 中选择并返回已就绪的通道数int number = selector.select();if (number == 0) continue;// 7. 这些 SelectionKey 对象代表了就绪的通道及其相应的注册事件。Iterator<SelectionKey> it = selector.selectedKeys().iterator();while (it.hasNext()) {SelectionKey key = it.next();// 8. 判断就绪事件类型if (key.isAcceptable()) {System.out.println("有客户端连接了...");// 1. 接受新的客户端连接SocketChannel sc = ssc.accept();// 2. 设置连接的通道为非阻塞模式sc.configureBlocking(false);// 3. 将新连接的通道注册到选择器上,监听读事件sc.register(selector, SelectionKey.OP_READ);System.out.println("客户端连接成功");} else if (key.isReadable()) {// 读事件// 1. 创建缓冲区ByteBuffer buffer = ByteBuffer.allocate(1024);// 2. 将 key 关联的通道 (channel) 转换为 SocketChannel 类型。转换后的 socketChannel 可用于进行网络读写操作。SocketChannel socketChannel = (SocketChannel) key.channel();// 3. 读取数据到缓冲区int read = socketChannel.read(buffer);if (read > 0) {// 获取缓冲区的数据byte[] array = buffer.array();// 转成字符串String msg = new String(array, 0, read);// 将请求信息进行 URL 解码,然后使用 UTF-8 进行编码String message = URLDecoder.decode(msg, "UTF-8");// 调用 HttpRequest 类解析请求信息HttpRequest httpRequest = new HttpRequest(message);// 拼接请求的静态资源的路径String filePath = "/webs" + httpRequest.getRequestModule();// 创建响应对象HttpResponseNIO httpResponseNIO = new HttpResponseNIO(socketChannel, buffer, httpRequest);// 响应数据到客户端httpResponseNIO.response(filePath);} else if (read == -1) {// 关闭通信通道socketChannel.close();// 取消 key 关联的通道在 selector 上的注册key.cancel();}}// 9. 移除已经处理的 SelectionKey,防止下次循环再处理这个键it.remove();}}} catch (IOException e) {e.printStackTrace();}}
}
五、测试

六、总结

七、获取全部用户的功能
POJO
因为获取的用户数据需要封装,需要一个类和数据库的字段一一对应,这就是 对象关系映射(ORM) ,这个类可以称为 POJO

生成 POJO


1. 在 Dao 层定义接口

2. 获取用户数据
public ArrayList<Users> GetAllUser() {Connection connection = null;PreparedStatement pstmt = null;ResultSet resultSet = null;ArrayList<Users> users = new ArrayList<>();try {// 3. 从连接池获取一个数据库连接connection = DBConnectPool.getInstance().getConnection();// 4. 获取可执行对象// 定义 SQL 语句String SQL = "select id, account, name, password, money from train.users";pstmt = connection.prepareStatement(SQL);// 5. 执行sql语句,获取结果集resultSet = pstmt.executeQuery();// 6. 结果处理while (resultSet.next()) {// 获取一行数据,封装到对象中Users user = new Users();user.setId(resultSet.getLong("id"));user.setAccount(resultSet.getString("account"));user.setName(resultSet.getString("name"));user.setPassword(resultSet.getString("password"));user.setMoney(resultSet.getDouble("money"));// 添加到集合中users.add(user);}} catch (SQLException e) {e.printStackTrace();} finally {DBConnectPool.getInstance().releaseConnection(connection);DBConnectUtil.releaseSource(pstmt, resultSet);}return users;}
3. 在 Service 层定义接口

4. Service 层的实现方法
public responseDTO GetAllUser() {// 调用 Dao 层获取数据ArrayList<Users> users = userDao.GetAllUser();return new responseDTO(200, users, "获取成功", users.size());}
5. 创建 Servlet

package com.shao.Servlet;import com.alibaba.fastjson2.JSON;
import com.shao.Annotation.MyServlet;
import com.shao.Service.ServiceFactory;
import com.shao.Service.UserService;
import com.shao.Utils.responseDTO;
import com.shao.net.HttpRequest;
import com.shao.net.HttpResponseNIO;@MyServlet("/GetAllUser")
public class GetAllUserServlet extends BaseServlet {responseDTO responseDTO = null;@Overridevoid doGet(HttpRequest httpRequest, HttpResponseNIO httpResponse) {// 获取实例UserService userService = ServiceFactory.getUserService();// 调用获取所有用户的方法responseDTO = userService.GetAllUser();// 响应数据httpResponse.send(JSON.toJSONBytes(responseDTO));}@Overridevoid doPost(HttpRequest httpRequest, HttpResponseNIO httpResponse) {responseDTO = new responseDTO(400, null, "不支持POST提交方法");httpResponse.send(JSON.toJSONBytes(responseDTO));}
}
6. 测试

八、作业
优化NIO
在高并发的场景下,现在的NIO配置无法及时处理,如何解决?
如果客户端发送的数据很多,如何分批次读取数据?
到目前为止,我们学习了 BIO和NIO网络通信模块、HttpRequest、HttpResponse、线程池、任务队列、线程任务对象、Servlet、业务逻辑处理 Service 、Dao 、数据库连接池、POJO、DTO、注解等。这些内容组合起来就是一个简单的框架
相关文章:
【学习笔记】手写 Tomcat 八
目录 一、NIO 1. 创建 Tomcat NIO 类 2. 启动 Tomcat 3. 测试 二、解析请求信息 三、响应数据 创建响应类 修改调用的响应类 四、完整代码 五、测试 六、总结 七、获取全部用户的功能 POJO 生成 POJO 1. 在 Dao 层定义接口 2. 获取用户数据 3. 在 Service 层定…...
24年九月份生活随笔
九月份最后一天,烈士纪念日。 上午看了一会儿直播,庄重的仪式,铭记先辈为新中国抛头颅洒热血,当今盛世,如您所愿。 郑州马拉松官方通告,今天十点公布直通,中签,候补结果。 看完直…...
[含文档+PPT+源码等]精品大数据项目-基于Django实现的高校图书馆智能推送系统的设计与实现
大数据项目——基于Django实现的高校图书馆智能推送系统的设计与实现背景,可以从以下几个方面进行详细阐述: 一、信息技术的发展背景 随着信息技术的飞速发展和互联网的广泛普及,大数据已经成为现代社会的重要资源。在大数据背景下…...
Leecode刷题之路第七天之整数反转
题目出处 07-整数反转 题目描述 个人解法 思路: 1.将整数转换为字符串 2.倒序输出字符串 3.兼容负数case 代码示例:(Java) public int reverse(int x) {Integer integer new Integer(x);String s integer.toString();Strin…...
SpringBoot项目 | 瑞吉外卖 | 短信发送验证码功能改为免费的邮箱发送验证码功能 | 代码实现
0.前情提要 之前的po已经说了单独的邮箱验证码发送功能怎么实现: https://blog.csdn.net/qq_61551948/article/details/142641495 这篇说下如何把该功能整合到瑞吉项目里面,也就是把原先项目里的短信发送验证码的功能改掉,改为邮箱发送验证…...
Windows暂停更新
目录 前言注册表设定参考 前言 不想Windows自动更新,同时不想造成Windows商店不可用,可以采用暂停更新的方案。 但是通过这里设定的时间太短了,所以我们去注册表设定。 注册表设定 win r 输入 regedit进入注册表 HKEY_LOCAL_MACHINE\SOFT…...
alpine安装docker踩坑记
文章目录 前言错误场景正确操作最后 前言 你好,我是醉墨居士,最近使用alpine操作系统上docker遇到了一些错误,尝试解决之后就准备输出一篇博客,帮助有需要的后人能够少踩坑,因为淋过雨所以想给别人撑伞 错误场景 我…...
使用openpyxl轻松操控Excel文件
目录 1. openpyxl 简介2. 安装与快速入门2.1 安装 openpyxl2.2 快速创建一个 Excel 文件2.3 读取 Excel 文件 3. openpyxl 的核心概念3.1 工作簿(Workbook)3.2 工作表(Worksheet)3.3 单元格(Cell)3.4 行与列…...
指定PDF或图片多个识别区域,识别区域文字,并批量对PDF或图片文件改名
常见场景 用户有大量图片/PDF文件,期望能按照图片/PDF中的某些文字对图片/PDF文件重命名。期望工具可以批量处理、离线识别(保证数据安全性)。手工操作麻烦。具体场景:用户有工程现场照片,订单,简历等PDF或…...
Web3中的跨链技术:实现无缝连接的挑战
Web3的到来为互联网带来了去中心化的愿景,而跨链技术则是实现这一愿景的关键。跨链技术旨在解决不同区块链之间的互操作性问题,使得用户和应用能够在多个区块链网络之间无缝地传输数据和价值。尽管这一技术具有广阔的前景,但在实现过程中仍面…...
词袋(Bag of Words, BoW)
词袋(Bag of Words, BoW)模型详解 词袋(BoW)是一种用于文本处理的特征提取方法,常用于自然语言处理(NLP)任务中。在BoW模型中,文本被表示为一个词的无序集合,而忽略了词…...
HTTP Status 404 - /brand-demo/selectAllServlet错误解决原因-Servlet/JavaWeb/IDEA
检查xml文件的包名有无错误检查html文件的url有无写错,是否与Servlet的urlPatterns一致检查Servlet的urlpattern有没有写错(如写成name),检查doPost、doGet是否正常运行 注:IDEA新建Servlet时,默认的WebServlet注解中name需要改urlPatterns&…...
宁夏众智科技OA办公系统存在SQL注入漏洞
漏洞描述 宁夏众智科技OA办公系统存在SQL注入漏洞 漏洞复现 POC POST /Account/Login?ACTIndex&CLRHome HTTP/1.1 Host: Content-Length: 45 Cache-Control: max-age0 Origin: http://39.105.48.206 Content-Type: application/x-www-form-urlencoded Upgrade-Insecur…...
Spring邮件发送:配置与发送邮件详细步骤?
Spring邮件发送教程指南?怎么用Spring邮件发送服务? Spring框架提供了强大的邮件发送支持,使得开发者能够轻松地在应用程序中集成邮件发送功能。AokSend将详细介绍如何在Spring应用中配置和发送邮件,帮助开发者快速掌握这一关键技…...
iPhone/iPad技巧:如何解锁锁定的 iPhone 或 iPad
“在我更新 iPhone 上的软件后,最近我遇到了iPhone 被锁定到所有者的消息,该如何解决?” 根据我们的研究,许多用户在 iOS 18 更新或恢复出厂设置后都会遇到同样的问题。只要出现问题,您就无法使用 iPhone 或 第 1 部分…...
无源码实现免登录功能
因项目要求需要对一个没有源代码的老旧系统实现免登录功能,系统采用前后端分离的方式部署,登录时前端调用后台的认证接口,认证接口返回token信息,然后将token以json的方式存储到cookie中,格式如下: 这里有…...
大数据毕业设计选题推荐-民族服饰数据分析系统-Python数据可视化-Hive-Hadoop-Spark
✨作者主页:IT研究室✨ 个人简介:曾从事计算机专业培训教学,擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…...
疾风大模型气象,基于气象数据打造可视化平台
引言 随着气象数据的广泛应用,越来越多的行业依赖天气预报与气候分析来做出决策。从农业、航空、能源到物流,气象信息无时不刻影响着各行各业的运作。然而,气象数据本身复杂且多样,如何将这些数据转化为直观、易于理解的图形和信…...
PHP安装后Apache无法运行的问题
问题 按照网上教程php安装点击跳转教程,然后修改Apache的httpd.conf文件,本来可以运行的Apache,无法运行了 然后在"C:\httpd-2.4.62-240904-win64-VS17\Apache24\logs\error.log"(就是我下载Apache的目录下的logs中&am…...
[论文精读]Multi-Channel Graph Neural Network for Entity Alignment
论文网址:Multi-Channel Graph Neural Network for Entity Alignment (aclanthology.org) 论文代码:https:// github.com/thunlp/MuGNN 英文是纯手打的!论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误&a…...
React 第五十五节 Router 中 useAsyncError的使用详解
前言 useAsyncError 是 React Router v6.4 引入的一个钩子,用于处理异步操作(如数据加载)中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误:捕获在 loader 或 action 中发生的异步错误替…...
基于服务器使用 apt 安装、配置 Nginx
🧾 一、查看可安装的 Nginx 版本 首先,你可以运行以下命令查看可用版本: apt-cache madison nginx-core输出示例: nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...
【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力
引言: 在人工智能快速发展的浪潮中,快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型(LLM)。该模型代表着该领域的重大突破,通过独特方式融合思考与非思考…...
OkHttp 中实现断点续传 demo
在 OkHttp 中实现断点续传主要通过以下步骤完成,核心是利用 HTTP 协议的 Range 请求头指定下载范围: 实现原理 Range 请求头:向服务器请求文件的特定字节范围(如 Range: bytes1024-) 本地文件记录:保存已…...
React19源码系列之 事件插件系统
事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...
Matlab | matlab常用命令总结
常用命令 一、 基础操作与环境二、 矩阵与数组操作(核心)三、 绘图与可视化四、 编程与控制流五、 符号计算 (Symbolic Math Toolbox)六、 文件与数据 I/O七、 常用函数类别重要提示这是一份 MATLAB 常用命令和功能的总结,涵盖了基础操作、矩阵运算、绘图、编程和文件处理等…...
html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码
目录 一、👨🎓网站题目 二、✍️网站描述 三、📚网站介绍 四、🌐网站效果 五、🪓 代码实现 🧱HTML 六、🥇 如何让学习不再盲目 七、🎁更多干货 一、👨…...
AI病理诊断七剑下天山,医疗未来触手可及
一、病理诊断困局:刀尖上的医学艺术 1.1 金标准背后的隐痛 病理诊断被誉为"诊断的诊断",医生需通过显微镜观察组织切片,在细胞迷宫中捕捉癌变信号。某省病理质控报告显示,基层医院误诊率达12%-15%,专家会诊…...
Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析
Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析 一、第一轮提问(基础概念问题) 1. 请解释Spring框架的核心容器是什么?它在Spring中起到什么作用? Spring框架的核心容器是IoC容器&#…...
springboot整合VUE之在线教育管理系统简介
可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生,小白用户,想学习知识的 有点基础,想要通过项…...
