😀大家好,我是白晨,一个不是很能熬夜😫,但是也想日更的人✈。如果喜欢这篇文章,点个赞👍,关注一下👀白晨吧!你的支持就是我最大的动力!💪💪💪
📙前言
哟,大家好,我是白晨。距离上一次更新已经过了一段时间了,属实是当鸽子当惯了🤣。
上一篇文章我们全面讲解了网络中的基础知识(没看过上篇文章的的可以先去看上一篇文章【网络】网络基础知识详解),这篇文章白晨准备从应用层中的一个及其重要的协议开始讲起,它就是——HTTP协议
。HTTP协议由于简单、快速而成为了应用最广泛的web文档传递协议协议,在理解了HTTP协议的种种行为后,再自顶向下去学习传输层,网络层以及数据链路层将会好理解许多。
📗HTTP协议
🏡1. HTTP背景介绍
1989 年 3 月,HTTP 诞生。最初设想的基本理念是: 借助多文档之间相互关联形成的超文本(HyperText),连成可相互参阅的 WWW( World Wide Web,万维网)。现在已提出了 3 项 WWW 构建技术,分别是:
- 把 SGML(StandardGeneralized Markup Language,标准通用标记语言)作为页面的文本标记语言的 HTML(HyperText Markup Language,超文本标记语言);
- 作为文档传递协议的 HTTP ;
- 指定文档所在地址的 URL(Uniform Resource Locator,统一资源定位符)。
WWW 这一名称, 是 Web 浏览器当年用来浏览超文本的客户端应用程序时的名称。现在则用来表示这一系列的集合,也可简称为 Web。
⛪2. HTTP知识预备
2.1 TCP/IP协议
上篇文章我们曾经讲过,TCP/IP模型将网络分为了四层,将不同的任务划分,每层只要完成自己的任务即可,完成了每层间的解耦,同时上层依赖下层的服务以实现自己的功能。HTTP是一个建立在TCP/IP协议族上的应用层协议,所以,要利用HTTP发送数据,就要通过上图过程。
我们用 HTTP 举例来说明,
- 首先作为发送端的客户端在应用层(HTTP 协议)发出一个想看某个 Web 页面的 HTTP 请求。
- 接着,为了传输方便,在传输层(TCP 协议)把从应用层处收到的数据(HTTP 请求报文)进行分割,并在各个报文上打上标记序号及端口号后转发给网络层。
- 在网络层(IP 协议),增加作为通信目的地的 MAC 地址后转发给链路层。这样一来,发往网络的通信请求就准备齐全了。
- 接收端的服务器在链路层接收到数据, 按序往上层发送,一直到应用层。当传输到应用层,才能算真正接收到由客户端发送过来的 HTTP。
2.2 URI 和 URL
URL (Uniform Resource Locator)
,我们称为统一资源定位符
。它是对可以从互联网上得到的资源的位置和访问方法的一种简洁的表示,是互联网上标准资源的地址。也就是我们经常说的网址。
- 具体格式为:
URI (Uniform Resource Identifier)
,我们称为统一资源标识符
。URI 就是由某个协议方案表示的资源的定位标识符。
- URI 与 URL 的联系:
URI 用字符串标识某一互联网资源, 而 URL 表示资源的地点(互联网上所处的位置),可见 URL 是 URI 的子集。URI 可以分为 URL + URN (统一资源名称) ,打个比方,URI 相当于一个人,URL 就是这个人的住址,URN 就是这个人的名字。
所以,URL 必定也是 URI。
- URI 与 URL 的区别:
URI 不一定是 URL,根据上文,URI 中还有一个 URN ,它只是命名资源,而不定位资源。
2.3 DNS服务
DNS( Domain Name System)**DNS 协议提供通过域名查找 IP 地址,或逆向从 IP 地址反查域名的服务 **,和 HTTP 协议一样位于应用层的协议。
相比IP地址这种纯数字,域名这种字母+数字的组合让人更加容易记忆,比如:百度的IP地址为
110.242.68.3
,域名为www.baicdu.com
。可见,大多数用户在访问网站时,都习惯使用域名。但我们知道在网络上,IP地址+端口号才能定位一个唯一的服务,所以就需要 DNS服务 将域名解析为 IP地址。
具体工作场景如下图:
🕋3. HTTP协议格式
请求报文
POST /data/index HTTP/1.1
Host: baichen.com
Connection: keep-alive
Content-Type: text/html; charset=utf8
Content-Length: 16
name=chen&age=37
请求报文的构成为:
由此,我们可以得到请求报文的格式为:
-
请求行:
方法
+URL
+协议版本
-
请求头部: 请求的属性, 冒号分割的键值对;每组属性之间使用
\n
分隔。 -
空行:空行用于标识请求头部结束,请求正文开始。
-
请求正文: 空行后面的内容都是请求正文,内容为应发送的数据。请求正文允许为空字符串。如果请求正文存在, 则在请求头部中会有一个Content-Length属性来标识请求正文的长度。
这里要解释一个问题,为什么前文讲解URL的结构这么复杂,但是在上面的请求报文中却好像只有目录结构,而没有协议、域名等部分?
当客户端请求访问资源而发送请求时, URL 需要将作为请求报文中的请求 URL 包含在内。指定请求 URI 的方式有很多。
- 指定完整的URL
GET http://www.baichen.com/index.html HTTP/1.1
- 在首部字段Host中写明网络域名或IP地址
GET / HTTP/1.1 Host: www.baichen.com HTTP请求的'/'并不是根目录,而是web根目录,意味着我们要请求该网站的首页
- 对于针对服务器本身的访问,可以用* 来代替请求 URL
OPTIONS * HTTP/1.1 -- 查询服务器支持的方法
响应报文
HTTP/1.1 200 OK
Date: Tue, 10 Jul 2012 06:50:15 GMT
Content-Length: 362
Content-Type: text/html
<html>
…
响应报文的构成为:
由此,我们可以得到响应报文的格式为:
- 相应行:
协议版本
+状态码
+状态码描述
- 响应头部: 请求的属性, 冒号分割的键值对;每组属性之间使用
\n
分隔。 - 空行:空行用于标识响应头部结束,响应正文开始。
- 响应正文: 空行后面的内容都是响应正文,内容为应发送的数据。响应正文允许为空字符串。如果响应正文存在,则在响应头部中会有一个Content-Length属性来标识响应正文的长度;如果服务器返回了一个html页面,那么html页面内容就是在响应正文中。
🕌4. HTTP的方法
向请求 URL 指定的资源发送请求报文时,采用称为方法的命令。方法的作用在于, 可以指定请求的资源按期望产生某种行为。方法中有
GET
、POST
和HEAD
等。eg.
- HTTP支持的方法详见下表:
方法 | 说明 | 支持的HTTP协议版本 |
---|---|---|
GET | 获取资源 | 1.0、1.1 |
POST | 传输实体主体 | 1.0、1.1 |
PUT | 传输文件 | 1.0、1.1 |
HEAD | 获得报文首部 | 1.0、1.1 |
DELETE | 删除文件 | 1.0、1.1 |
OPTIONS | 询问支持方法 | 1.1 |
TRACE | 追踪路径 | 1.1 |
CONNECT | 要求用隧道协议连接代理 | 1.1 |
LINK | 建立和资源之间的联系 | 1.0 |
UNLINE | 断开连接关系 | 1.0 |
LINK 和 UNLINK 方法已经被 HTTP/1.1 弃用,这两个方法只需了解即可。
GET:获取资源
GET 方法用来请求访问已被 URL 识别的资源。指定的资源经服务器端解析后返回响应内容。
请求报文:
GET / HTTP/1.1
Host: www.baichen.com
Connection: keep-alive
User-Agent: XXXXX
响应报文:
HTTP/1.1 200 OK
Content-Type: text/plain
hello, i'm from the futurn
上面就是一个经典的使用GET方法的例子,GET方法是一个使用非常频繁的方法,用于获取指定 URL 的资源,当我们访问一个网站的时候,第一次使用的方法基本都是GET,以获取网页信息。
POST:传输实体主体
POST 方法用来传输实体的主体。虽然用 GET 方法也可以传输实体的主体,但一般不用 GET 方法进行传输,而是用 POST 方法。虽说 POST 的功能与 GET 很相似,但POST 的主要目的并不是获取响应的主体内容。
注:使用GET方法传输的内容直接会在URL上显示,而用POST方法传输的内容会在报文正文传输,而不会在URL上直接显示。
POST / HTTP/1.1
Host: www.baichen.com
Content-Length: 1560(1560字节的数据)
PUT:传输文件
PUT 方法用来传输文件。就像 FTP 协议的文件上传一样,要求在请求报文的主体中包含文件内容,然后保存到请求 URL 指定的位置。 这个方法由于安全性较低,所以大部分主流网站不会支持这个方法或者配合验证机制使用此方法。
PUT / HTTP/1.1
Host: www.baichen.com
Content-Type: text/html
Content-Length: 1560(1560字节的数据)
HEAD:获得报文首部
HEAD 方法和 GET 方法一样,只是不返回报文主体部分,用于确认 URL 的有效性及资源更新的日期时间等。
HEAD /index.html HTTP/1.1
Host: www.baichen.com
DELETE:删除文件
DELETE 方法按请求 URL 删除指定的资源。DELETE 方法和 PUT 效果正好相反,所以 DELETE 也具有安全性问题,一般不会单独使用。
DELETE /index.html HTTP/1.1
Host: www.baichen.com
OPTIONS:询问支持的方法
OPTIONS 方法用来查询针对请求 URL 指定的资源支持的方法。
请求:
OPTIONS * HTTP/1.1
Host: www.baichen.com
返回:
HTTP/1.1 200 OK
Allow: GET, POST, HEAD, OPTIONS
(返回服务器支持的方法)
TRACE:追踪路径
TRACE 方法是让 Web 服务器端将之前的请求通信环回给客户端的方法 .客户端通过 TRACE 方法可以查询发送出去的请求是怎样被加工修改 / 篡改的。这是因为,请求想要连接到源目标服务器可能会通过代理中转, TRACE 方法就是用来确认连接过程中发生的一系列操作。
发送请求时, 在
Max-Forwards
首部字段中填入数值,每经过一个服务器端就将该数字减 1, 当数值刚好减到 0 时,就停止继续传输,最后接收到请求的服务器端则返回状态码 200 OK 的响应
请求:
TRACE / HTTP/1.1
Host: WWW.baichen.com
Max-Forwards: 2
返回:
HTTP/1.1 200 OK
Content-Type: message/http
Content-Length: 1024
TRACE / HTTP/1.1
Host: www.baichen.com
Max-Forwards: 2(返回响应包含请求内容)
CONNECT:要求用隧道协议连接代理
CONNECT 方法要求在与代理服务器通信时建立隧道,实现用隧道协议进行 TCP 通信。
使用隧道通信相当于是在客户端和服务器之间建立了一个隧道,两者的报文从隧道走,可以保证通信的连续和安全。
CONNECT方法使用格式:
CONNECT 代理服务器名:端口号 HTTP版本
eg.
CONNECT 101.58.2.1:8080 HTTP/1.1
Host: www.baichen.com
🛕5. HTTP的状态码
状态码的职责是当客户端向服务器端发送请求时,描述返回的请求结果。 借助状态码,用户可以知道服务器端是正常处理了请求,还是出现了错误。
eg.
5.1 状态码分类
状态码 | 类别 | 原因短语 |
---|---|---|
1XX | Informational(信息性状态码) | 接收的请求正在处理 |
2XX | Success(成功状态码) | 请求正常处理完毕 |
3XX | Redirection(重定向状态码) | 需要进行附加操作以完成请求 |
4XX | Client Error(客户端错误状态码) | 服务器无法处理请求 |
5XX | Server Error(服务器错误状态码) | 服务器处理请求出错 |
RFC2016上规定的状态码有40余种,再加上WebDAV等的状态码,一共有60余种。但是,常用的状态码大概就14种,下面由我向大家介绍这14种状态码。
5.2 2XX 成功
2XX
的响应结果表明请求被正常处理了。
200 OK
表示从客户端发来的请求在服务器端被正常处理了。
204 No Content
该状态码代表服务器接收的请求已成功处理,但在返回的响应报文中不含实体的主体部分。 另外,也不允许返回任何实体的主体。比如,当从浏览器发出请求处理后, 返回 204 响应,那么浏览器显示的页面不发生更新 。
206 Partial Content
该状态码表示客户端进行了范围请求,而服务器成功执行了这部分的
GET
请求。响应报文中包含由Content-Range
首部字段指定范围的实体内容。
5.3 3XX 重定向
3XX
响应结果表明浏览器需要执行某些特殊的处理以正确处理请求。
301 Moved Permanently
永久性重定向。该状态码表示请求的资源已被分配了新的
URL
,以后应使用资源现在所指的URL
。也就是说,如果已经把资源对应的URL
保存为书签了,这时应该按Location
首部字段提示的URL
重新保存。永久性重定向一般用于网址搬迁、网站升级等服务,需要用
Location: 新的地址
指明跳转的地址,301
状态码会建议浏览器更新书签,将其指向新网址,但是浏览器的实现各不相同,可能需要你手动修改,也可能帮你修改。
302 Found
临时性重定向。该状态码表示请求的资源已被分配了新的
URL
,希望用户(本次)能使用新的URL
访问。一般用于网站抢修,或者跳转至登录页面等,这个状态码也需要配合
Location: 新的地址
一起使用,这个状态码不会建议浏览器更新书签。
303 See Other
该状态码表示由于请求对应的资源存在着另一个
URI
,应使用GET
方法定向获取请求的资源。303
状态码和302 Found
状态码有着相同的功能,但303
状态码明确表示客户端应当采用GET
方法获取资源,这点与302
状态码有区别。
注:当 301、302、303 响应状态码返回时,几乎所有的浏览器都会把 POST 改成 GET,并删除请求报文内的主体,之后请求会自动再次发送。301、302 标准是禁止将 POST 方法改变成 GET 方法的,但实际使用时大家都会这么做。
304 Not Modified
该状态码表示客户端发送附带条件的请求 A 时,服务器端允许请求访问资源,但未满足条件的情况。
304
状态码返回时,不包含任何响应的主体部分。
307 Temporary Redirect
临时重定向。该状态码与
302 Found
有着相同的含义。尽管302
标准禁止POST
变换成GET
,但实际使用时大家并不遵守。
307
会遵照浏览器标准, 不会从POST
变成GET
。但是,对于处理响应时的行为,每种浏览器有可能出现不同的情况。
5.4 4XX 客户端错误
4XX
的响应结果表明客户端是发生错误的原因所在。
400 Bad Request
该状态码表示请求报文中存在语法错误。当错误发生时,需修改请求的内容后再次发送请求。另外,浏览器会像
200 OK
一样对待该状态码。
401 Unauthorized
该状态码表示发送的请求需要有通过
HTTP 认证
( BASIC 认证、DIGEST 认证)的认证信息。 另外若之前已进行过 1 次请求,则表示用户认证失败。返回含有
401
的响应必须包含一个适用于被请求资源的WWW-Authenticate
首部用以质询( challenge)用户信息。当浏览器初次接收到401
响应,会弹出认证用的对话窗口。
403 Forbidden
该状态码表明对请求资源的访问被服务器拒绝了。服务器端没有必要给出拒绝的详细理由, 但如果想作说明的话,可以在实体的主体部分对原因进行描述,这样就能让用户看到了。
未获得文件系统的访问授权, 访问权限出现某些问题(从未授权的发送源 IP 地址试图访问)等列举的情况都可能是发生
403
的原因。
404 Not Found
该状态码表明服务器上无法找到请求的资源。除此之外,也可以在服务器端拒绝请求且不想说明理由时使用。
5.5 5XX服务器错误
5XX
的响应结果表明服务器本身发生错误。
500 Internal Server Error
该状态码表明服务器端在执行请求时发生了错误。
503 Service Unavailable
该状态码表明服务器暂时处于超负载或正在进行停机维护,现在无法处理请求。 如果事先得知解除以上状况需要的时间,最好写入
Retry After
首部字段再返回给客户端。
状态码的本意是用来快速获取请求状态,但是在很多场景下,我们会发现状态码和出错的地方根本就对不上。归根到底,状态码并不是一种强制性的命令,而是一种建议式的反馈,很多浏览器对于同一状态码的处理可能都千差万别,比如,404 状态码在谷歌浏览器上,如果返回有实体主体,那么谷歌浏览器并不会理会 404 状态码,而是显示返回的实体主体的内容,但是404 状态码在Edge浏览器上就会强制执行 404 Not Fount。
虽然网络上的状态码鱼龙混杂,但是我们自己写的状态码一定要符合标准,这样有利于养成良好的代码习惯和排查错误。
🕍6. HTTP的首部
HTTP 协议的请求和响应报文中必定包含 HTTP 首部。首部内容为客户端和服务器分别处理请求和响应提供所需要的信息。
HTTP 首部字段是构成 HTTP 报文的要素之一。在客户端与服务器之间以 HTTP 协议进行通信的过程中,无论是请求还是响应都会使用首部字段,它能起到传递额外重要信息的作用。使用首部字段是为了给浏览器和服务器提供报文主体大小、 所使用的语言、认证信息等内容。
eg.
HTTP 首部字段分类
- 请求报文
- 响应报文
HTTP 首部字段结构
首部字段名: 字段值
eg.
Content-Type: text/plain, charset=utf-8 -- 报文主体的对象类型,字体协议为utf-8协议
HTTP 首部字段类型
- 请求首部字段
- 响应首部字段
- 通用首部字段
- 实体首部字段
HTTP 首部字段概览
- 通用首部字段
- 请求首部字段
- 响应首部字段
- 实体首部字段
Connection
HTTP/1.1 之前的 HTTP 版本的默认连接都是非持久连接,也叫短连接。HTTP 协议是基于 TCP 协议的,所以要进行一次会话必须要经过建立连接、请求、响应、断开连接的过程,这样每进行一次请求和响应都要建立连接和断开连接,非常耗费时间,这是由于以前传输的都是体积不太大文本等文件,不需要持久连接。但是,现在由于网络传输需求的迅速发展,持久连接已经成为了 HTTP 连接的必然。
为此,如果想在旧版本的 HTTP 协议上维持持续连接,则需要指定
Connection
首部字段的值为Keep-Alive
。Connection: Keep-Alive
HTTP/1.1 版本的默认连接都是持久连接,也叫长连接。为此,客户端会在持久连接上连续发送请求。 当服务器端想明确断开连接时, 则指定
Connection 首部字段的值为 Close。Connection: Close
Connection 字段还有控制不再转发给代理的首部字段的功能,但是这里主要是讲解长短连接的概念
Cookie
Cookie 的工作机制是用户识别及状态管理。 Web 网站为了管理用户的状态会通过 Web 浏览器,把一些数据临时写入用户的计算机内。接着当用户访问该Web网站时,可通过通信方式取回之前发放的Cookie。
Cookie其实是一种特别我们经常使用的字段,因为HTTP协议是一种无状态的协议,也就意味着:本次会话和上一次会话之间没有任何关系,互不影响。但是,我们日常使用B站等视频网站时,却能够保持登录状态,这是因为HTTP协议不是无状态的吗?
当然不是,HTTP协议引入了 Cookie 值作为“会话保持”的媒介,那么 cookie 到底是什么呢?
简单来说,Cookie 是一种保存在内存或者本地磁盘上的数据文件,其中保存着客户端的各种信息。
- Set-Cookie
当服务器准备开始管理客户端的状态时,会使用
Set-Cookie
字段事先设置客户端的Cookie
。
Set-Cookie 字段的属性:
属性 | 说明 |
---|---|
NAME=VALUE | 赋予Cookie的名称和其值(必需项) |
expires=DATE | Cookie的有效期(若不明确指定则默认为浏览器关闭前为止) |
path=PATH | 将服务器上的文件目录作为Cookie的适用对象(若不指定则 默认为文档所在的文件目录) |
domain=域名 | 作为Cookie适用对象的域名 (若不指定则默认为创建 Cookie 的服务器的域名) |
Secure | 仅在HTTPS安全通信时才会发送Cookie |
HttpOnly | 加以限制,使Cookie不能被JavaScript脚本访问 |
eg.
Set-Cookie: id=1; password=123456; expires=Tue, 05 Jul 2023 18:43:32 GMT; path=/; domain=baichen.com;
- Cookie
当被服务器用
Set-Cookie
设置了Cookie
以后,客户端发送请求报文时,会在头部字段添加一个Cookie
字段,用来保存服务器设置的Cookie
。接收到多个Cookie
时,同样可以以多个Cookie
形式发送。一般来说,每个
Cookie
的大小不能超过4KB
。
eg.
Cookie: id=1; password=123456;
- Set-Cookie 和 Cookie 实例
本会话的源代码见简易HTTP服务器,以下报文都是基于下文代码获得,大家可以自行验证。
请求:
GET / HTTP/1.1
Host: baichen.com
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: xxx
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
响应:
HTTP/1.0 200 OK
Set-Cookie: id=1111 -- 设置Cookie
Set-Cookie: password=2222 -- 设置Cookie
Content-Type: text/html, charset=utf-8
Content-Length: XXX
.....
请求:
POST / HTTP/1.1
Host: baichen.com
Connection: keep-alive
Content-Length: 37
Content-Type: application/x-www-form-urlencoded
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Cookie: id=1111; password=2222 --- Cookie值
name=%E7%99%BD%E6%99%A8&passwd=123456
Session
如果有人通过一些手段截取了我们的
Cookie
文件,如果Cookie使用的是明文传递,那么我们的账号、密码等个人信息会被窃取,如果Cookie使用的是加密传递,虽然攻击者不能获得我们的私密信息,但是他可以使用我们的Cookie访问各种私密资源,甚至做违法之事。当然,前文提到了Cookie在配置良好的情况下,安全性还不错,但是总有被劫持的风险。
Session
就是为了解决上述问题而设计的方案,具体做法是:存储于服务器上的 Session 对象存储特定用户会话所需的属性及配置等私密信息,第一次访问该服务器的用户会创建一个唯一的 Session 对象存储私密信息,以后访问该服务器时就根据用户信息读取对应的信息。由于 Session 对象存储在服务器上,不在客户端本地保存,所以不会被劫持,这样就解决了安全的问题。但是可以只使用 Session 方案吗?是不行的,Session 是需要消耗服务器服务器资源的,会增加运行成本,所以并不能将大规模数据都保存到 Session 对象中。一般的方案是:Cookie 和 Session 配合使用,以达到安全和会话保持的平衡。
-
Cookie 和 Session 对比
-
Cookie
数据存放在客户端上,Session
数据放在服务器上。 -
Cookie
不是很安全,别人可以分析存放在本地的Cookie
并进行Cookie
欺骗。 -
Session
对比Cookie
来说更安全,Session
会在一定时间内保存在服务器上,但是当访问增多,会比较占用服务器的性能。 - 一般的方案是:
Cookie
和Session
配合使用,以达到安全和会话保持的平衡。
-
⛩️7. 搭建简易HTTP服务器
这次实现的HTTP服务器是一个最简单的HTTP服务器,主要是为了让大家了解HTTP的使用,实现的功能有显示网页,上传数据,设置Cookie等。
- 效果图如下:
- 首先,封装一个套接字库。
#pragma once
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
class Sock
{
public:
static int Socket()
{
int sock = socket(AF_INET, SOCK_STREAM, 0);
if(sock < 0)
{
std::cerr << "socket failed" << std::endl;
exit(1);
}
}
static void Bind(int sock, uint16_t port)
{
struct sockaddr_in local;
local.sin_family = AF_INET;
local.sin_port = htons(port);
local.sin_addr.s_addr = INADDR_ANY;
if(bind(sock, (struct sockaddr*)&local, sizeof(local)) < 0)
{
std::cerr << "bind failed" << std::endl;
exit(2);
}
}
static void Listen(int sock)
{
const int backlog = 5;
if(listen(sock, backlog) < 0)
{
std::cerr << "listen failed" << std::endl;
exit(3);
}
}
// tcp通信直接在套接字中写就可以,所以可以不用返回客户端的信息
static int Accept(int sock)
{
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
int new_sock = accept(sock, (struct sockaddr*)&peer, &len);
if(new_sock < 0)
{
std::cerr << "accept failed" << std::endl;
exit(4);
}
return new_sock;
}
static void Connect(int sock, const std::string& ip, uint16_t port)
{
struct sockaddr_in server;
memset((void*)&server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(port);
server.sin_addr.s_addr = inet_addr(ip.c_str());
if(connect(sock, (struct sockaddr*)&server, sizeof(server)) < 0)
{
std::cerr << "connect failed" << std::endl;
exit(5);
}
else
{
std::cout << "connect succeed" << std::endl;
}
}
};
- HTTP服务:
#include "Sock.hpp"
#include <fstream>
#include <sys/stat.h>
// 设置网页根目录
#define WWWROOT "./wwwroot/"
#define HOME_PAGE "index.html"
void Usage(std::string args)
{
std::cout << "Usage : " << args << " server_port" << std::endl;
}
void *handler(void *args)
{
pthread_detach(pthread_self());
int sock = *(int *)args;
delete (int *)args;
#define NUM 1024 * 10
char buf[NUM];
memset(buf, 0, sizeof(NUM));
// 接收请求报文
ssize_t sz = recv(sock, buf, sizeof(buf) - 1, 0);
if (sz > 0)
{
buf[sz] = 0;
std::cout << buf << std::endl;
std::string fileloc = WWWROOT;
fileloc += HOME_PAGE;
// fileloc += "a/b";
// 读取网页信息
std::ifstream ifs(fileloc);
if (!ifs.is_open())
{
std::string http_response = "HTTP/1.0 404 NotFount";
http_response += "Content-Type: text/html, charset=utf-8";
http_response += "\n";
http_response += "<html><p> 你访问的资源不存在 </p></html>";
send(sock, http_response.c_str(), http_response.size(), 0);
}
else
{
// 构建响应报文
std::string line;
std::string echo;
while (std::getline(ifs, line))
{
echo += line;
}
struct stat fst;
stat(fileloc.c_str(), &fst);
std::string http_response = "HTTP/1.0 200 OK\n";
// std::string http_response = "HTTP/1.0 404 NotFount";
// std::string http_response = "HTTP/1.0 302 Temporarily Move\n";
// http_response += "Location: http://www.bilibili.com";
http_response += "Set-Cookie: id=1111\n";
http_response += "Set-Cookie: password=2222\n";
http_response += "Content-Type: text/html, charset=utf-8\n";
http_response += "Content-Length: ";
http_response += std::to_string(fst.st_size);
http_response += "\n\n";
http_response += echo;
// http_response += "\n";
http_response += "\n";
// std::string http_response = "HTTP/1.0 200 OK\n";
// http_response += "Content-Type: text/plain\n";
// http_response += "\n";
// http_response += "hello, i'm from the futurn\n";
// 发送响应报文
send(sock, http_response.c_str(), http_response.size(), 0);
}
}
close(sock);
return nullptr;
}
int main(int argc, char *argv[])
{
if (argc != 2)
{
Usage(argv[0]);
exit(-1);
}
int sock = Sock::Socket();
Sock::Bind(sock, atoi(argv[1]));
Sock::Listen(sock);
while (true)
{
pthread_t tid;
int new_sock = Sock::Accept(sock);
// int *psock = new int(new_sock);
// pthread_create(&tid, nullptr, handler, psock);
}
return 0;
}
- index网页构建:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<h5>hello, i'm baichen!</h5>
<h5>hello, 我是表单!</h5>
<!-- action是数据提交到哪 method是提交数据时使用什么方法,可自行测试GET、POST方法传递信息的不同 -->
<form action="/a/handler_form" method="POST">
姓名:<input type="text" name="name"><br/>
密码:<input type="password" name="passwd"><br/>
<input type="submit" value="登录">
</form>
</body>
</html>
📕后记
参考书籍:《图解HTTP》
本篇文章白晨讲解了HTTP
的背景知识、协议结构、状态码、相关技术知识以及服务器的编写,也算是讲解的非常全面了。其中也有一些知识由于篇幅问题,白晨并没有展开说明,比如HTTP的首部字段等,需要大家在这篇文章的基础上自行进行相关学习。下一篇文章,白晨准备讲解HTTP协议的升级版——HTTPS
协议,通过学习HTTPS的学习,相信你会对HTTP协议有更深的理解。
如果大家有什么想和白晨交流的,欢迎私信白晨🎊。
如果讲解有不对之处还请指正,我会尽快修改,多谢大家的包容。
如果大家喜欢这个系列,还请大家多多支持啦😋!
如果这篇文章有帮到你,还请给我一个大拇指
👍和小星星
⭐️支持一下白晨吧!喜欢白晨【网络】系列的话,不如关注
👀白晨,以便看到最新更新哟!!!文章来源:https://www.toymoban.com/news/detail-784227.html
我是不太能熬夜的白晨,我们下篇文章见。文章来源地址https://www.toymoban.com/news/detail-784227.html
到了这里,关于【网络】HTTP协议详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!