我们已经实现过应用层协议,但也要看一看成熟的应用层协议
目录
- 1 HTTP协议
- 1·1 URL
- 1·2 urlencode 和 urldecode
- 1·3 HTTP 协议请求与响应格式
- 请求格式
- 响应格式
- 1·4 界面的基本处理
- 显示基本主页
- 显示图片
- 页面跳转
- 1·5 常见header
- 1·6 状态码
- 1·6·1 404举例
- 1·6·2 关于3开头的状态码
- 1·7 请求方法
- 1·7·1 PostMan
- 1·7·2 form表单
- 1·8 cookie
接下来我们自己编写一个tcp服务端并将会以此代码为蓝本进行演示。
具体的代码请看: HTTP极简
1 HTTP协议
虽然说应用层协议是程序员自己定的,就像我们已经写过的tcp、udp、网络版本计算器,都是自己定的协议。
但是应用层也有大佬们已经定制好的协议:HTTP(超文本传输协议)就是其中之一。
在互联网世界中,HTTP(HyperText Transfer Protocol,超文本传输协议)是一个至关重要的协议。它定义了客户端(如浏览器)与服务器之间如何通信,以交换或传输超文本(如 HTML 文档)。
HTTP 协议是客户端与服务器之间通信的基础。客户端通过 HTTP 协议向服务器发送请求,服务器收到请求后处理并返回响应。HTTP 协议是一个无连接、无状态的协议,即每次请求都需要建立新的连接,且服务器不会保存客户端的状态信息。
这里理解一下无连接无状态:
- HTTP协议被设计为无连接的,这意味着HTTP客户端(通常是浏览器)和HTTP服务器之间的每一次请求和响应都是独立的。即使HTTP通常运行在TCP/IP协议之上,而TCP是一个面向连接的协议,HTTP本身并不维护任何连接状态。
- HTTP协议被设计为无状态的,意味着服务器不会保存有关客户端的任何上下文信息。每个请求都是独立的,并且包含了所有必要的信息来完成该请求。服务器不会自动保留任何关于先前请求的信息。
1·1 URL
平时我们俗称的 “网址” 其实就是说的 URL。
我们先随便点找一个网页来看看:
https://news.cctv.com/2024/08/23/ARTIENSZojGK092VY8UxceVT240823.shtml
https
代表协议头,我们暂时认为http = https,在十几年前http还是很多的,不过https更安全,多了一层安全层,因此现在都是用https了。
://
代表分割符,是网址要求的特殊字符
news.cctv.com
是域名,但是可以通过DNS协议转为ip。
注意:
但是我们知道 ip+ port 才能准确的定位到服务进程,
可是我们发现网址中并没有体现出端口号的信息。
原因在于https是知名端口,他对应80端口。
就像我们平时说打110,和打报警电话是一样的,我们一说报警就能想到110,一说到110就能想到报警。
所以事实是发起请求时任然会使用ip + port,只是没有在网址中体现,但会发送请求时会自动拼接。
com后的部分
首先我们的http是超文本协议,超文本也就是超越文本,是网页、图片、视频、音频…这些资源化都在服务器上,我们没访问服务器时资源也是在服务器的。
而我们对于服务器一般都是进行读或写操作。
再者服务器一般都是linux系统的,linux下一切皆文件–>资源也是文件—>
因此对资源进行读或写就需要路径–>因此com后边的这部分理所当然的是路径。
而恰好路径就对应着资源。
那么/2024/08/23/ARTIENSZojGK092VY8UxceVT240823.shtml
中的第一个/
岂不是就意味着是Linux的根目录?其实不然,这个叫做web根目录,可以是任意一个linux下的目录。
因此:域名(ip标识唯一一台主机) + 路径 = 互联网中的唯一一份资源。
因此URL也叫做统一资源定位符。
我们再来看一下完整版的URL
user:pass就是登录信息,我们不管。
可以看到我们在域名后确实看到了端口号。
那么一份资源路径后的?
是什么?
我们打开百度试验一下
https://www.baidu.com/s?wd=hello&rsv_spt=1&rsv_iqid=0xd8f11d5d0005ed5b&issp=1&f=8&rsv_bp=1&rsv_idx=2&ie=utf-8&tn=baiduhome_pg&rsv_dl=tb&rsv_enter=1&rsv_sug3=10&rsv_sug1=10&rsv_sug7=101&rsv_sug2=0&rsv_btype=i&inputT=2757&rsv_sug4=13469
可以看到,这就是代表你要搜索的资源的参数
。
最后的#ch的意思下图可以很好的反应,就是图片的那个啥、
1·2 urlencode 和 urldecode
像 / ? : 等这样的字符, 已经被 url 当做特殊意义理解了.
因此这些字符不能随意出现.
比如,某个参数中需要带有这些特殊字符, 就必须先对特殊字符进行转义.否则则会干扰正常格式。
转义的规则如下:将需要转码的字符转为 16 进制,然后从右到左,取 4 位(不足 4 位直接处理),每 2 位做一位,前面加上%,编码成%XY 格式
例如:
“+” 被转义成了 “%2B”
urldecode 就是 urlencode 的逆过程;
1·3 HTTP 协议请求与响应格式
请求格式
注意:我们在自己编写时的是将报头与正文部分分开进行的,而http请求是全部放入一个结构体中统一处理的,我们在自己编写应用层时也可以选择适合的协议方法来处理。
我们看一看利用浏览器得到的最原始请求是什么样子
是一行一行的,也不出我们所料,因为每一行都有/r/b,所以自然而然打印出来就是一行行的,方便我们查看。
响应格式
我们可以通过telnet命令,或费德勒等软件进行观察返回的响应
发现body就是html代码
1·4 界面的基本处理
几乎都是修改一下前端代码即可
注意:由于响应添加了正文部分,所以content-length报头一定要添加,否则就很难分清每一个响应。
显示基本主页
我们先构建一下最基本的主页
index.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>我的网页</title>
</head>
<body>
<h1>欢迎来到我的网站</h1>
<p>这是我的网页,我是内容!</p>
</body>
<a href="register.html">注册</a>
// 这个地方也可以写成"/register.html",多一个带/的,若是不带,默认会带上/,而这个/就是web根目录
<div>
<img src="Photos/Screenshot_2024-08-21_210803.png" alt="Big Boat">
</div>
</html>
利用浏览器访问进行查看现象
显示图片
我们在index.html中添加即可
现象:果然出现了图片,但是有一个问题,我们明明申请时只请求了首页,并没有请求图片,为什么仍然会显示图片?
原因:
虽然我们只访问了主页,但是浏览器却仍然发起两次请求,我将请求行进行了截取
同样,我们也会发现还有另一个请求,favicon?这其实就是我们网页的小图标,因为我们没搞,所以他就生成了默认的丑丑图标。
页面跳转
我们依旧更改一下html即可,也要添加一下你要舔转的html
index.html
register.html
现象:
1·5 常见header
Content-Type: 数据类型(text/html 等)
由于现在的浏览器都做的太牛了,虽然传过去的都是二进制,但二进制有可能是音频,视频等格式,就算没有加Content-Type报头也有可能直接解析,但是我们仍然需要加,保证鲁棒性!
至于如何填参对应就需要依靠一下type对照表了
Content-Length: Body 的长度
这个没什么好说的,只要有正文,不论是请求还是响应报文都需要带
Host: 客户端告知服务器, 所请求的资源是在哪个主机的哪个端口上;
就像浏览器发送请求,fiddler进行截取了,因为截取了所以还要重新发送,所以就必须包含HOST报头,否则fiddler就无法知道目的地进而发送了。
User-Agent: 声明用户的操作系统和浏览器版本信息;
我们发现每次进行下载时,都会提示出你要下载的正确版本,就像你是安卓手机,你搜索微信,那么跳出来的就是安卓版本。这就是由于User-Agent存在的原因,他会将你的浏览器地址,设备版本等信息作为报头发送,
referer: 当前页面是从哪个页面跳转过来的;
我们进行网页跳转测试时就会发现请求报头中有这个东西,反而第一次进入初始页面是没有出现。
那么他的意义是什么?
比如:进行统计喜欢从那个页面到哪个页面;
如果跳转网页时可以进制从一个跳到另一个…
Location 与 Cookie会在稍后进行解释
前者在状态码3时会讲到,cookie会在讲到方法后会讲到。
connection
首先我们当前服务器就是短连接,在以前网页没有那么花哨时,实用的就是短连接,每次请求都需要重新accept与connect,所以当一个网页中需要请求的资源很多时就造成了浪费。
那如何改成长连接?
我们在网络版本计算其中已经进行设计过了!
HTTP 中的 Connection 字段是 HTTP 报文头的一部分,它主要用于控制和管理客户端与服务器之间的连接状态
核心作用
• 管理持久连接:Connection 字段还用于管理持久连接(也称为长连接)。持久
连接允许客户端和服务器在请求/响应完成后不立即关闭 TCP 连接,以便在同一个连接上发送多个请求和接收多个响应。
持久连接(长连接)
• HTTP/1.1:在 HTTP/1.1 协议中,默认使用持久连接。当客户端和服务器都不明
确指定关闭连接时,连接将保持打开状态,以便后续的请求和响应可以复用同一个连接。
• HTTP/1.0:在 HTTP/1.0 协议中,默认连接是非持久的。如果希望在 HTTP/1.0
上实现持久连接,需要在请求头中显式设置 Connection: keep-alive。
语法格式
• Connection: keep-alive:表示希望保持连接以复用 TCP 连接。
• Connection: close:表示请求/响应完成后,应该关闭 TCP 连接。
keep-alive代表这一种协商,表示客户端支持长连接,如果server也支持,那么就使用长连接。
1·6 状态码
状态码 | 含义 | 应用样例 |
---|---|---|
100 | Continue | 上传大文件时,服务器告诉客户端可以继续上传 |
200 | OK | 访问网站首页,服务器返回网页内容 |
201 | Created | 发布新文章,服务器返回文章创建成功的信息 |
204 | No Content | 删除文章后,服务器返回“无内容”表示操作成功 |
301 | Moved Permanently | 网站换域名后,自动跳转到新域名;搜索引擎更新网站链接时使用 |
302 | Found 或 See Other | 用户登录成功后,重定向到用户首页 |
304 | Not Modified | 浏览器缓存机制,对未修改的资源返回 |
400 | Bad Request | 填写表单时,格式不正确导致提交失败 |
401 | Unauthorized | 访问需要登录的页面时,未登录或认证失败 |
403 | Forbidden | 尝试访问你没有权限查看的页面 |
404 | Not Found | 访问不存在的网页链接 |
500 | Internal Server Error | 服务器崩溃或数据库错误导致页面无法加载 |
502 | Bad Gateway | 使用代理服务器时,代理服务器无法从上游服务器获取有效响应 |
503 | Service Unavailable | 服务器维护或过载,暂时无法处理请求 |
1·6·1 404举例
这里就拿最常见的404状态码进行举例,相信各位小伙伴们也都遇到过。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>404 - 页面未找到</title>
<style>
body {
font-family: 'Arial', sans-serif;
background-color: #100e0e;
color: #333;
margin: 0;
padding: 0;
}
.container {
max-width: 800px;
margin: 0 auto;
text-align: center;
padding: 50px 0;
}
h1 {
font-size: 36px;
color: #333;
margin-bottom: 20px;
}
p {
font-size: 18px;
line-height: 1.6;
margin-bottom: 20px;
}
.return-home {
display: inline-block;
background-color: #007BFF;
color: #c50e0e;
text-decoration: none;
padding: 10px 20px;
border-radius: 5px;
transition: background-color 0.3s ease;
}
.return-home:hover {
background-color: #0056b3;
}
</style>
</head>
<body>
<div class="container">
<h1>404 - 页面未找到</h1>
<p>很抱歉,您访问的页面不存在。</p>
</div>
</body>
</html>
在进行取出指定路径内容时如果没有该文件那就构建404的响应即可。
1·6·2 关于3开头的状态码
我们在来详细的看一下3××状态码
状态码 | 含义 | 是否为临时重定向 | 应用样例 |
---|---|---|---|
301 | Moved Permanently | 否(永久重定向) | 网站换域名后,自动跳转到新域名;搜索引擎更新网站链接时使用 |
302 | Found 或 See Other | 是(临时重定向) | 用户登录成功后,重定向到用户首页 |
307 | Temporary Redirect | 是(临时重定向) | 临时重定向资源到新的位置(较少使用) |
308 | Permanent Redirect | 否(永久重定向) | 永久重定向资源到新的位置(较少使用) |
首先location与3开头的状态码就是相互配合的使用的。
然后重定向分为临时与永久,我们分别理解一下。
我们先来看看重定向是什么意思
我们在来两个例子解释临时与永久的区别
临时:
学校的学生想去吃饺子,但是饺子换由于修路临时搬到到西门,因此要贴一个告示,然后学生转而就去西门去了,时隔两月这个学生又想吃饺子,但这个学生还是会去老店,因为是临时的搬走。
永久:若是永久的搬走,那么时隔两个月后学生就会直接去西门了。
他们两的区别就在这临时的不会影响未来的你,永久的会影响。
那我们映射到应用场景中
临时:就像我们看vip视频超过时限时会自动跳转到付费界面。
永久:假设我们更换了域名
我们这里在更改代码时以提供重定向到新地址时就要意识到,这其实本质就是一个服务了,而不是一个简单的html界面。
只需添加如下的一个报头即可。
注意:Location报头中包含的url必须是一个完整的!
1·7 请求方法
给大家一个量化的概念,虽然请求方法很多,但是GET与POST可以占据99,而GET又占据这99中的70。
所以我们只介绍GET与POST
我们总归还是要构建一下对应的场景
注意:关于GET上图写的并不完整,他也同样可以传输实体主体。
传输实体主体包括:登录,注册…
注意:
浏览器默认是通过GET方法进行访问的,那么如何修改?
1·7·1 PostMan
我们可以利用postman构建需要的请求
我们先试用GET进行测试
对于端口号后边的service我们先暂时将他理解为一个服务,参数就是传给这个服务的。
发现url中就会带有参数
而为POST时就不会再url中带有参数,反而在body中带有参数。
1·7·2 form表单
这里我们就要与前端联系一下了,我们先大概的看一下form表单
我们先暂时不管/login
,这个东西本质就是一个服务。
method不写的话默认就是GET。随后生成的窗口就是这个样子。
其实我们熟知的二维码本质也是。
我们输入观察,跳转到了自定义404页面,因为我们确实没有实现这个页面,到也很正常。
我们依据form表单与图框进行分析一下参数含义
input type:输入类型(test就正常显示,password就加密显示…)
name:传递参数时的名字(kv格式)
value:默认值,可以看到前端代码我们写了一个.
,默认也就出现了一个点。
我们接下来观察一下输入参数后请求的报文
发现果然与我们使用postman进行测试时的结论一样。
此时我们可以下4个结论了:
- GET一般获取静态资源,也可以通过url向服务器传递参数
- POST使用正文部分进行传递参数
- url进行传参时体量一定不大,但使用正文可以很大。
- 由于我们使用GET传参时,参数会暴露。因此没有POST安全()相对来说,但其实POST也并不安全,所以我们还有HTTPS协议多了一层保护层。
此时我们要研究一下action的含义!
先说结论:本质就是一种服务
我们可以进行与action的匹配,不同action匹配不同的服务,将参数传递给对应的服务,再由服务构建出对应的html进行返回。
1·8 cookie
我们知道HTTP是无连接无状态
也就是每次都重新发送请求,并不记录数据。
举例:
我们未登录app观看电影时,只能试看5min;我们登录时,就可以观看免费电影了。
此时我们找了一部电影,看了一会又换了一部,仍旧可以看完,可是我们HTTP是无状态啊,为什么可以记录我们的登录数据呢?
答案就在于cookie,用于在客户端存储少量信息. 通常用于实现会话(session)的功能;
我们在报头中添加即可:
完~