我们已经实现过应用层协议,但也要看一看成熟的应用层协议
目录
- 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
接下来我们自己编写一个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构建需要的请求
持续更新~~~~~~~~~~~~~~~~~~~~~~~