文章目录
应用层协议------HTTP协议
HTTP简介
- HTTP(Hyper Text Transfer Protocol): 全称超文本传输协议,是用于从万维网服务器传输超文本到本地浏览器的传送协议
- HTTP 是一种应用层协议,是基于 TCP/IP 通信协议来传递数据的,其中 HTTP1.0、HTTP1.1、HTTP2.0 均为 TCP 实现,HTTP3.0 基于 UDP 实现。现主流使用 HTTP1.0 和 HTTP3.0
URL
URL简介
- 平时我们俗称的”网址“,其实就是 URL(Uniform Resource Locator),翻译为统一资源定位符
- 互连网上的每个文件都有一个唯一的 URL,它包含的信息指出文件的位置以及浏览器应该怎么处理它
URL格式
参数 | 简介 | 能否省略 |
---|---|---|
协议方案名 | 常见的协议类型有 http 和 https,访问 mysql 时的协议类型为 jdbc:mysql | 可以省略,省略后默认为 http:// |
登录信息 | 一般就是登录信息(用户名、密码等),但是现在的网站进行认证一般不再通过 URL 进行,故一般省略 | 可以省略 |
服务器地址 | 服务器的地址可以时一个 IP 地址,也可以是一个域名(域名会通过 DNS 系统解析成一个具体的 IP 地址,可以使用 ping 域名 来得到该域名的 IP 地址),IP 地址用来描述网络上的一个具体位置,能够用来定位一个具体的主机 | 在 HTML 中可以省略,省略后表示服务器的 ip 或域名与当前 HTML 所属的 ip 或域名一致 |
端口号 | 端口号的主要作用是表示一台计算机中的特定进程所提供的服务,即用来区分一个主机上的不同程序。每个程序在访问网络的时候,都会关联上一个或多个端口号,通过端口号就能区分出当前的请求要给谁。 | 可以被省略,当端口号省略时,浏览器会根据协议类型自动决定使用哪个端口号(如 http 协议默认使用80端口,https 协议默认使用443端口) |
资源层级 UNIX 文件路径 | 表示访问该服务器程序上某个资源的路径 | 可以省略,省略后相当于 / |
文件名 | 表示访问该服务器上的哪个资源(如 html、图片等等) | 不能省略 |
查询字符串 | 查询字符串(query string)本质是一个键值对结构,且键值对之间使用 & 分割,键和值之间使用 = 分割。表示客户端给服务器传递的参数。该参数是 web 开发的一个重要参数,给前后端交互提供了很多可能性。该参数 key 和 value 的取值和个数,完全都是由程序员自己约定,因此可以通过这样的方式来自定制我们需要的信息给服务器 | 可以省略 |
片段标识符 | 片段标识符主要用于页面内跳转,例如跳转到当前页面的某个部分、章节等等 | 可以省略 |
urlencode和urldecode
当我们在百度上搜索’c++‘时,得到的URL如下:
通过 urldecode,知道c%2B%2B就是表示c++
为什么需要urlencode?
-
这是因为像 /、?、: 等这样的字符,已经被 url 当做特殊意义理解了,因此这些字符不能随意出现。如果某个参数中需要带有这些特殊字符,就必须先对特殊字符进行转义,即 urlencode
-
一个中文字符由 UTF-8 或者 GBK 这样的编码方式构成,虽然在 URL 中没有特殊含义,但是仍然需要进行转义,否则浏览器可能把 UTF-8/GBK 编码中的某个字节当做 URL 中的特殊符号
转义的规则如下:
将需要转码的字符转为16进制,然后从右到左,取4位(不足4位直接处理),每2位做一位,前面加上%,编码成 %XY 格式
HTTP通讯流程
协议格式
HTTP请求协议格式
HTTP请求由以下四部分组成:
- 请求行:[请求方法]+[url]+[http版本]
- 请求报头:请求的属性,这些属性都是以key: value的形式按行陈列的
- 空行:遇到空行表示请求报头结束
- 请求正文:请求正文允许为空字符串,如果请求正文存在,则在请求报头中会有一个Content-Length属性来标识请求正文的长度
如何将HTTP请求的报头与有效载荷进行分离?
我们可以根据HTTP请求当中的空行来进行分离,当服务器收到一个HTTP请求后,就可以按行进行读取,如果读取到空行则说明已经将报头读取完毕,实际HTTP请求当中的空行就是用来分离报头和有效载荷的
如果将HTTP请求想象成一个大的线性结构,此时每行的内容都是用\n隔开的,因此在读取过程中,如果连续读取到了两个\n,就说明已经将报头读取完毕了,后面剩下的就是有效载荷了
我们来实现一个简单的TCP服务器,将浏览器发来的HTTP请求进行打印即可,通过具体例子来加深对HTTP请求的印象
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <strings.h>
#include <signal.h>
#include <sys/wait.h>
#include <pthread.h>
#define BACKLOG 5
//./httpServer 8888
int main(int argc,char* argv[])
{
if(argc != 2)
{
std::cout<<"Usage"<<"\n\t"<<"proc port"<<std::endl;
return 1;
}
int port = std::atoi(argv[1]);
//创建套接字
int listenSocket = socket(AF_INET,SOCK_STREAM,0);
if(listenSocket < 0)
{
std::cerr<<"create listen socket error"<<std::endl;
return 1;
}
//绑定
struct sockaddr_in local;
bzero(&local,sizeof(local));
local.sin_family = AF_INET;
local.sin_port = htons(port);
local.sin_addr.s_addr = INADDR_ANY;
if(bind(listenSocket,(struct sockaddr*)&local,sizeof(local)) < 0)
{
std::cerr<<"bind error"<<std::endl;
return 2;
}
//监听
if(listen(listenSocket,BACKLOG) < 0)
{
std::cerr<<"listen error"<<std::endl;
return 3;
}
//accept
struct sockaddr_in peer;
bzero(&peer,sizeof(peer));
socklen_t len = sizeof(peer);
for(;;)
{
int newSocket = accept(listenSocket,(sockaddr*)&peer,&len);
if(newSocket < 0)
{
std::cerr<<"accept error"<<std::endl;
continue;
}
if(fork() == 0)
{
close(listenSocket);
if(fork() > 0)
{
exit(0);
}
char buffer[1024];
recv(newSocket,buffer,sizeof buffer,0);
std::cout << "--------------------------http request begin--------------------------" << std::endl;
std::cout << buffer << std::endl;
std::cout << "---------------------------http request end---------------------------" << std::endl;
close(newSocket);
exit(0);
}
close(newSocket);
waitpid(-1,nullptr,0);
}
return 0;
}
HTTP响应协议格式
HTTP响应由以下四部分组成:
- 状态行:[http版本]+[状态码]+[状态码描述]
- 响应报头:响应的属性,这些属性都是以key: value的形式按行陈列的
- 空行:遇到空行表示响应报头结束
- 响应正文:响应正文允许为空字符串,如果响应正文存在,则响应报头中会有一个Content-Length属性来标识响应正文的长度。比如服务器返回了一个html页面,那么这个html页面的内容就是在响应正文当中的
如何将HTTP请求的报头与有效载荷进行分离?
对于HTTP响应来讲,这里的状态行和响应报头就是HTTP的报头信息,而这里的响应正文实际就是HTTP的有效载荷。与HTTP请求相同,当应用层收到一个HTTP响应时,也是根据HTTP响应当中的空行来分离报头和有效载荷的。当客户端收到一个HTTP响应后,就可以按行进行读取,如果读取到空行则说明报头已经读取完毕
HTTP的方法
其中最常用的就是POST与GET方法
GET方法一般用于获取某种资源信息,而POST方法一般用于将数据上传给服务器。但实际我们上传数据时也有可能使用GET方法。POST与GET最大的不同点就在于GET方法是通过URL传参的,而POST通过正文传参;从GET方法和POST方法的传参形式可以看出,POST方法能传递更多的参数,因为url的长度是有限制的,POST方法通过正文传参就可以携带更多的数据,此外,使用POST方法传参更加私密,因为POST方法不会将你的参数回显到url当中,此时也就不会被别人轻易看到,由此可以得到结论:
- GET通过URL传参,POST通过正文传参
- 使用POST方法传参更加私密,但想要做到真正的安全必须通过加密来实现
HTTP的状态码
类别 | 原因短语 | |
---|---|---|
1XX | Informational(信息性状态码) | 接收的请求正在处理 |
2XX | Success(成功状态码) | 请求正常处理完毕 |
3XX | Redirection(重定向状态码) | 需要进行附加操作以完成请求 |
4XX | Client Error(客户端错误状态码) | 服务器无法处理请求 |
5XX | Server Error(服务器错误状态码) | 服务器处理请求出错 |
最常见的状态码,比如200(OK),404(Not Found),403(Forbidden请求权限不够),302(Redirect),504(Bad Gateway)
重定向就是通过各种方法将各种网络请求重新定个方向转到其它位置,此时这个服务器相当于提供了一个引路的服务。重定向又可分为临时重定向和永久重定向,其中状态码301表示的就是永久重定向,而状态码302和307表示的是临时重定向。
临时重定向和永久重定向本质是影响客户端的标签,决定客户端是否需要更新目标地址。如果某个网站是永久重定向,那么第一次访问该网站时由浏览器帮你进行重定向,但后续再访问该网站时就不需要浏览器再进行重定向了,此时你访问的直接就是重定向后的网站。而如果某个网站是临时重定向,那么每次访问该网站时如果需要进行重定向,都需要浏览器来帮我们完成重定向跳转到目标网站
HTTP常见Header
-
Content-Type:数据类型(text/html等)。
-
Content-Length:正文的长度。
-
Host:客户端告知服务器,所请求的资源是在哪个主机的哪个端口上。
-
User-Agent:声明用户的操作系统和浏览器的版本信息。
-
Referer:当前页面是哪个页面跳转过来的。
-
Location:搭配3XX状态码使用,告诉客户端接下来要去哪里访问。
-
Cookie:用于在客户端存储少量信息,通常用于实现会话(session)的功能
Keep-Alive(长连接)
在早期的HTTP/1.0
中,每次http请求都要创建一个连接,而创建连接的过程需要消耗资源和时间,为了减少资源消耗,缩短响应时间,就需要重用连接。在后来的HTTP/1.0
中以及HTTP/1.1
中,引入了重用连接的机制,就是在http请求头中加入Connection: keep-alive
来告诉对方这个请求响应完成后不要关闭,下一次咱们还用这个请求继续交流。协议规定HTTP/1.0
如果想要保持长连接,需要在请求头中加上Connection: keep-alive
,而HTTP/1.1默认是支持长连接的,有没有这个请求头都行
Cookie和Session
cookie是什么?
首先我们要知道,HTTP是一种无状态协议,现在有这种场景,假如你是某个视频网站的VIP,这个网站里面的VIP视频有成百上千个,你每次点击一个视频都要重新进行VIP身份认证。而HTTP不支持记录用户状态,那么我们就需要有一种独立技术来帮我们支持,这种技术目前现在已经内置到HTTP协议当中了,叫做cookie,以下就是cookie使用的一种场景:
在我们第一次登陆一个网站时,我们需要输入用户名与密码,当认证通过并在服务端进行Set-Cookie设置后,服务器在对浏览器进行HTTP响应时就会将这个Set-Cookie响应给浏览器。而浏览器收到响应后会自动提取出Set-Cookie的值,将其保存在浏览器的cookie文件当中,此时就相当于我的账号和密码信息保存在本地浏览器的cookie文件当中
但是单纯的使用cookie是非常不安全的,cookie文件当中保存的是你的私密信息,在网络传输中,一旦cookie文件泄漏你的隐私信息也就泄漏,因此便有了SessionID的概念
此时当认证通过后服务端在对浏览器进行HTTP响应时,就会将这个生成的SessionID值响应给浏览器。浏览器收到响应后会自动提取出SessionID的值,将其保存在浏览器的cookie文件当中。后续访问该服务器时,对应的HTTP请求当中就会自动携带上这个SessionID
引入SessionID后的好处
- 在引入SessionID之前,用户登录的账号信息都是保存在浏览器内部的,此时的账号信息是由客户端去维护的。
41689343578)]
此时当认证通过后服务端在对浏览器进行HTTP响应时,就会将这个生成的SessionID值响应给浏览器。浏览器收到响应后会自动提取出SessionID的值,将其保存在浏览器的cookie文件当中。后续访问该服务器时,对应的HTTP请求当中就会自动携带上这个SessionID
引入SessionID后的好处
- 在引入SessionID之前,用户登录的账号信息都是保存在浏览器内部的,此时的账号信息是由客户端去维护的。
- 而引入SessionID后,用户登录的账号信息是有服务器去维护的,在浏览器内部保存的只是SessionID