一.前言

大家好!最近国庆节期间闲来无事,然后就想着心心念念的web服务器,当时大一的时候学习javaweb,对Tomcat感到很神奇,后面的springboot等,其实也是内嵌了Tomcat,我们只需要写我们的业务逻辑,运行就直接交给服务器就行了,后面我在想是否可以实现了一个简易的http的web服务器,于是我花了五天的时间来尝试写一下,下面我会逐步介绍思路。

二.HTTP协议的认识

 要想写我们的服务器,我们需要先对http协议认识清楚,

http协议分为请求报文和响应报文两部分,每一部分由三部分组成,请求(响应)行,请求(响应)头,请求(响应)体,我们先来对请求报文解析一下:

1.http请求报文

1.GET请求

GET /user?id=1 HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: Pycharm-88cc7be9=560f40e1-3f78-4f3e-98c2-4f7d525be9bc; Idea-6d63c5b6=78bea6ca-d22a-4bbf-920a-9cf28e438b9d

这是get请求,第一行是请求行,分别表示:请求方法,请求路径,http协议版本

请求路径中?后面的是携带的参数: id=1

Host: 表示从哪里发来的请求,请求原地址

Connection: 表示连接状态,Keep-alive表示连接活跃

User-Agent: 表示用户代理,其实就是给一个说明是浏览器发来的,在爬虫中,一般都需要加上这个头,来模仿浏览器发的请求

Accept:表示可接受的响应内容类型

Accept-Encoding: 表示可接受的数据压缩格式

Accept-Language: 表示浏览器可以接受的语言

Cookie: 也就是我们常说的那个cookie

下面,我们看一下post请求:

2.POST请求

我选择了两种提交方式,第一种是常见的form表单提交格式,第二种是以json格式提交的

Java 路书解析 java解析http_http

POST /user HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Content-Length: 42
Pragma: no-cache
Cache-Control: no-cache
Accept: application/json, text/plain, */*
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36
Content-Type: application/json;charset=UTF-8
Origin: null
Sec-Fetch-Site: cross-site
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9

{user: "DasfasdfaSDF", pass: "123123123"}

第一行和上面一样

Content-Length: 表示请求体的长度

Content-Type: 表示请求体的内容格式,我这里是通过axios发送的,所以是application/json

最后一行,和请求头之间有一个空行,这就是携带的请求体,是以json格式提交的

然后,我们用form表单的格式提交一下:

POST /user HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Content-Length: 40
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
Origin: null
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: cross-site
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9

username=张三&password=123

我们可以看到Content-Type变成了 application/x-www-form-urlecoded

请求体变成了和get请求参数一样的格式

3.OPTIONS请求

注意:我们在发送post请求时,如果是以json格式发送的,它会发送俩个请求,第一个先发送options请求,第二次才发送了post请求,

options请求是一个预请求,它会先访问服务器一些信息,这里我们来说明什么情况下会发送options请求:

1.当出现跨域请求时,关于跨域,可以自行搜索

2.Content-Type 的值仅限于下列三者之一,即application/x-www-form-urlencoded、multipart/form-data、text/plain

 如果你没有对options请求做处理,比如允许跨域,那么你的下一次post请求将发送失败

我们可以在响应头中加入下面信息来运行跨域:

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST,GET,OPTIONS,DELETE
Access-Control-Allow-Headers: access-control-allow-origin, authority, content-type, version-info, X-Requested-With
Access-Control-Allow-Age: 3600

第一个表示允许跨域的IP,*默认为所有IP

第二个是允许跨域的请求方式

第三个是允许跨域的请求头

第四个是相当于给跨域检查设置一个缓存时间,在这个时间内,都算通过,

2.http响应报文

(1)示例

下面我们来解析一下http响应报文:

HTTP/1.1 200 OK
Server:JhWebServer
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods:POST,GET,OPTIONS,DELETE
Date:2023-10-04 14
Content-Type: application/json

{"password":"123","username":"jjh123"}

响应行:第一个是HTTP版本,第二个是状态码,第三个是状态码对应的描述

响应头:

Server: 表示提供服务的服务器是哪个,JhWebServer是我自己写的服务器

Date: 表示的响应的时间

Content-Type: 表示响应体的格式

响应体:

 json格式的响应内容

(2)响应状态码

 1XX:100-199 信息响应
2XX:200-299 成功响应
3XX:300-399 重定向
4XX:400-499客户端错误
5XX:500-505 服务器端错误

  • 100(继续):请求者应当继续提出请求。服务器已收到请求的第一部分,正在等待剩余部分
  • 101(切换协议):请求者要求服务器切换协议,服务器也已确认切换协议

 200(成功):服务器已成功处理请求。一般这表示服务器正常处理了请求,并且正常返回了相应的页面
201(已创建):请求成功并且服务器成功创建新资源
202(已接受):服务器已接收请求,但仍未处理
203(非授权信息):服务器成功处理请求,但是返回的信息可能来自另外一来源
204(无内容):服务器成功处理请求,但是没有返回任何内容
205(重置内容):服务器成功处理请求,但没有返回任何内容
206(部分内容):服务器成功处理了部分GET请求
 

300(多钟选择):针对请求,服务器可以执行多种操作。服务器可以根据请求者的(user-agent)选择一项操作,或者提供操作列表供请求者选择
301(永久移动):请求的网页已永久移动到新的位置。服务器返回该状态码时,会自动将请求者转到新位置
302(临时移动):服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置进行后续的请求
303(查看其它位置):请求者应当对不同的位置使用单独的GET请求来检索响应时,服务器返回此状态码
304(未修改):自从上次请求后,请求的网页未修改过。服务器返回此状态码时,不会返回网页内容
305(使用代理):请求者只能使用代理访问请求的网页
307(临时重定向):服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行后续请求

400(错误请求):表示客户端请求的语法错误,服务器无法理解。例如:url中含有非法字符,json格式出现问题…
401(未授权):请求要求身份验证。一般需要登录的网站,服务器可能会返回此状态码
402:保留
403(禁止):服务器理解请求客户端的请求,拒绝请求
404(未找到):服务器无法根据客户端请求找到资源
405(方法禁用):禁用请求中指定的方法
406(不接受):无法使用请求的内容特性响应请求的网页
407(需要代理授权):此状态码与401类似,但指定请求者应当授权使用代理
408(请求超时):服务器等候请求时超时
409(冲突):服务器在完成请求是发生冲突。服务器必须在响应中包含有关冲突的信息
410(已删除):请求的资源已永久删除
411(需要有效长度):服务器不接受不含有效内容长度标头字段的请求
412(未满足前提条件):服务器未满足请求者在请求中设置的其中一个前提条件
413(请求实体过大):相应实体过大。服务器拒绝处理当前请求,请求超过服务器所能处理和允许处理的最大值
414(请求的url过长):请求的url过长,服务器无法处理
415(不支持的媒体类型):请求的格式不受请求页面的支持
416(请求范围不符合要求):如果页面无法提供请求的范围,服务器则会返回此状态码
417(未满足期望值):在请求头Expect指定的预期内容无法被服务器满足
422(不可处理的实体):请求格式正确,但由于含有语义错误,无法响应
 

500(服务器内部错误):服务器遇到一个未预料到的状况,导致无法完成对请求的处理
501(尚未实施):服务器不具备完成请求的功能。例如:服务器无法识别请求方法…
502(错误网关):服务器作为网关或者代理,从上游服务器收到无效响应
503(服务不可用):服务器目前无法使用。例如:超载、停机维护…
504(网关超时):服务器作为网关或代理,但未及时收到上游服务器的响应
505(HTTP版本不受支持):服务器不支持请求中所用的HTTP版本

(3)Content-Type

这里要格外说一下这个,大致分为三类,

1,文本内容:

 text/html:网页

text/css: css样式

text/plain: 文本内容

application/javascript: js文件

application/x-www-form-urlencoded: form表单提交的内容

2:文件类型

image/jpeg: .jpg ,jpeg图片格式

image/gif: gif动态图

image/webp:   .webp图片格式

multipart/form-data: form表单提交的文件

3.json文本

application/json: json文本

总结:

好了,我们先介绍到这里,后面我们来正式进行我们的服务器开发!