socket实现http协议

article/2025/8/27 16:07:15

1、在B/S架构中,Web浏览器与Web服务器之间的一次HTTP请求与响应:需要完成以下步骤:

http://localhost:8089/index.html

1)、浏览器端根据所在的HTTP协议解析出对应的url域名:localhost

2)、通过DNS域名解析,查询出该域名对应的IP地址:127.0.0.1

3)、通过URL解析出对应的端口号:8080

4)、浏览器发起并建立到127.0.0.1的连接(SocketTCP的三次握手)

关于SocketTCP的三次握手建立连接如下:

a、浏览器向服务器发送一个SYN J

b、服务器对SYN J进行确认ACK J+1,向浏览器响应一个SYN KACK J + 1

c、浏览器再向服务器发送一个确认ACK K + 1

 

5)、浏览器向服务器发送GET请求

6)、服务器响应浏览器的请求

7)、浏览器读取响应,根据http协议渲染页面

8)、浏览器关闭与服务器的连接(TCP四次挥手)


现在实现一个简单的多线程http服务器:

对于一个web服务器就是在一台虚拟主机上建立的一个特定的socket连接服务端端口,

然后一直等待浏览器发送连接请求,

一旦有一个连接建立就会产生一个Request和Response对象,

然后按照http协议去读取request对象中的请求的参数,找到相应的资源文件,并通过IO进行读写,将要返回的资源封装到response对象中

response再按照http协议返回给浏览器。


import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.StringTokenizer;/*** * @author jxc* 一个进程对应可以有多个线程,采用多线程服务器实现,* 一是在连接过多的时候使得已经连接的请求可以传递消息,* 尤其是I/O设备读写的时候提高效率* 二十防止在只有主线程的情况下只有一个线线程出现网络延迟或者阻塞而队列中所有进程都进入死锁*/
public class TestSocketHttp{private static int PORT = 8089;public static void main(String[] args) {try{ServerSocket serverSocket = new ServerSocket(PORT);while(true){try{Socket connection = serverSocket.accept();Thread task = new HttpSocket(connection);task.start();System.out.println("HTTP服务器正在运行,端口:" + PORT);}catch(Exception ex){}}}catch(Exception ex){System.exit(0);}}}class Request{private String uri;//资源路径private InputStream inputStream;//读取请求private String encoding = "GBK";//请求资源的编码Request(InputStream inputStream) {this.inputStream = inputStream;}public String getEncoding() {return encoding;}public String getUri() {return uri;}public void parse() throws IOException{System.out.println("客户端发送的信息:------------------>");//读取第一行,请求地址String line = readLine(inputStream, 0);//打印首部行System.out.print(line);//获取资源的路径uri = line.substring(line.indexOf('/'),line.lastIndexOf('/') - 5);//获取请求方法String method = new StringTokenizer(line).nextElement().toString();//如果是POST方法,则会有消息体长度int contentLength = 0;//读取包含元数据的HTTP首部并打印do{line = readLine(inputStream, 0);//如果有Content-Length消息头时输出if(line.startsWith("Content-Length")){contentLength = Integer.parseInt(line.split(":")[1].trim());}//打印元数据信息System.out.print(line);//如果遇到了一个单独的回车换行,则表示请求头结束}while(!line.equals("\r\n"));//如果是Post请求,则有消息体if("POST".equalsIgnoreCase(method)){//这里只是简单的处理表单提交的参数,而对于上传文件这里是不能这样处理的//因为上传的文件时消息体不止一行,会有多行消息体System.out.print(readLine(inputStream, contentLength));}//客户端发送消息结束System.out.println("客户端发送请求消息结束!!!!!!!!!!!!!!!");System.out.println("用户请求的资源是:" + uri);System.out.println("用户请求的方法类型是: " + method);}/*** * 这里模拟读取一行,因为如果使用API中的BufferedReader时,它是读取到一个回车换行后才返回,* 否则如果没有读取,则一直阻塞,这就导致如果POST请求时,表单中的元素以消息体传送,这时,消息体最末* 按标准是没有回车换行的,如果此时还使用BufferedReader来读取时,则POST提交时会阻塞。如果是POST* 提交时我们按照消息体的长度Context-Length来截取消息体,这样不会被阻塞。*/private String readLine(InputStream inputStream, int contentLength) throws IOException{ArrayList<Object> arrayList = new ArrayList<>();byte readByte = 0;int total = 0;if(contentLength != 0){//post请求while(total < contentLength){readByte = (byte)inputStream.read();arrayList.add(Byte.valueOf(readByte));total++;}}else{//get请求while(readByte != 10){readByte = (byte)inputStream.read();arrayList.add(Byte.valueOf(readByte));}}byte[] tempByteArr = new byte[arrayList.size()];for(int i = 0; i < arrayList.size(); i++){tempByteArr[i] = ((Byte)arrayList.get(i)).byteValue();}arrayList.clear();String tempStr = new String(tempByteArr,encoding);/*** HTTP请求中的header中有一个referer属性,这个属性的意思就是如果当前请求是从别的页面链接过来的,* 那个属性就是那个页面的url,如果请求的url是直接从浏览器地址栏输入的就没有这个值。得到这个值可以实现* 很多有用的功能,例如防盗链,记录访问来源以及记住刚才访问的链接等。另外,从浏览器发送这个Referer* 链接时好像固定用utf-8编码的,所以在GBK下出现乱码,这里纠正一下*/if(tempStr.startsWith("Referer")){//如果有Referer头时,使用UTF-8编码tempStr = new String(tempByteArr,"UTF-8");}return tempStr;}}class Response{private Request request;//用于读取资源的uriprivate OutputStream outputStream;//用于输出资源public void setOutputStream(OutputStream outputStream) {this.outputStream = outputStream;}public void setRequest(Request request) {this.request = request;}public void sentResponse(){PrintWriter out = new PrintWriter(outputStream);//返回一个状态行out.println("HTTP/1.0 200 OK"); //返回一个首部out.println("Content-Type:text/html;charset=" + request.getEncoding());  // 根据 HTTP 协议, 空行将结束头信息  out.println();// 输出请求资源out.println("<h1 style='color: green'> Hello Http Server</h1>");  out.println("你好, 这是一个 Java HTTP 服务器 demo 应用.<br>");  out.println("您请求的路径是: " + request.getUri() + "<br>");  out.close();  }}class HttpSocket extends Thread{private Socket connection;//客户端与服务器之间的连接public HttpSocket(Socket connection) {this.connection = connection;}@Overridepublic void run() {if(connection != null){System.out.println("线程:" + Thread.currentThread().getName());System.out.println("连接到服务器的用户:" + connection);try{Request request = new Request(connection.getInputStream());request.parse();Response response = new Response();response.setRequest(request);response.setOutputStream(connection.getOutputStream());response.sentResponse();//因为http1.0是无状态协议,所以必须关闭连接closeSocket(connection);  }catch(Exception ex){System.out.println("HTTP服务器错误:" + ex.getLocalizedMessage());}} }/** * 关闭客户端 socket 并打印一条调试信息. * @param socket 客户端 socket. */  void closeSocket(Socket socket) {  try {  socket.close();  } catch (IOException ex) {  ex.printStackTrace();  }  System.out.println(socket + "离开了HTTP服务器");  }  }


在浏览器输入请求:


浏览器返回结果显示:


请求和响应:


http://chatgpt.dhexx.cn/article/gOoZtbbs.shtml

相关文章

TCP/IP协议族通信的socket介绍及编程

一、概念介绍 TCP/IP&#xff08;Transmission Control Protocol/Internet Protocol&#xff09;即传输控制协议/网间协议&#xff0c;是一个工业标准的协议集&#xff0c;它是为广域网&#xff08;WANs&#xff09;设计的。 UDP&#xff08;User Data Protocol&#xff0c;用…

tensorflow之argmax函数独特讲解

在讲解之前&#xff0c;先来一个该函数的参数说明&#xff1a; argmax(a, axisNone, outNone) # a 表示array # axis 表示指定的轴&#xff0c;默认是None&#xff0c;表示把array平铺&#xff0c; # out 默认为None&#xff0c;如果指定&#xff0c;那么返回的结果会插入其中…

南京邮电大学离散数学实验三:编程实现整除关系这一偏序关系上所有盖住关系的求取,并判定对应偏序集是否为格

实验原理及内容 说明&#xff1a;这部分内容主要包括&#xff1a; 1、形式化描述实验中所使用的数据结构和存储结构&#xff0c;给出函数之间的调用关系和数据传递方式&#xff1b; 2、给出核心算法的C或Java等语言的源代码&#xff0c;并加上详细注释&#xff0c;分析算法的…

组合学笔记(六)局部有限偏序集的关联代数,Möbius反演公式

tags: Combinatorics 写在前面 前面铺垫了很多偏序集和格,分配格等的基本知识, 下面开始以这些代数结构为研究对象, 探寻其上的一些性质与关系, 我们先以关联代数的定义开始说起. 关联代数简介 定义 令 I n t ( P ) \mathrm{Int}(P) Int(P)表示 P P P上所有的区间的集合, …

离散中偏序集、乘积群、关系的性质和集合的相关证明

离散证明题&#xff08;三&#xff09; 9.令❄是定义在有限集合A上的一个二元运算&#xff0c;若对于∀a,b∈A&#xff0c;❄满足①aa❄a②a❄bb❄a③a❄(b❄c)(a❄b)❄c,在A上定义一个关系C比如a≤b<>aa❄b. 证明&#xff1a;(1)(A,≤)是一个偏序集 (2)∀a,b∈A,a∧ba❄…

偏序关系以及最大元,最小元,极大,极小元和上下界与上下确界

偏序关系的定义 偏序关系就是自反&#xff0c;反对称&#xff0c;传递的序偶集合。其中满足偏序的集合我们一般称为A&#xff0c;偏序关系一般称为R。 A*A产生的序偶有很多&#xff0c;所以我们使用<A,R>去表示满足某个偏序关系的A中元素的子集。 如果使用图像画这个偏序…

上偏续关系哈斯图_[离散]哈斯图偏序集--最好理解版本

离散数学哈斯图的画法 两个步骤&#xff1a;(1)排点的层数 (2)把有关系的点连接起来 看一道题&#xff1a;设A{1&#xff0c;2&#xff0c;3&#xff0c;4&#xff0c;6&#xff0c;8&#xff0c;9}&#xff0c;偏序集S{A,《},其中《为整除关系&#xff0c;画出S的哈斯图 首先把…

【集合论】序关系 ( 偏序集元素之间的关系 | 可比 | 严格小于 | 覆盖 | 哈斯图 )

文章目录 一、可比二、严格小于三、覆盖四、哈斯图 一、可比 可比 : A A A 集合 , 该集合上存在 偏序关系 ≼ \preccurlyeq ≼ 小于等于 , 偏序集 是 集合 和 偏序关系 组成的有序对 < A , ≼ > <A, \preccurlyeq> <A,≼> , x , y x, y x,y 是 A A A 集…

等价关系偏序关系全序关系

等价关系 , 并且 , 如果 R是自反&#xff0c;对称&#xff0c;传递的&#xff0c;称R为A上的等价关系。 偏序关系 &#xff0c;并且 , 如果 R是自反: (每个元素都和自身有关系)&#xff1b; 反对称&#xff1a;如果有&#xff0c;则,否则&#xff0c;不能同时存在 &#xf…

如何从哈斯图判断一个偏序集是不是格?

离散数学中&#xff0c;格的定义如下&#xff1a; 设 < S , ≼ > < S, \preccurlyeq> <S,≼>是偏序集&#xff0c;如果 ∀ x , y ∈ S , { x , y } \forall x,y\in S, \{x,y\} ∀x,y∈S,{x,y}都有最小上界和最大下界&#xff0c;则称 S S S关于偏序 ≼ \pre…

对接阿里云的短信接口发送手机验证码

最近做的项目涉及到对接阿里云的短信接口实现用户注册时发送验证码&#xff0c;我们在一个网站进行注册时要用到手机号获取验证码&#xff0c;这是很常见的操作。本篇博客记录如何对接阿里云的短信接口用手机号获取验证码&#xff0c;步骤如下&#xff1a; 1、对接阿里云短信接…

用Python模拟识别图片验证码并发送手机验证码

1、导语 大家好&#xff0c;好久不见。又到每日分享Python小技能的时候了。最近因为疫情影响&#xff0c;所以更新内容比较慢…今天周一&#xff0c;就来更新一波&#xff0c;心血来潮&#xff0c;是时候上线经营了。其实也没想到有啥好分享的&#xff0c;不如分享一些干货给大…

springboot发送短信验证码

学习目标&#xff1a; 阿里云短信服务 准备工作&#xff1a; 注册阿里云账户开通短信服务申请签名和模板拿到AccessKey 大概说一下测试和申请流程&#xff0c;输入https://www.aliyun.com/进入官网&#xff0c;然后注册账号&#xff0c;直接在搜索框输入短信服务就能找到&…

SpringBoot 通过阿里云的短信功能 实现发送手机验证码

我们在项目中经常遇到 需要通过手机号发送验证码实现登录注册等功能。 这里讲一下&#xff0c;Springboot项目中如果通过阿里云的短信功能&#xff0c; 实现发送手机验证码并验证 一、准备工作 1、购买阿里云的短信服务 https://free.aliyun.com/product/cloudcommunication-…

使用node实现向手机发送验证码

步骤一&#xff1a;安装node.js Node官方网站https://nodejs.org/en/ 下载node.js 安装好之后输入 node -v 能够显示版本号表示安装成功了 步骤二&#xff1a;注册并登陆聚合科技&#xff0c;申请短信API服务 我们来到聚合科技的官网中https://www.juhe.cn/ &#xff0c;注册…

项目接入腾讯云短信服务SMS实现向用户发送手机验证码

1、自述 早在18年的时候&#xff0c;我就在项目中使用过阿里云的短信服务&#xff0c;现在我上阿里云短信控制台看&#xff0c;还能看到当时创建的短信签名&#xff0c;如下图所示。 出于某种原因&#xff0c;我现在想重新申请一个新的签名&#xff0c;却审批失败了&#xf…

SpringBoot+vue 使用阿里云的短信功能发送手机验证码

前言&#xff1a; 小编后端用的是Springboot 前端用的是vue ,小编主要是写后台&#xff0c;前端页面比较简陋&#xff0c;后期还要调优&#xff0c;写的不对处还望多多包涵。 环境&#xff1a; 需要先准备好阿里云的账号和一些必要的参数。详情见我的另一篇博客。 https://blo…

腾讯云短信服务实现 Java 发送手机验证码(SpringBoot+Redis 实现)

文章目录 腾讯云短信服务实现 Java 发送手机验证码&#xff08;SpringBootRedis 实现&#xff09;1、打开腾讯云短信服务2、创建短信签名3、创建短信正文模板4、等待全部审核完毕即可5、发送短信6、短信业务实战&#xff08;SpringBootRedis&#xff09;&#xff08;1&#xff…

Spring boot 发送手机验证码

由于阿里云现在的短信签名无法通过申请&#xff0c;所以我这里选择了中国网建SMS短信平台&#xff08;手机号注册即用&#xff0c;有免费赠送的几条短信测试&#xff09; demo代码地址&#xff1a;https://github.com/mer97/springboot-sendmessage Spring boot 实现发送手机验…