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

  • 13
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值