从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) {// 阻塞1Socket 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() {@Overridepublic 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);// 解析请求报文头和请求bodyfor (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]);// 解析请求headerfor (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");// 处理响应headerMap<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");// 处理响应bodyif (StringUtil.isNotBlank(this.body)) {sb.append(this.body);}return sb.toString().getBytes();}}

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

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

“OLED屏幕,色彩绚丽,画面清晰,让每一帧都生动无比。“#IIC协议【下】

"OLED屏幕&#xff0c;色彩绚丽&#xff0c;画面清晰&#xff0c;让每一帧都生动无比。"#IIC协议【下】 前言预备知识1. OLED显示一个点代码实现1.1 OLED显示一个点代码实现核心思路1.2和LCD1602一样需要初始化&#xff0c;看手册&#xff0c;写初识化函数1.3选择Pag…

知到答案在哪搜? #微信#笔记#其他

学习工具是我们的得力助手&#xff0c;帮助我们更好地组织学习内容和时间。 1.试题猪 这是一个公众号 总体来说还是很不错的&#xff0c;题库虽然不是特别全&#xff0c;但是大部分网课答案能够查询到&#xff0c;最重要的是免费的 下方附上一些测试的试题及答案 1、实验室…

C语言函数的栈帧与销毁(面试亮点)

目录 如果你能熟练的掌握函数的栈帧与销毁在面试中是及其亮眼的加分项&#xff0c;所以我们来以实例来将解函数是如何实现栈帧与销毁的。 一. 函数栈帧 二.寄存器 三. 用例题讲解创建栈帧的过程 3.1 main 函数的反汇编代码。 第一步&#xff1a;给调用main函数的函数分配…

STL之list容器的介绍与模拟实现+适配器

STL之list容器的介绍与模拟实现适配器 1. list的介绍2. list容器的使用2.1 list的定义2.2 list iterator的使用2.3 list capacity2.4 list element access2.5 list modifiers2.6 list的迭代器失效 3. list的模拟实现3.1 架构搭建3.2 迭代器3.2.1 正向迭代器3.2.2反向迭代器适配…

大脑是宇宙中最复杂的物体——科学家们试图破译它,读懂人们的思想

2023年&#xff0c;德克萨斯大学HuthLab进行的一项研究在神经科学和技术领域引发了震动。通过人工智能(AI)和脑成像技术的结合&#xff0c;无法与外界交流的人的思想首次被翻译成连续的自然语言。 这是迄今为止最接近读心术的科学方法。在过去的二十年里&#xff0c;神经成像技…

Qt中程序发布及常见问题

1、引言 当我们写好一个程序时通常需要发布给用户使用&#xff0c;那么在Qt中程序又是如何实现发布的呢&#xff0c;这里我就来浅谈一下qt中如何发布程序&#xff0c;以及发布程序时的常见问题。 2、发布过程 2.1、切换为release模式 当我们写qt程序时默认是debug模式&#x…

【51单片机】添加模块代码的常见问题(图示&代码演示)

前言 大家好吖&#xff0c;欢迎来到 YY 滴 系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过C的老铁 主要内容含&#xff1a; 本章节是Lcd1602章节的一部分&#xff0c;以把4个Lcd驱动程序添加为例子&#xff0c;完整传送门在下方传送门 欢迎订阅 YY滴C专栏&…

Red Panda Dev C++ Maker【2.0自创黑客版】使用说明

https://download.csdn.net/download/HappyStarLap/88825258https://download.csdn.net/download/HappyStarLap/88825258Red Panda Dev C&#xff08;旧名 Dev-C 2000&#xff09;是 Orwell Dev-C 的改进分支。包括heker.h、Heike.h、easxy.h 和Art_Text.h。Orwell Dev-C 自 20…

(三)elasticsearch 源码之启动流程分析

https://www.cnblogs.com/darcy-yuan/p/17007635.html 1.前面我们在《&#xff08;一&#xff09;elasticsearch 编译和启动》和 《&#xff08;二&#xff09;elasticsearch 源码目录 》简单了解下es&#xff08;elasticsearch&#xff0c;下同&#xff09;&#xff0c;现在我…

基于tomcat运行jenkins常见的报错处理

目录 1.jenkins.util.SystemProperties$Listener错误 升级jdk11可能遇到的坑 2.java.lang.RuntimeException: Fontconfig head is null, check your fonts or fonts configuration 3.There were errors checking the update sites: UnknownHostException:updates.jenkins.i…

Apache Zeppelin 整合 Spark 和 Hudi

一 环境信息 1.1 组件版本 组件版本Spark3.2.3Hudi0.14.0Zeppelin0.11.0-SNAPSHOT 1.2 环境准备 Zeppelin 整合 Spark 参考&#xff1a;Apache Zeppelin 一文打尽Hudi0.14.0编译参考&#xff1a;Hudi0.14.0 最新编译 二 整合 Spark 和 Hudi 2.1 配置 %spark.confSPARK_H…

Netty应用(三) 之 NIO开发使用 网络编程 多路复用

目录 重要&#xff1a;logback日志的引入以及整合步骤 5.NIO的开发使用 5.1 文件操作 5.1.1 读取文件内容 5.1.2 写入文件内容 5.1.3 文件的复制 5.2 网络编程 5.2.1 accept&#xff0c;read阻塞的NIO编程 5.2.2 把accept&#xff0c;read设置成非阻塞的NIO编程 5.2.3…

低代码平台与BPM:两者是否具有可比性?

传统上&#xff0c;业务流程管理 (BPM) 系统通过消除手动重复工作来帮助企业简化复杂的流程。它用于自动化、监控和分析业务流程&#xff0c;使高层管理人员的工作更轻松。这反过来又提高了所有其他相关利益相关者的生产力&#xff0c;并为业务增长铺平了道路。BPM 软件还使决策…

springboot174基于springboot的疾病防控综合系统的设计与实现

简介 【毕设源码推荐 javaweb 项目】基于springbootvue 的 适用于计算机类毕业设计&#xff0c;课程设计参考与学习用途。仅供学习参考&#xff0c; 不得用于商业或者非法用途&#xff0c;否则&#xff0c;一切后果请用户自负。 看运行截图看 第五章 第四章 获取资料方式 **项…

Linux-3 进程概念(三)

1.环境变量 1.1基本概念 环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数 如&#xff1a;我们在编写C/C代码的时候&#xff0c;在链接的时候&#xff0c;从来不知道我们的所链接的动态静态库在哪里&#xff0c;但是照样可以链接成功…

初识Solidworks:我的第一份作业的感想

从来没用CAD软件画过机械设计图。但我脑子里有一种概念&#xff0c;无非就是把尺规作图软件化&#xff0c;更方便画图、更方便修改、更方便打印一些。但第一份 Solidworks 作业就颠覆了我的认知&#xff0c;考虑到这个软件的上市时间&#xff0c;让我意识到自己对 CAD 软件的认…

如何让内网client通过公网地址访问内网server?

第一步&#xff0c;实现任意公网用户访问内网server。按教育网规矩&#xff0c;公网过来的流量要访问校内网的server必须从教育专线&#xff08;路由器接口G0/0/1)进入。 第二步&#xff0c;实现内网主机通过公网地址210.43.2.3能够访问内网server192.168.1.2&#xff0c;图中①…

ES实战-book笔记1

#索引一个文档,-XPUT手动创建索引, curl -XPUT localhost:9200/get-together/_doc/1?pretty -H Content-Type: application/json -d {"name": "Elasticsearch Denver","organizer": "Lee" } #返回结果 {"_index" : "g…

鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之Toggle组件

鸿蒙&#xff08;HarmonyOS&#xff09;项目方舟框架&#xff08;ArkUI&#xff09;之Toggle组件 一、操作环境 操作系统: Windows 10 专业版、IDE:DevEco Studio 3.1、SDK:HarmonyOS 3.1 二、Toggle组件 组件提供勾选框样式、状态按钮样式及开关样式。 子组件 仅当Toggl…

林浩然与杨凌芸的Java异常处理大冒险

林浩然与杨凌芸的Java异常处理大冒险 Lin Haoran and Yang Lingyun’s Java Exception Handling Adventure 在一个阳光明媚的午后&#xff0c;编程世界的英雄——林浩然和杨凌芸坐在Java王国的咖啡馆里&#xff0c;一边品尝着香醇的代码咖啡&#xff0c;一边探讨着他们的最新挑…