前面一章初步认识了URL,HTTP请求和相应协议格式,有所忘记的可以看一下前面的博客
(3条消息) 【网络篇】第十四篇——HTTP协议(一)(附带电视剧李浔同款爱心+端口号被恶意占用如何清除)_接受平凡 努力出众的博客-CSDN博客
HTTP的方法
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 |
UNLINK | 断开连接关系 | 1.0 |
其中最常用的就是GET方法和POST方法。
GET方法和POST方法
GET方法的含义是请求从服务器获取资源, 这个资源可以是文本,页面,图片视频等等。
例如,你打开我在CSDN写的文章,浏览器就会发送GET请求给服务器,服务器就会返回文章的所有文字及资源。
POST方法则是相反的,一般用于将数据上传给服务器,它向URI指定的资源提交数据,数据就放在报文的body中.
例如,你在我的CSDN文章下面,评论了然后提交,浏览器就会执行一次POST请求,把你的评论放进报文body里,然后拼接好POST请求头,通过TCP协议发送给服务器。
GET方法和POST方法都可以带参:
- GET方法是通过url传参的。
- POST方法是通过正文传参的。
从GET方法和POST方法的传参形式可以看出,POST方法能传递更多的参数,因为url的长度是有限制的,而POST方法通过正文传参就可以携带比它更多的数据。
使用POST方法的传参会更加私密,因为POST方法不会将你的参数回显到url当中,此时也就不会被别人轻易看到。不能说POST方法比GET方法更安全,因为POST方法和GET方法实际上都不安全,要做到安全只能通过加密来完成。
GET和POST方法都是安全和幂等的嘛?
先说明下安全和幂等的概念:
- 在 HTTP 协议里,所谓的「安全」是指请求方法不会「破坏」服务器上的资源。
-
所谓的「幂等」,意思是多次执行相同的操作,结果都是「相同」的。
Postman演示GET和POST区别
如果访问我们的服务器使用的是GET方法,此时应该通过url进行传参,可以在Params下进行参数设置,因为Postman当中的Params就相当于url当中的参数,你在设置参数时可以看到对应的url也在随之变化。
此时在我们的服务器收到的HTTP请求当中,可以看到请求行中的url就携带上了我们刚才在Postman当中设置的参数。
而如果我们使用的是POST方法,此时就应该通过正文进行传参,可以在Body下进行参数设置,在设置时可以选中Postman当中的raw方式传参,表示原始传参,也就是你输入的参数是什么样的实际传递的参数就是什么样的.
此时服务器收到的HTTP请求的请求正文就不再是空字符串了,而是我们通过正文传递的参数。
因为此时响应正文不为空字符串,因此响应报头当中出现了Content-Length属性,表示响应正文的长度。
TCP套接字演示GET和POST的区别
要演示GET方法和POST方法传参的区别,就需要让浏览器提交参数,此时我们可以在index.html当中再加入两个表单,用作用户名和密码的输入,然后再新增一个提交按钮,此时就可以让浏览器提交参数了。
我们可以通过修改表单当中的method属性指定参数提交的方法,还有一个属性叫做action,表示想把这个表单提交给服务器上的哪个资源。
此时当我们用浏览器访问我们的服务器时,就会显示这两个表单。
当前我们是用GET方法提交参数的,当我们填充完用户名和密码进行提交时,我们的用户名和密码就会自动被同步到url当中。
同时在服务器这边也通过url收到了刚才我们在浏览器提交的参数。
如果我们将提交表单的方法改为POST方法,此时当我们填充完用户名和密码进行提交时,对应提交的参数就不会在url当中体现出来,而会通过正文将这两个参数传递给了服务器。
此时用户名和密码就通过正文的形式传递给服务器了。
说明一下:
- 使用GET方法时,我们提交的参数会回显到url当中,因此GET方法一般是处理数据不敏感的。
- 如果你要传递的数据比较私密的话一定要用POST方法,不是因为POST方法安全,实际上GET和POST方法传参都是明文传送,因此都不安全,但是相比于GET而言POST方法更加私密,因为POST是通过正文传参的,不会将参数显示到浏览器上的url框上。
HTTP的状态码
- 1xx类:属于提示信息,是协议处理中的一种中间状态,实际用到的比较少。
- 2xx类:表示服务器成功处理了客户端的请求,也是我们最愿意看到的状态。
「200 OK]是最常见的成功状态码,表示一切正常。如果是非HEAD请求,服务器返回的响应头都会有body数据。
「204 No Content]也是最常见的成功状态码,也200 OK基本相同,但是响应头没有body数据。
[206 Partial Contentt]:是应用于HTTP分块下载或断点续传,表示响应返回的body数据并不是资源的全部,而是其中的一部分,也是服务器处理成功的状态。
- 3xx类:码表示客户端请求的资源发送了变动,需要客户端用新的 URL 重新发送请求获取资源,也就是重定向。
- 4xx类:表示客户端发送的报文有误,服务器无法处理,也就是错误码的含义。
- 5xx类:表示客户端请求报文正确,但是服务器处理时内部发生了错误,属于服务器端的错误
码。
临时重定向演示
进行临时重定向时需要用到Location字段,Location字段是HTTP报头当中的一个属性信息,该字段表明了你所要重定向到的目标网站。
我们这里要演示临时重定向,可以将HTTP响应当中的状态码改为307,然后跟上对应的状态码描述,此外,还需要在HTTP响应报头当中添加Location字段,这个Location后面跟的就是你需要重定向到的网页,比如我们这里将其设置为CSDN的首页。
#include <iostream>
#include <fstream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
using namespace std;
int main()
{
//创建套接字
int listen_sock = socket(AF_INET, SOCK_STREAM, 0);
if (listen_sock < 0){
cerr << "socket error!" << endl;
return 1;
}
//绑定
struct sockaddr_in local;
memset(&local, 0, sizeof(local));
local.sin_family = AF_INET;
local.sin_port = htons(8081);
local.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(listen_sock, (struct sockaddr*)&local, sizeof(local)) < 0){
cerr << "bind error!" << endl;
return 2;
}
//监听
if (listen(listen_sock, 5) < 0){
cerr << "listen error!" << endl;
return 3;
}
//启动服务器
struct sockaddr peer;
memset(&peer, 0, sizeof(peer));
socklen_t len = sizeof(peer);
for (;;){
int sock = accept(listen_sock, (struct sockaddr*)&peer, &len);
if (sock < 0){
cerr << "accept error!" << endl;
continue;
}
if (fork() == 0){ //爸爸进程
close(listen_sock);
if (fork() > 0){ //爸爸进程
exit(0);
}
//孙子进程
char buffer[1024];
recv(sock, buffer, sizeof(buffer), 0); //读取HTTP请求
cout << "--------------------------http request begin--------------------------" << endl;
cout << buffer << endl;
cout << "---------------------------http request end---------------------------" << endl;
//构建HTTP响应
string status_line = "http/1.1 307 Temporary Redirect\n"; //状态行
string response_header = "Location: https://www.csdn.net/\n"; //响应报头
string blank = "\n"; //空行
string response = status_line + response_header + blank; //响应报文
//响应HTTP请求
send(sock, response.c_str(), response.size(), 0);
close(sock);
exit(0);
}
//爷爷进程
close(sock);
waitpid(-1, nullptr, 0); //等待爸爸进程
}
return 0;
}
如果我们用浏览器访问我们的服务器,当浏览器收到这个HTTP响应后,还会对这个HTTP响应进行分析,当浏览器识别到状态码是307后就会提取出Location后面的网址,然后继续自动对该网站继续发起请求,此时就完成了页面跳转这样的功能,这样就完成了重定向功能。
此时当浏览器访问我们的服务器时,就会立马跳转到CSDN的首页。
HTTP常见的Header
HTTP常见的Header如下:
- Content-Type:数据类型(text/html等)
- Content-Length:正文的长度。
- Host:客户端告知服务器,所请求的资源是在哪个主机的哪个端口上。
- User-Agent:声明用户的操作系统和浏览器的版本信息。
- Referer:当前页面是哪个页面跳转过来的。
- Location:搭配3XX状态码使用,告诉客户端接下来要去哪里访问。
- Cookie:用于在客户端存储少量信息,通常用于实现会话(session)的功能。
Host:
客户端发送请求时,用来指定服务器的域名。
有了Host字段,就可以将请求发往[同一台]服务器上的不同网站。
Content-Length
服务器在返回数据时,会有Content-Length字段,表明本次回应的数据长度
如上面则是告诉浏览器,本次服务器回应的数据长度是1000个字节,后面的字节就属于下一个回应了。
Connection
Connection 字段最常用于客户端要求服务器使用 TCP 持久连接,以便其他请求复用。
一个可以复用的 TCP 连接就建立了,直到客户端或服务器主动关闭连接。但是,这不是标准字段。
User-Agent
User-Agent代表的是客户端对应的操作系统和浏览器的版本信息。
比如当我们用电脑下载某些软件时,它会自动向我们展示与我们操作系统相匹配的版本,这实际就是因为我们在向目标网站发起请求的时候,User-Agent字段当中包含了我们的主机信息,此时该网站就会向你推送相匹配的软件版本。
Content-Type
Content-Type 字段用于服务器回应时,告诉客户端,本次数据是什么格式。
上面的类型表明,发送的是网页,而且编码是UTF-8.
客户端请求的时候,可以使用 Accept 字段声明自己可以接受哪些数据格式。 上面代码中,客户端声明自己可以接受任何格式的数据。
Content-Encoding
Content-Encoding 字段说明数据的压缩方法。表示服务器返回的数据使用了什么压缩格式 。
Cookie和Session
HTTP实际上是一种无状态协议,HTTP的每次请求/响应之间是没有任何关系的,但你在使用浏览器的时候发现并不是这样的。
比如你登录一次CSDN侯,就算把CSDN网站关了甚至是重启电脑,当你再次打开CSDN网站时,CSDN并没有要求你再次输入账号和密码,这实际上就是通过cookie技术实现的,点击浏览器当中的锁的标志就可以看到对应网站的各种cookie数据。
这些cookie数据实际都是对应的服务器方写的,如果你将对应的某些cookie删除,那么此时可能就需要你重新进行登录认证了,因为你删除的可能正好就是你登录时所设置的cookie信息。
cookie是什么
由于HTTP是一种无状态协议,如果没有cookie的存在,那么每当要进行页面请求时都需要重新输入账号和密码进行认证,这样太麻烦。
当我们第一次登录某个网站时,需要输入我们的账号和密码进行身份认证,此时如果服务器经过数据比对后判定你是一个合法的用户,那么为了让你后续在进行某些网页请求时不用重新输入账号和密码,此时服务器就会进行Set-Cookie的设置。(Set-Cookie也是HTTP报头当中的一种属性信息)
当认证通过并在服务端进行Set-Cookie设置后,服务器在对浏览器进行HTTP响应时就会将这个Set-Cookie响应给浏览器。而浏览器收到响应后会自动提取出Set-Cookie的值,将其保存在浏览器的cookie文件当中,此时就相当于我的账号和密码信息保存在本地浏览器的cookie文件当中。
从第一次登录认证之后,浏览器再向该网站发起HTTP请求当中就就会自动包含一个cookie字段,其中携带的就是我第一次的认证信息,此后对端服务器需要对你进行认证时就会直接提取出HTTP请求当中的cookie字段,而不会重新让你输出账号和密码了。
也就是在第一次认证登录后,后续所有的认证都变成了自动认证,这就叫做cookie技术。
内存级别&文件级别
cookie就是在浏览器当中的一个小文件,文件里记录就是用户的私有信息,cookie文件可以分种,一种是内存级别的cookie文件,另一种是文件级别的cookie文件。
- 将浏览器关掉之后再打开,访问之前登录过的网站,如果需要你重新输入账号和密码,说明你之前登录时浏览器当中保存的cookie信息是内存级别的。
- 将浏览器关掉甚至关机重启再打开,访问之前登录的网站,如果不需要你重新输入账号和密码,说明你之前登录时浏览器当中保存的cookie信息是文件级别的。
cookie被盗取
如果你浏览器当中保存的cookie信息被非法用户盗取了,那么此时这个非法用户就可以用你的cookie信息,以你的身份去访问你曾经访问过的网站,我们将这种现象称为cookie被盗取了。
比如你不小心点了某个链接,这个链接可能就是一个下载程序,当你点击之后它就会通过某种方式把程序下载到你本地,并且自动执行该程序,该程序会扫描你的浏览器当中的cookie目录,把所有的cookie信息通过网络的方式传送给恶意方,当恶意方拿到你的cookie信息后就可以拷贝到它的浏览器对应的cookie目录当中,然后以你的身份访问你曾经访问过的网站。
SessionID
单纯的使用cookie是非常不安全的,因为此时cookie文件当中就保存的是你的私密信息,一旦cookie文件泄漏你的隐私信息也就泄漏。
所以当前主流的服务器还引入了SessionID这样的概念,当我们第一次登录某个网站输入账号和密码后,服务器认证成功后还会服务端生成一个对应的SessionID,这个SessionID与用户信息是不相关的。系统会将所有登录用户的SessionID值统一维护起来。
此时当认证通过后服务端在对浏览器进行HTTP响应时,就会将这个生成的SessionID值响应给浏览器。浏览器收到响应后会自动提取出SessionID的值,将其保存在浏览器的cookie文件当中。后续访问该服务器时,对应的HTTP请求当中就会自动携带上这个SessionID
而服务器识别到HTTP请求当中包含了SessionID,就会提取出这个SessionID,然后再到对应的集合当中进行对比,对比成功就说明这个用户是曾经登录过的,此时也就自动就认证成功了,然后就会正常处理你发来的请求,这就是我们当前主流的工作方式。
安全是相对的
引入SessionID之后,浏览器当中的cookie文件保存的是SessionID,此时这个cookie文件同样可能被盗取。此时用户的账号和密码虽然不会泄漏了,但用户对应的SessionID是会泄漏的,非法用户仍然可以盗取我的SessionID去访问我曾经访问过的服务器,相当于还是存在刚才的问题。
- 之前的工作方式就相当于把账号和密码信息在浏览器当中再保存一份,每次请求时都自动将账号和密码的信息携带上,但是账号和密码一直在网当中发送太不安全了。
- 因此现在的工作方式是,服务器只有在第一次认证的时候需要在网络中传输账号和密码,此后在网络上发送的都是SessionID。
这种方法虽然没有真正解决安全问题,但这种方法是相对安全的。互联网上是不存在绝对安全这样的概念的,任何安全都是相对的,就算你将发送到网络当中的信息进行加密,也有可能被别人破解。
不过在安全领域有一个准则:如果破解某个信息的成本已经远远大于破解之后获得的收益(说明做这个事是赔本的),那么就可以说这个信息是安全的
引入SessionID后的好处
- 在引入SessionID之前,用户登录的账号信息都是保存在浏览器内部的,此时的账号信息是由客户端去维护的。
- 而引入SessionID后,用户登录的账号信息是有服务器去维护的,在浏览器内部保存的只是SessionID。
此时虽然SessionID可能被非法用户盗取,但服务器也可以使用各种各样的策略来保证用户账号的安全。
- IP是有归类的,可以通过IP地址来判断登录用户所在的地址范围。如果一个账号在短时间内登录地址发送了巨大变化,此时服务器就会立马识别到这个账号发生异常了,进而在服务器当中清除对应的SessionID的值。这时当你或那个非法用户想要访问服务器时,就都需要重新输入账号和密码进行身份认证,而只有你是知道自己的密码的,当你重新认证登录后服务器就可以将另一方识别为非法用户,进而对该非法用户进行对应的黑名单/白名单认证。
- 当操作者想要进行某些高权限的操作时,会要求操作者再次输入账号和密码信息,再次确认身份。就算你的账号被非法用户盗取了,但非法用户在改你密码时需要输入旧密码,这是非法用户在短时间内无法做到的,因为它并不知道你的密码。这也就是为什么账号被盗后还可以找回来的原因,因为非法用户无法在短时间内修改你的账号密码,此时你就可以通过追回的方式让当前的SessionID失效,让使用该账号的用户进行重新登录认证
- SessionID也有过期策略,比如SessionID是一个小时内是有效的。所以即便你的SessionID被非法用户盗取了,也仅仅是在一个小时内有效,而且在功能上受约束,所以不会造成太大的影响。
任何事情都有两面性,如果不是这些非法用户的存在,现在的服务器肯定是漏洞百出,只有双方不断进行对抗双方才能不断进步。
HTTP VS HTTPS
HTTP与HTTPS有哪些区别?
1.HTTP是超文本传输协议,信息是明文传输,存在安全风险的问题,HTTPS则解决HTTP不安全的缺陷,在TCP和HTTP网络层之间加入了SSL/TLS安全协议,使得报文能够加密传输。
2. HTTP连接建立相对简单,TCP三次握手后便可以进行HTTP的报文传输。而HTTPS在TCP三次握手之后,还需进行SSL/TLS的握手过程,才可进入加密报文传输。
3.HTTP的端口号是80,HTTPS的端口号是443。
4.HTTPS协议需要向CA申请数字整数,来保证服务器的身份是可信的。
HTTPS解决了HTTP的哪些问题?
HTTP由于是明文传输,所以安全上存在以下三个风险:
- 窃听风险,比如通信链路上可以获取通信内容,用户号容易没。
- 篡改风险,比如强制植入垃圾广告,视觉污染,用户眼容易瞎。
- 冒充风险,比如冒充淘宝网站,用户钱容易没。
HTTPS在HTTP与TCP层之间加入了SSL/TLS协议,可以很好的解决了上述的风险:
- 信息加密:交互信息无法被窃取,但你得号回因为[自身忘记]账号而没。
- 校验机制:无法篡改通信内容,篡改了就不能正常显示,但百度[竞价排名]依然可以搜索到垃圾广告。
- 身份证书:证明淘宝是真的淘宝网。
可见,SSL/TLS协议是能保证通信是安全的.
HTTPS是如何解决上面的三个风险的?
- 混合加密的方式实现信息的机密性,解决了窃听的风险。
- 摘要算法的方式来实现完整性,它能够为数据生成独一无二的[指纹],指纹用于校验数据完整性,解决了篡改的风险。
- 将服务器公钥放入到数字证书中,解决了冒充的风险。
混合加密
通过混合加密的方式就可以保证信息的机密性,解决了窃听的风险.
-
在通信建立前采用 非对称加密 的方式交换「会话秘钥」,后续就不再使用非对称加密。
-
在通信过程中全部使用 对称加密 的「会话秘钥」的方式加密明文数据
采用「混合加密」的方式的原因:
- 对称加密只使用一个密钥,运算速度快,密钥必须保密,无法做到安全的密钥交换
-
非对称加密 使用两个密钥:公钥和私钥,公钥可以任意分发而私钥保密,解决了密钥交换问题但 速度慢。
摘要算法
HTTP/1.1,HTTP/2,HTTP/3演变
HTTP/1.1相比 HTTP/1.0提高了什么性能?
HTTP/1.1相比HTTP/1.0性能上的改进:
- 使用TCP长连接方式改善了HTTP/1.0短链接造成的性能开销。
- 支持管道(pipeline)网络传输,只要第一个请求发出去了,不必等其回来,就可以发送第二个请求出去,可以减少整体的响应时间。
但HTTP/1.1还是有性能瓶颈:
- 请求/响应头部(Header)未经压缩就发送,首部信息越多延迟越大。只能压缩Body的部分。
- 发送冗长的首部。每次互相发送相同的首部造成浪费较多;
- 服务器是按请求的顺序相应的,如果服务器响应慢,会招致客户端一直请求不到数据,也就是对头阻塞;
- 没有请求优先级控制;
- 请求只能从客户端开始,服务器只能被动响应。
那上面的HTTP/1.1的性能瓶颈,HTTP/2做了什么优化?
HTTP/2有哪些缺陷?HTTP/3做了哪些优化?
-
HTTP/1.1 中的管道( pipeline )传输中如果有一个请求阻塞了,那么队列后请求也统统被阻塞住了
-
HTTP/2 多个请求复用一个 TCP 连接,一旦发生丢包,就会阻塞住所有的 HTTP 请求。这都是基于 TCP 传输层的问题,所以 HTTP/3 把 HTTP 下层的 TCP 协议改成了 UDP !
-
QUIC 有自己的一套机制可以保证传输的可靠性的。当某个流发生丢包时,只会阻塞这个流, 其他 流不会受到影响
-
TLS3 升级成了最新的 1.3 版本,头部压缩算法也升级成了 QPackHTTPS 要建立一个连接,要花费 6 次交互,先是建立三次握手,然后是 TLS/1.3 的三次握手。QUIC 直接把以往的 TCP 和 TLS/1.3 的 6 次交互 合并成了 3 次,减少了交互次数 。所以, QUIC 是一个在 UDP 之上的 伪 TCP + TLS + HTTP/2 的多路复用的协议。
QUIC 是新协议,对于很多网络设备,根本不知道什么是 QUIC,只会当做 UDP,这样会出现新的问题。所以 HTTP/3 现在普及的进度非常的缓慢,不知道未来 UDP 是否能够逆袭 TCP。