当前位置: 首页 > news >正文

手写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静态资源获取的仿写

相关文章:

  • python手写数字识别(PaddlePaddle框架、MNIST数据集)
  • 嵌入式科普(18)Ubuntu在移动硬盘的安装和启动
  • JavaWeb基础(HTML,CSS,JS)
  • 大数据运维面临的常见问题及其解决方案和步骤
  • 力扣HOT100 - 136. 只出现一次的数字
  • 继续分析开发人员容易被骗的原因和防范措施
  • 3D工业视觉
  • 功耗相关总结
  • Vue3实战笔记(33)—组件传值props终章
  • jsp连接数据库
  • 【考研数学】李林《880》是什么难度水平强化够用吗
  • 全文检索ElasticSearch简介
  • ubuntu 卸载 程序
  • 云和恩墨海外首秀在吉隆坡召开的2024中国智能科技与文化展览会
  • 操作系统 c语言简单模仿进程创建和时间片轮转调度算法中的进程调度
  • @angular/forms 源码解析之双向绑定
  • 《深入 React 技术栈》
  • 002-读书笔记-JavaScript高级程序设计 在HTML中使用JavaScript
  • Android Volley源码解析
  • Apache的80端口被占用以及访问时报错403
  • JAVA SE 6 GC调优笔记
  • JavaScript创建对象的四种方式
  • JSONP原理
  • Js实现点击查看全文(类似今日头条、知乎日报效果)
  • Promise初体验
  • vue2.0开发聊天程序(四) 完整体验一次Vue开发(下)
  • WebSocket使用
  • 持续集成与持续部署宝典Part 2:创建持续集成流水线
  • 检测对象或数组
  • 聊聊directory traversal attack
  • 做一名精致的JavaScripter 01:JavaScript简介
  • 阿里云API、SDK和CLI应用实践方案
  • 摩拜创始人胡玮炜也彻底离开了,共享单车行业还有未来吗? ...
  • ​​​​​​​sokit v1.3抓手机应用socket数据包: Socket是传输控制层协议,WebSocket是应用层协议。
  • ​io --- 处理流的核心工具​
  • #define,static,const,三种常量的区别
  • #我与Java虚拟机的故事#连载09:面试大厂逃不过的JVM
  • (20050108)又读《平凡的世界》
  • (附源码)计算机毕业设计ssm本地美食推荐平台
  • (附源码)计算机毕业设计ssm电影分享网站
  • (考研湖科大教书匠计算机网络)第一章概述-第五节1:计算机网络体系结构之分层思想和举例
  • (六)Hibernate的二级缓存
  • (区间dp) (经典例题) 石子合并
  • (三十五)大数据实战——Superset可视化平台搭建
  • (十二)devops持续集成开发——jenkins的全局工具配置之sonar qube环境安装及配置
  • (文章复现)基于主从博弈的售电商多元零售套餐设计与多级市场购电策略
  • (原创)攻击方式学习之(4) - 拒绝服务(DOS/DDOS/DRDOS)
  • (源码版)2024美国大学生数学建模E题财产保险的可持续模型详解思路+具体代码季节性时序预测SARIMA天气预测建模
  • .bat文件调用java类的main方法
  • .md即markdown文件的基本常用编写语法
  • .net core 使用js,.net core 使用javascript,在.net core项目中怎么使用javascript
  • .NET Entity FrameWork 总结 ,在项目中用处个人感觉不大。适合初级用用,不涉及到与数据库通信。
  • .net on S60 ---- Net60 1.1发布 支持VS2008以及新的特性
  • .NET中两种OCR方式对比
  • @Autowired注解的实现原理