手写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) {
        // 可以不受阻塞地从此输入流读取(或跳过)的估计字节数;如果到达输入流末尾,则返回 0
            count = 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.html
        String 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\n
            bufferedOutputStream.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静态资源获取的仿写

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/635428.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

GNU Radio之OFDM Divide和Matrix Transpose底层C++实现

文章目录 前言一、OFDM Divide 模块1、简介2、模块作用3、参数意义4、C 具体实现 二、Matrix Transpose 模块1、简介2、参数意义3、C 具体实现 前言 gr-radar 中的 OFDM Divide 模块是GNU Radio中的一个组件&#xff0c;专门用于处理正交频分复用&#xff08;OFDM&#xff09;…

力扣HOT100 - 72. 编辑距离

解题思路&#xff1a; 动态规划 class Solution {public int minDistance(String word1, String word2) {int n1 word1.length();int n2 word2.length();int[][] dp new int[n1 1][n2 1];for (int j 1; j < n2; j) dp[0][j] dp[0][j - 1] 1;for (int i 1; i < …

【Android安全】AOSP版本对应编号| AOSP版本适配Pixel或Nexus型号 | 驱动脚本下载地址

AOSP版本对应编号 https://source.android.com/docs/setup/about/build-numbers?hlzh-cn#source-code-tags-and-builds 例如android-8.1.0_r1 对应的编号是OPM1.171019.011 可以适配Pixel 2 XL AOSP驱动脚本下载 编译AOSP时&#xff0c;需要Google的驱动&#xff0c;后面才…

SaToken+SpringBoot+Redis前后端分离登录认证

目录 前言一、创建工程项目&#x1f38d;1.1 创建后端工程1.2 创建前端工程 二、业务代码&#x1f38a;后端代码前端代码 三、测试参考资料 前言 Sa-Token 是一款 Java 语言的权限认证框架&#xff0c;提供了灵活、高效、易用的权限认证和会话管理功能。它是 SpringBoot、Spri…

解除网页禁止选择

控制台输入以下命令 复制&#xff1a;javascript:void(document.body.οncοpy) 可选&#xff1a;javascript:void(document.body.onselectstart) 拖拉&#xff1a;javascript:void(document.body.οnmοuseup)

短剧系统源码解析与应用

在数字化时代&#xff0c;短剧作为一种新兴的娱乐形式&#xff0c;因其内容紧凑、节奏快速而受到广大年轻群体的喜爱。短剧系统源码的开发和应用&#xff0c;不仅为创作者提供了一个展示才华的平台&#xff0c;也为观众带来了全新的观看体验。本文将对短剧系统源码进行解析&…

MVSnet 代码详解(pytorch)

大致过一下MVSnet 论文中核心的点对应代码应该怎么写。 forward 函数需要 照片&#xff0c;映射矩阵&#xff0c;以及深度值。 照片的shape是 &#xff08;1&#xff0c;5,3&#xff0c;1184,1600&#xff09;代表着1个batch,5张图片&#xff0c;然后一次是每张图片的channel和…

【软考】设计模式之装饰器模式

目录 1. 说明2. 应用场景3. 结构图4. 构成5. 适用性6. 优点7. 缺点8. java示例 1. 说明 1.动态地给一个对象添加一些额外的职责。2.Decorator Pattern。3.就增加功能而言&#xff0c;装饰器模式比生成子类更加灵活。4.一种在不改变现有对象结构的情况下&#xff0c;动态地给对…

quartz定时任务

Quartz 数据结构 quartz采用完全二叉树&#xff1a;除了最后一层每一层节点都是满的&#xff0c;而且最后一层靠左排列。 二叉树节点个数规则&#xff1a;每层从左开始&#xff0c;第一层只有一个&#xff0c;就是2的0次幂&#xff0c;第二层两个就是2的1次幂&#xff0c;第三…

Go 和 Delphi 定义可变参数函数的对比

使用可变参数函数具有灵活性、重用性、简化调用等优点&#xff0c;各个语言有各自定义可变参数函数的方法&#xff0c;也有通用的处理方法&#xff0c;比如使用数组、定义参数结构体、使用泛型等。 这里总结记录一下 go、delphi 的常用的定义可变参数函数的方式&#xff01; 一…

Docker安装MongoDB(Linux版)

文章目录 前言一、Docker环境的准备1.安装依赖2.安装Docker 二、使用Docker安装MongoDB1.mongo版本选取2.拉取合适的镜像3.宿主机创建MongoDB需要挂载的文件夹4.第一次无认证创建mongo用户5.启动需要认证的mongo容器 问题汇总总结 前言 本文章主要介绍在Centos系统&#xff0c…

delphi fmx 跨平台文件浏览器

很多人在找delphi fmx 开发的 android下的文件浏览器 现在她来了 支持android,ios android12 测试通过 代码: object Form1: TForm1Left = 0Top = 0Caption = Form1ClientHeight = 549ClientWidth = 340FormFactor.Width = 320FormFactor.Height = 480FormFactor.Dev…

【GDAL】GDAL库学习(C#版本)

1.GDAL 2.VS2022配置GDAL环境&#xff08;C#&#xff09; VS2022工具–NuGet包管理器–管理解决方案的NuGet程序包&#xff0c;直接安装GDAL包。 并且直接用应用到当前的控制台程序中。 找一张tiff格式的图片&#xff0c;或者用格式转换网站&#xff1a;https://www.zamzar.c…

Web前端开发技术、详细文章、(例子)html 列表、有序列表、无序列表、列表嵌套

目录 列表概述 列表类型与标记符号 无序列表 语法&#xff1a; 语法说明&#xff1a; 无序列表标记的 type 属性及其说明 代码解释 有序列表 基本语法 属性说明 1、列表 o1标记的属性 2、列表项li标记的属性 有序列表 o1标记的属性、值 代码解释 列表嵌套 基本…

【Qt】深入探索Qt主窗口与菜单栏:构建高效用户界面的实践指南

文章目录 前言1. 什么是Main Window?2. 详细了解一下其中的 菜单栏&#xff1a;2.1. 创建菜单栏2.2. 添加快捷键2.3. 添加子菜单2.4. 添加分割线2.5. 添加图标 3. 内存泄漏问题&#xff1a;总结 前言 在现代软件开发中&#xff0c;用户界面的设计对于提升用户体验至关重要。Q…

秀某动预约抢票脚本

秀某动预约抢票脚本 小白操作-仅供学习参考 主要流程和功能 初始化和配置变量: confirm_url 和 login_url: 分别存储登录和确认订单的URL。 wait_time: 用户输入的提前多少秒开始执行。 start_time: 开售时间。 DEBUG: 调试标志&#xff0c;用于控制脚本的行为。 浏览…

并网逆变器学习笔记9---VSG控制

参考文献&#xff1a;《新型电力系统主动构网机理与技术路径》 “构网技术一般包含下垂控制&#xff0c;功率同步控制&#xff0c;虚拟同步机控制&#xff0c;直接功率控制&#xff0c;虚拟振荡器控制 等。其中&#xff0c;虚拟同步机技术&#xff0c;即 VSG&#xff0c;因其物…

css扇形菜单动画效果

菜单组件 IntelligentAnalysis.vue 中间圆形区域可以换个图片 <template><div class"intel-analysis"><div class"info" :class"{ close-animation: !showMenu }"><div class"middle"></div><div cl…

协变(List泛型作为方法参数时的父类子类问题)

有段时间没搞.net的项目了&#xff08;没办法&#xff0c;谁让国内JAVA流行是事实&#xff09;。最近又回归.net&#xff08;哪里需要哪里搬~&#xff09;。 接收到需求后&#xff0c;一顿输出&#xff0c;结果…咦?编译失败??? 错误信息&#xff1a; CS1503:参数1:无法…

阿里云 EMR Serverless Spark 版开启免费公测

阿里云 EMR Serverless Spark 版是一款云原生&#xff0c;专为大规模数据处理和分析而设计的全托管 Serverless 产品。它为企业提供了一站式的数据平台服务&#xff0c;包括任务开发、调试、调度和运维等&#xff0c;极大地简化了数据处理的全生命周期工作流程。使用 EMR Serve…