手写tomcat(Ⅱ)——Socket通信+tomcat静态资源的获取
Socket通信简介
参考文章:socket通讯原理及例程(一看就懂)
socket是介于应用层(http协议)和传输层(TCP/UDP协议)之间的一层虚拟层

Socket是一个程序,符合TCP/UDP协议的规范,并封装了TCP/UDP等协议
在CS模式(client-server模式,即客户端-服务端模式)中,Socket是客户端和服务端的共同组成部分

从图中我们可以看到,socket负责建立连接,请求数据与响应数据,结束连接
而tomcat负责其中具体的请求处理
Socket的具体实现
第一步,建立连接
/*** tomcat启动类*/
public class TomcatStart {private static Request request = new Request();public static void main(String[] args) throws IOException {System.out.println("socket服务器启动!!!");// 1. 打开相关通信端口// tomcat:8080,mysql:3306,应用软件独占一个端口的全部信息ServerSocket serverSocket = new ServerSocket(8666);// 线程持续扫描当前网卡xxxx端口(死循环),如果有数据就拿过来,交给端口对应的程序处理// 2. 监听并接收请求数据while (true) {// 一旦发现有数据,就打开socket通信// 这里没有创建新的线程,所以这里是main线程监听数据Socket socket = serverSocket.accept();System.out.println(socket.getInetAddress().getCanonicalHostName() + "进行了连接!");// 第二步监听并接收到了数据,处理数据可以用主线程,但是没必要,创建子线程处理// 每接收一次数据,创建一个子线程Thread t1 = new Thread(() -> {// 处理数据包括两部分:读和写try {dataHandle(socket);} catch (Exception e) {throw new RuntimeException(e);}});t1.start();}}
}
第二步,读入并处理请求数据,写出响应数据给浏览器
读入时应该做一步判断,这里没有考虑到动态资源,只有静态资源,所以不需要判断
// 处理数据的方法,读+写public static void dataHandle(Socket socket) throws Exception {// 1. 读取请求的数据// 1.1打开输入流对象,读取socket对象中的数据,这里的数据都是0101010的二进制数据InputStream inputStream = socket.getInputStream();requestContext(inputStream);// 数据的输出Response response = new Response(socket.getOutputStream());// 访问资源response.writeHtml(request.getUrl());}
public static void requestContext(InputStream inputStream) throws IOException {// 1.2二进制数据的翻译并读取int count = 0;while (count == 0) {// 可以不受阻塞地从此输入流读取(或跳过)的估计字节数;如果到达输入流末尾,则返回 0count = inputStream.available();}byte[] bytes = new byte[count];inputStream.read(bytes);// 这里用URLDecoder是为了防止路径中出现特殊符号,经过get请求之后会被URLEncode为乱码String context = URLDecoder.decode(new String(bytes, "utf-8"));System.out.println("===context:" + context);// 空请求if ("".equals(context)) {System.out.println("null request!");} else {// 非空请求,逐行获取request内容//根据换行来获取第一行数据String firstLine = context.split("\\n")[0];// 第一行数据的第2个字符串System.out.println("===url:" + firstLine.split("\\s")[1]);request.setUrl(firstLine.split("\\s")[1]);// 第一行数据的第1个字符串System.out.println("===methodType:" + firstLine.split("\\s")[0]);request.setMethodType(firstLine.split("\\s")[0]);}
我们不难发现完成这一步的关键在于Request类和Response类的具体实现
public class Request implements MyHttpServletRequest{private String url;private String methodType;public String getUrl() {return url;}public void setUrl(String url) {this.url = url;}public String getMethodType() {return methodType;}public void setMethodType(String methodType) {this.methodType = methodType;}
}
public class Response implements MyHttpServletResponse {// 获取输出流private OutputStream outputStream;public Response(OutputStream outputStream) {this.outputStream = outputStream;}// 静态资源的输出public void writeHtml(String path) throws Exception {//// 根据路径返回资源路径地址,例如http://localhost:8666/index.htmlString resource = FileUtil.getResoucePath(path);File file = new File(resource);if (file.exists()) {// 静态资源存在!System.out.println("静态资源存在!");FileUtil.writeFile(file, outputStream);} else {System.out.println(path + "对应的该静态资源不存在!");}}// 数据写回public void write(String context) throws IOException {outputStream.write(context.getBytes());outputStream.flush();}
}
FileUtil在这里完成了前端路径到本地资源路径的转化+响应头的添加+文件输入流转为socket输出流的操作
import java.io.*;
import java.nio.file.Files;/*** 该类的主要作用是进行读取文件*/
public class FileUtil {public static boolean writeFile(InputStream inputStream, OutputStream outputStream) {boolean success = false;// buffer是缓冲的意思BufferedInputStream bufferedInputStream;BufferedOutputStream bufferedOutputStream;try {bufferedInputStream = new BufferedInputStream(inputStream);bufferedOutputStream = new BufferedOutputStream(outputStream);// 先写入响应头,为Content-Type:text/html// Http/1.1 200 \r\nContent-Type:text/html \r\n\r\nbufferedOutputStream.write(ResponseUtil.htmlResponseHeader.getBytes());int count = 0;while (count == 0) {count = inputStream.available();}int fileSize = inputStream.available();long written = 0;int beteSize = 1024;byte[] bytes = new byte[beteSize];while (written < fileSize) {if (written + beteSize > fileSize) {beteSize = (int) (fileSize - written);bytes = new byte[beteSize];}bufferedInputStream.read(bytes);bufferedOutputStream.write(bytes);bufferedOutputStream.flush();written += beteSize;}success = true;} catch (IOException e) {e.printStackTrace();}return success;}public static boolean writeFile(File file, OutputStream outputStream) throws Exception {return writeFile(Files.newInputStream(file.toPath()), outputStream);}/*** 获取资源地址** @param path* @return*/public static String getResoucePath(String path) {String resource = FileUtil.class.getResource("/").getPath();return resource + "\\" + path;}
}
我们启动项目,在浏览器访问localhost:8666/index.html时
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body><p>Hello TomcatDemo!!!</p>
</body>
</html>

控制台输出
至此完成了socket通信+tomcat静态资源获取的仿写
相关文章:
手写tomcat(Ⅱ)——Socket通信+tomcat静态资源的获取
Socket通信简介 参考文章:socket通讯原理及例程(一看就懂) socket是介于应用层(http协议)和传输层(TCP/UDP协议)之间的一层虚拟层 Socket是一个程序,符合TCP/UDP协议的规范&…...
解决Error: error:0308010C:digital envelope routines::unsupported的四种解决方案
问题描述: 报错:Error: error:0308010C:digital envelope routines::unsupported 报错原因: 主要是因为 nodeJs V17 版本发布了 OpenSSL3.0 对算法和秘钥大小增加了更为严格的限制,nodeJs v17 之前版本没影响&am…...
shell 脚本笔记2
3.env与set区别 env用于查看系统环境变量 set用于查看系统环境变量自定义变量函数 4.常用环境变量 变量名称含义PATH命令搜索的目录路径, 与windows的环境变量PATH功能一样LANG查询系统的字符集HISTFILE查询当前用户执行命令的历史列表 Shell变量:自定义变量 目标…...
aws eks集成wasm运行时并启动pod
参考资料 WebAssembly 在云原生中的实践指南,https://cloud.tencent.com/developer/article/2324065 作为一种通用字节码技术,wasm的初衷是在浏览器中的程序实现原生应用性能。高级语言将wasm作为目标语言进行编译并运行在wasm解释器中。和nodejs类似的…...
linux:切分大文件
文章目录 1. 前言2. 用法3. 例子 1. 前言 如果传输、存储过程中出现大文件,希望切分成小文件。在 Linux 中,可以使用多种工具来切分大文件,最常用的是 split 命令。split 命令可以将一个大文件按照指定大小切分成多个小文件。 2. 用法 spl…...
docker 配置文件使用经验,后续持续增加
1. 容器中如何访问主机服务 在docker容器、docker compose 中如何访问主机服务呢? docker容器 20.10.0 版本在 linux 新增 host.docker.internal 支持: docker run -it --add-hosthost.docker.internal:host-gateway alpine cat /etc/hosts 127.0.0.…...
Qml:键盘事件
import QtQuickWindow {width: 640height: 480visible: truetitle: qsTr("Test KeyEvent")//传递给活动窗口的QQuickWindow//传递给当前活动的Item(focus为true),如没则找子节点中的,都没有则忽略Item{id:item1//focus:…...
Java列表导出时将附件信息压缩成一个zip
一:使用场景 在最近的工作当中遇到了一个需求,在列表导出时,不仅需要将列表信息导出为excel文件,同时也需要将列表每一条数据所对应的附件信息放在同一个文件夹当中,并且压缩成一个zip响应给浏览器。首先后端需要写两…...
简单美观易上手的 Docker Compose 可视化管理器 Dockge
本文首发于只抄博客,欢迎点击原文链接了解更多内容。 前言 Dockge 是 Uptime Kuma 作者的新作品,因此 UI 风格与 Uptime Kuma 基本一致,如果你正在使用 Uptime Kuma 的话,那么 Dockge 的 UI 设计应该也不会让你失望。Dockge 主打…...
贴片 RS8752XK 封装SOP-8 250MHz,2通道高速运放
传感器信号放大:在传感器应用中,RS8752XK可以用于放大微弱的传感信号,如压力、温度、光强等传感器的信号。 数据采集系统:在数据采集设备中,RS8752XK可以用于放大和调理模拟信号,以供模数转换器࿰…...
图论-最短路算法
1. Floyd算法 作用:用于求解多源最短路,可以求解出任意两点的最短路 利用动态规划只需三重循环即可(动态规划可以把问题求解分为多个阶段)定义dp[k][i][j]表示点i到点j的路径(除去起点终点)中最大编号不超…...
家政预约小程序05服务管理
目录 1 设计数据源2 后台管理3 后端API4 调用API总结 家政预约小程序的核心是展示家政公司提供的各项服务的能力,比如房屋维护修缮,家电维修,育婴,日常保洁等。用户在选择家政服务的时候,价格,评价是影响用…...
Django自定义命令
Django自定义命令 我们知道,Django内部内置了很多命令,例如 python manage.py runserver python manage.py makemigrations python manage.py migrate我们可以在python控制台中查看所有命令 我们也可以自定义命令,让python manage.py执行…...
详解VLSM技术
在现代网络设计中,如何高效地分配和管理IP地址是一个关键问题。传统的子网划分方法虽然简单,但在实际应用中常常导致IP地址的浪费。为了应对这一问题,VLSM(Variable Length Subnet Mask,可变长子网掩码)技术…...
面向浏览器端免费开源的三维可视化编辑器,包含BIM轻量化,CAD解析预览等特色功能。
ES 3DEditor 🌍Github地址 https://github.com/mlt131220/ES-3DEditor 🌍在线体验 https://editor.mhbdng.cn/#/ 基于vue3与ThreeJs,具体查看Doc 主要功能: 模型导入展示,支持OBJ、FBX、GLTF、GLB、RVT、IFC、SEA、3…...
Nacos 进阶篇---Nacos服务端怎么维护不健康的微服务实例 ?(七)
一、引言 在 Nacos 后台管理服务列表中,我们可以看到微服务列表,其中有一栏叫“健康实例数” (如下图),表示对应的客户端实例信息是否可用状态。 那Nacos服务端是怎么感知客户端的状态是否可用呢 ? 本章…...
【oracle004】oracle内置函数手册总结(已更新)
1.熟悉、梳理、总结下oracle相关知识体系。 2.日常研发过程中使用较少,随着时间的推移,很快就忘得一干二净,所以梳理总结下,以备日常使用参考 3.欢迎批评指正,跪谢一键三连! 总结源文件资源下载地址&#x…...
建模:Maya
一、常用按键 1、alt 左键 —— 环绕查看 2、alt 中键 —— 拖动模型所在面板 3、空格 —— 进入三视图模式;空格 左键按住拖动 —— 切换到对应视图 二、骨骼归零 1、T Pose 旋转模式,点击模型,摆好T姿势即可 2、复制模型设置200距离…...
持续总结中!2024年面试必问 20 道 Redis面试题(四)
上一篇地址:持续总结中!2024年面试必问 20 道 Redis面试题(三)-CSDN博客 七、Redis过期键的删除策略? Redis 过期键的删除策略主要涉及以下几种方式: 1. 定时删除(Timed Expirationÿ…...
Java中关于List的一些常用操作
先定义一个List,代码如下 //定义一个实例类 public class Model{private String id;private String code;private String name;//setter getter 方法省略}//定义一个List,赋值过程省略 List<Model> list new ArrayList<>();1.将List中每一个对象的id…...
MPNet:旋转机械轻量化故障诊断模型详解python代码复现
目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...
基于FPGA的PID算法学习———实现PID比例控制算法
基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容:参考网站: PID算法控制 PID即:Proportional(比例)、Integral(积分&…...
简易版抽奖活动的设计技术方案
1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...
【入坑系列】TiDB 强制索引在不同库下不生效问题
文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...
聊聊 Pulsar:Producer 源码解析
一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台,以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中,Producer(生产者) 是连接客户端应用与消息队列的第一步。生产者…...
【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)
服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...
剑指offer20_链表中环的入口节点
链表中环的入口节点 给定一个链表,若其中包含环,则输出环的入口节点。 若其中不包含环,则输出null。 数据范围 节点 val 值取值范围 [ 1 , 1000 ] [1,1000] [1,1000]。 节点 val 值各不相同。 链表长度 [ 0 , 500 ] [0,500] [0,500]。 …...
【项目实战】通过多模态+LangGraph实现PPT生成助手
PPT自动生成系统 基于LangGraph的PPT自动生成系统,可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析:自动解析Markdown文档结构PPT模板分析:分析PPT模板的布局和风格智能布局决策:匹配内容与合适的PPT布局自动…...
select、poll、epoll 与 Reactor 模式
在高并发网络编程领域,高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表,以及基于它们实现的 Reactor 模式,为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。 一、I…...
如何理解 IP 数据报中的 TTL?
目录 前言理解 前言 面试灵魂一问:说说对 IP 数据报中 TTL 的理解?我们都知道,IP 数据报由首部和数据两部分组成,首部又分为两部分:固定部分和可变部分,共占 20 字节,而即将讨论的 TTL 就位于首…...
