从Socket中解析Http协议实现通信

        在网络协议中,Socket是连接应用层和运输层的中间层,主要作用为了通信。Http协议是应用层上的封装协议。我们可以通过Http协议的规范解析Socket中数据,完成Http通信。

        首先,我们先回顾一下Http协议的规范。主要复习一下,请求与响应报文格式,方便我们解析Socket中数据。请求报文格式具体如下图:

         响应报文格式具体如下图:

         了解Http请求和响应的报文格式后,可准备编写代码了。Java的Socket支持BIO、NIO等IO模型,我以下的代码使用BIO阻塞模式实现通信,具体代码如下:

        HttpBioServer类主要负责开启Socket服务端监听,当有客户端连接接入后,读取客户端数据,将客户端数据交给另一个线程处理。另一个线程会调用http(),http()会解析Http请求的数据,对数据进行一些操作后,再封装一个响应体返回给客户端。


import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;

/**
 * 〈一句话功能简述〉<br>
 * 〈Bio服务端〉
 *
 * @author hanxiaozhang
 * @create 2023/6/20
 * @since 1.0.0
 */
public class HttpBioServer {


    public static void main(String[] args) throws Exception {

        ServerSocket server = new ServerSocket(9090, 20);

        while (true) {
            // 阻塞1
            Socket client = server.accept();
//            System.out.println(client.getInetAddress());
//            System.out.println(client.getLocalPort());
            System.out.println("client connect success,client port is " + client.getPort());

            new Thread(new Runnable() {

                @Override
                public void run() {
                    try {
                        http(client);
                        client.close();
                        System.out.println("client close");
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    }

    /**
     * Http协议
     *
     * @param client
     * @throws IOException
     */
    private static void http(Socket client) throws IOException {
        // 读取输入流中数据
        ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream();
        try {
            InputStream in = client.getInputStream();
            int len = 0;
            byte[] buf = new byte[1024];
            // 每次读取 1024 字节,知道读取完成
            while ((len = in.read(buf)) != 0) {
                byteArrayOut.write(buf, 0, len);
                if (in.available() == 0) {
                    break;
                }
            }
            byte[] bytes = byteArrayOut.toByteArray();
            RequestEntity request = new RequestEntity();
            request.byteToRequest(bytes);
            byte[] responseBytes = handler(request);
            OutputStream ops = client.getOutputStream();
            ops.write(responseBytes);
            ops.flush();
        } finally {
            byteArrayOut.close();
        }
    }

    private static byte[] handler(RequestEntity request) {

        System.out.println("request is " + request);
        Map<String, String> headers = new HashMap<>(4);
        headers.put("Content-Type", "text/plain");
        String body = "success";

        // 假装处理一些逻辑

        ResponseEntity response = new ResponseEntity(200, "OK", headers, body);
        byte[] responseBytes = response.responseToBytes(request);
        System.out.println("response is " + response);

        return responseBytes;
    }


}

        RequestEntity类主要是存储Http请求解析后的数据,并且包含了一个将请求数据转换为RequestEntity类数据的方法。


import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

/**
 * 〈一句话功能简述〉<br>
 * 〈请求实体〉
 *
 * @author hanxiaozhang
 * @create 2023/6/25
 * @since 1.0.0
 */
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class RequestEntity {

    /**
     * 请求行
     */
    private String requestLine;

    /**
     * 请求方法
     */
    private String method;

    /**
     * Url
     */
    private String url;

    /**
     * 版本协议
     */
    private String requestAndVersion;


    /**
     * header
     */
    private Map<String, String> headers;

    /**
     * 报文内容
     */
    private String body;


    /**
     * 字节数组转换request实体
     *
     * @param bytes
     * @return
     */
    public void byteToRequest(byte[] bytes) {
        // \r\n连续出现两次的情况认为首部结束,剩下是主体部分
        int flag = 0;
        // 是否为body内容
        boolean isBody = false;
        char temp;
        StringBuffer headerSb = new StringBuffer(),
                bodySb = new StringBuffer();
        Map<String, String> headers = new HashMap<>(16);

        // 解析请求报文头和请求body
        for (int i = 0; i < bytes.length; i++) {
            if (isBody) {
                bodySb.append((char) bytes[i]);
            } else {
                temp = (char) bytes[i];
                if (temp == '\r' || temp == '\n') {
                    flag++;
                } else {
                    flag = 0;
                }
                if (flag == 4) {
                    isBody = true;
                }
                headerSb.append(temp);
            }
        }

        // 解析请求行
        String[] lines = headerSb.toString().split("\r\n");
        String requestLine = lines[0];
        String[] requestLines = requestLine.split("\\s");
        this.setRequestLine(requestLine)
                .setMethod(requestLines[0])
                .setUrl(requestLines[1])
                .setRequestAndVersion(requestLines[2]);

        // 解析请求header
        for (int i = 1; i < lines.length; i++) {
            if (lines[i] != "") {
                String[] header = lines[i].split(": ");
                headers.put(header[0], header[1]);
            }
        }

        this.setHeaders(headers)
                .setBody(bodySb.toString());
    }

}

        ResponseEntity类主要是存储Http需要响应的数据,并且包含了一个将ResponseEntity类数据转换成Http响应的方法。

import com.hanxiaozhang.utils.StringUtil;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

import java.util.HashMap;
import java.util.Map;

/**
 * 〈一句话功能简述〉<br>
 * 〈〉
 *
 * @author hanxiaozhang
 * @create 2023/6/25
 * @since 1.0.0
 */
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class ResponseEntity {

    public ResponseEntity(Integer stateCode, String reason, Map<String, String> headers, String body) {
        this.stateCode = stateCode;
        this.reason = reason;
        this.headers = headers;
        this.body = body;
    }

    /**
     * 状态
     */
    private String stateLine;

    /**
     * 版本协议
     */
    private String requestAndVersion;

    /**
     * 状态码
     */
    private Integer stateCode;

    /**
     * 原因
     */
    private String reason;

    /**
     * 响应header
     */
    private Map<String, String> headers;

    /**
     * 响应内容
     */
    private String body;


    public byte[] responseToBytes(RequestEntity request) {

        // 处理状态行
        StringBuilder sb = new StringBuilder();
        this.requestAndVersion = request.getRequestAndVersion();
        this.stateLine = request.getRequestAndVersion() + " " + stateCode + " " + reason;
        sb.append(stateLine);
        sb.append("\r\n");
        // 处理响应header
        Map<String, String> tempHeaders = new HashMap<>(16);
        tempHeaders.putAll(request.getHeaders());
        if (this.headers != null && !this.headers.isEmpty()) {
            tempHeaders.putAll(this.headers);
        }
        if (StringUtil.isNotBlank(this.body)) {
            tempHeaders.put("Content-Length", String.valueOf(this.body.length()));
        }
        tempHeaders.forEach((k, v) -> {
            sb.append(k + ": " + v + "\r\n");
        });
        sb.append("\r\n");
        // 处理响应body
        if (StringUtil.isNotBlank(this.body)) {
            sb.append(this.body);
        }
        return sb.toString().getBytes();
    }

}

        最后,我们启动HttpBioServer类,在浏览器中地址栏请求该地址,看一下效果:

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

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

相关文章

YOLOv8改进 | 检测头篇 | 独创RFAHead检测头超分辨率重构检测头(适用Pose、分割、目标检测)

一、本文介绍 本文给大家带来的改进机制是RFAHead,该检测头为我独家全网首发,本文主要利用将空间注意力机制与卷积操作相结合的卷积RFAConv来优化检测头,其核心在于优化卷积核的工作方式,特别是在处理感受野内的空间特征时。RFAConv主要的优点就是增加模型的特征提取能力,…

【web前端开发】HTML及CSS简单页面布局练习

案例一 网页课程 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"widthdevice-wi…

Linux中ps/kill/execl的使用

ps命令&#xff1a; ps -aus或者ps -ajx或者 ps -ef可以查看有哪些进程。加上 | grep "xxx" 可以查看名为”xxx"的进程。 ps -aus | grep "xxx" kill命令&#xff1a; kill -9 pid 杀死某个进程 kill -l 查看系统有哪些信号 execl函数&#…

在Ubuntu上部署Stable Video Diffusion动画制作

Stable Diffusion团队推出的开源模型Stable Video Diffusion&#xff0c;支持生成约3秒的视频&#xff0c;分辨率为5761024。通过测试视频展示了其令人瞩目的性能&#xff0c;SVD模型是一个生成图像到视频的扩散模型&#xff0c;通过对静止图像的条件化生成短视频。其特点主要包…

Vue源码系列讲解——虚拟DOM篇【三】(更新子节点)

1. 前言 在上一篇文章中&#xff0c;我们了解了Vue中的patch过程&#xff0c;即DOM-Diff算法。并且知道了在patch过程中基本会干三件事&#xff0c;分别是&#xff1a;创建节点&#xff0c;删除节点和更新节点。创建节点和删除节点都比较简单&#xff0c;而更新节点因为要处理…

使用cocos2d-console初始化一个项目

先下载好cocos2d-x的源码包 地址 https://www.cocos.com/cocos2dx-download 这里使用的版本是 自己的电脑要先装好python27 用python安装cocos2d-console 看到项目中有个setup.py的一个文件 python setup.py 用上面的命令执行一下。 如果执行正常的话回出现上面的图 然后…

教师如何找答案? #知识分享#职场发展

当今社会&#xff0c;随着信息技术的迅猛发展&#xff0c;大学生们在学习过程中面临着各种各样的困难和挑战。而在这些挑战中&#xff0c;面对繁重的作业和复杂的题目&#xff0c;大学生搜题软件应运而生 1.快解题 这是一个网站 是一款服务于职业考证的考试搜题软件,拥有几千…

CVE-2018-19518 漏洞复现

CVE-2018-19518 漏洞介绍 IMAP协议&#xff08;因特网消息访问协议&#xff09;它的主要作用是邮件客户端可以通过这种协议从邮件服务器上获取邮件的信息&#xff0c;下载邮件等。它运行在TCP/IP协议之上&#xff0c;使用的端口是143。在php中调用的是imap_open函数。 PHP 的…

特征工程:数据平衡

目录 一、前言 二、正文 Ⅰ.基于过采样算法 Ⅱ.基于欠采样算法 Ⅲ..基于过采样和欠采样的综合算法 三、结语 一、前言 大多数情况下&#xff0c;使用的数据集是不完美的&#xff0c;会出现各种各样的问题&#xff0c;尤其针对分类问题的时候&#xff0c;会出现类别不平衡的…

可达鸭二月月赛——基础赛第六场(周五)题解,这次四个题的题解都在这一篇文章内,满满干货,含有位运算的详细用法介绍。

姓名 王胤皓 T1 题解 T1 题面 T1 思路 样例输入就是骗人的&#xff0c;其实直接输出就可以了&#xff0c;输出 Hello 2024&#xff0c;注意&#xff0c;中间有一个空格&#xff01; T1 代码 #include<bits/stdc.h> using namespace std; #define ll long long int …

机器学习---学习与推断,近似推断、话题模型

1. 学习与推断 基于概率图模型定义的分布&#xff0c;能对目标变量的边际分布&#xff08;marginal distribution&#xff09;或某些可观测变量 为条件的条件分布进行推断。对概率图模型&#xff0c;还需确定具体分布的参数&#xff0c;称为参数估计或学习问 题&#xff0c;…

MATLAB环境下一维时间序列信号的同步压缩小波包变换

时频分析相较于目前的时域、频域信号处理方法在分析时变信号方面&#xff0c;其主要优势在于可以同时提供时域和频域等多域信号信息&#xff0c;并清晰的刻画了频率随时间的变化规律&#xff0c;已被广泛用于医学工程、地震、雷达、生物及机械等领域。 线性时频分析方法是将信…

第十七篇【传奇开心果系列】Python的OpenCV库技术点案例示例:自适应阈值二值化处理图像提取文字

传奇开心果短博文系列 系列短博文目录Python的OpenCV库技术点案例示例系列短博文目录前言一、自适应阈值二值化处理图像提取文字轮廓的初步示例代码:二、扩展思路介绍三、调整自适应阈值二值化的参数示例代码四、对二值化图像进行形态学操作示例代码五、使用轮廓特征进行筛选示…

Ubuntu22.04 gnome-builder gnome C 应用程序习练笔记(三)

八、ui窗体创建要点 .h文件定义(popwindowf.h)&#xff0c; TEST_TYPE_WINDOW宏是要创建的窗口样式。 #pragma once #include <gtk/gtk.h> G_BEGIN_DECLS #define TEST_TYPE_WINDOW (test_window_get_type()) G_DECLARE_FINAL_TYPE (TestWindow, test_window, TEST, WI…

java缓冲流

缓冲流相比较基本流效率更高&#xff0c;因为自带长度的8192缓冲区 缓冲流在io体系中的的位置&#xff1a; 字节缓冲流&#xff1a; 缓冲流的构造方法&#xff1a;输入、输出 **先通过一个练习了解字节缓冲流两个写法&#xff1a; //创建缓冲流对象 BufferedInputStream bis…

零基础学编程怎么入手,中文编程工具构件箱之渐变背景构件用法教程,系统化的编程视频教程上线

零基础学编程怎么入手&#xff0c;中文编程工具构件箱之渐变背景构件用法教程&#xff0c;系统化的编程视频教程上线 一、前言 今天给大家分享的中文编程开发语言工具资料如下&#xff1a; 编程入门视频教程链接 https://edu.csdn.net/course/detail/39036 编程工具及实例…

跟着cherno手搓游戏引擎【22】CameraController、Resize

前置&#xff1a; YOTO.h: #pragma once//用于YOTO APP#include "YOTO/Application.h" #include"YOTO/Layer.h" #include "YOTO/Log.h"#include"YOTO/Core/Timestep.h"#include"YOTO/Input.h" #include"YOTO/KeyCod…

070:vue+cesium: 利用canvas设置线性渐变色材质

第070个 点击查看专栏目录 本示例的目的是介绍如何在vue+cesium中设置线性渐变色的材质,这里使用canvas的辅助方法。 直接复制下面的 vue+cesium源代码,操作2分钟即可运行实现效果. 文章目录 示例效果配置方式示例源代码(共104行)专栏目标示例效果 配置方式 1)查看基础…

每日五道java面试题之java基础篇(三)

第一题. switch 是否能作⽤在 byte/long/String 上&#xff1f; Java5 以前 switch(expr)中&#xff0c;expr 只能是 byte、short、char、int。从 Java 5 开始&#xff0c;Java 中引⼊了枚举类型&#xff0c; expr 也可以是 enum 类型。从 Java 7 开始&#xff0c;expr 还可以…

504. Base 7(七进制数)

题目描述 给定一个整数 num&#xff0c;将其转化为 7 进制&#xff0c;并以字符串形式输出。 问题分析 按照二进制转换的方式进行转换即可 代码 char* convertToBase7(int num) {int count 0;char *x (char *)malloc(sizeof(char)*32);char *y (char *)malloc(sizeof(c…