HTTP(Hypertext Transfer Protocol)是一种用于传输超文本的应用层协议。它是在Web应用中最为常用的协议之一,用于在客户端和服务器之间传输数据。
其多工作在tcp协议之上,是由大佬在软件层编写的一个协议。
URL是用于标识和定位资源的字符串格式,HTTP是一种用于传输超文本的协议,它通过URL来指定要访问的资源。(实际上就是我们所说的网址)
http://
表示的是协议名称 ,表示请求时需要使用的协议 ,通常使用的是HTTP协议或安全协议HTTPS。
usr:pass
表示的是登录认证信息 。(包括登录用户的用户名和密码)
虽然登录认证信息可以在URL中体现出来, 但绝大多数URL的这个字段都是被省略的 因为登录信息可以通过其他方案交付给服务器。
www.example.jp表示的是服务器地址 ,也叫做域名。
而域名实际上就是为了方便我们记忆而设计出来和IP地址一一对应的助记符。
在进行套接字编程时我们需要给服务器绑定对应的IP和端口, 而这里的应用层协议也同样需要有明确的端口号。
常见协议对应的端口号:
协议名称 | 对应端口号 |
---|---|
HTTP | 80 |
HTTPS | 443 |
SSH | 22 |
实际上0~1023的端口号,早在互联网发展的初期已经被确定好了,就比如说我们上面举例的这些。
/dir/index.htm
表示的是要访问的资源所在的路径,就好比在计算机磁盘系统上要确定一个文件的位置就需要用到这一路径信息。
uid=1
表示的是发送网络请求时提供的额外的参数 ,这些参数是以键值对的形式 通过&符号分隔开的。
当我们在百度上搜索HTTP的时候可以在最上面的URL发现这样一段文字:
这代表着我们的搜索关键字是HTTP协议。
ch1
是一个片段标识符。
片段标识符是指URL中的一个特殊字符“#”后面的部分 ,它通常用于指定文档中的某个位置。
例如HTML文档中的锚点 在浏览器中 ,当用户点击一个链接时 ,浏览器会自动滚动到该链接指定的位置。
片段标识符不会被发送到服务器, 因此不会影响服务器的响应。
urlencode
和urldecode
是用于处理URL中特殊字符的编码和解码函数。
它将URL中的非字母数字字符替换为百分号(%)后跟两个表示字符ASCII码的十六进制值。这样做是为了确保URL在传输过程中不会出现冲突或错误(比如说用在传网址的时候用了个空格符号)。
我们发起网页请求时,我们实际上是向服务器发送了一个HTTP请求。服务器接收到请求后,会根据请求的内容和参数进行处理,并返回一个HTTP响应。
HTTP响应中包含了网页的信息,包括HTML、CSS、JavaScript等资源文件。这些资源文件会被浏览器解析和渲染,最终呈现给用户的是一个完整的网页。因此,我们可以说我们通过发起网页请求得到了一个完整的网页。
这张图片想必大家也不陌生,我们接下来的博客顺序也是采用了自顶向下来为大家一一介绍。
实际上在这四层协议当中,下三层主要负责的是建立起通信过程中的细节设计,而应用层则负责的是在建立好了链接有了这一套基础上我们将如何处理这些传递上来的数据来满足我们的具体需求。
比如HTTP这一套基于请求和响应的应用层访问就是典型的应用层协议代表,而想要更好的了解这一协议究竟做了什么我们必须要了解HTTP的请求和响应格式。
我们可以看到HTTP请求由四部分组成
如图可以看出这里的报头 = 请求行+请求报头
有效载荷 = 请求正文
从图上可以看出, 报头和有效载荷之间隔着一个空行。
如果我们将整个http协议想象成一个线性的结构, 每一行都是使用\n
来进行分隔的 ,那么如果我们连续读取到**两个\n
**的话就说明报头读取完毕了。
我们的浏览器向服务器发送请求时,起的作用相当于我们之前写的客户端给我们的服务端发送了一段消息,那么我们现在只需要写个服务器,就能查看这段消息的具体格式。
服务器代码如下:
int main()
{
int listen_sock = socket(AF_INET, SOCK_STREAM, 0);
if (listen_sock < 0)
{
cerr << "socket error" << endl;
exit(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;
exit(2);
}
if (listen(listen_sock, 5) < 0)
{
cerr << "listen error" << endl;
exit(3);
}
struct sockaddr_in peer;
memset(&peer, '\0', sizeof(peer));
socklen_t len;
for (;;)
{
int sock = accept(listen_sock, (struct sockaddr*)&peer, &len);
if (sock < 0)
{
cerr << "accept error" << endl;
continue;
}
if (fork() == 0)
{
// father
close(listen_sock);
if (fork() > 0)
{
// father
exit(0);
}
// child
char buff[1024];
recv(sock, buff, sizeof(buff), 0);
cout << "-------- -------- http request begin -------" << endl;
cout << buff << endl;
cout << "-------- -------- http request end -------" << endl;
close(sock);
exit(0);
}
close(sock);
waitpid(-1, nullptr, 0);
}
return 0;
}
运行服务器之后我们就可以使用浏览器访问了:
string http_response = "http/1.0 200 OK\n";
http_response += "Content-type : text/plain\n";
http_response += "\n";
http_response += "HELLO HTTP";
send(sock , http_response.c_str() , http_response.size() , 0);
响应的代码格式如上 ,如果此时网页端访问我们的服务器 ,我们的服务器就能得到这个http的响应:
上面的格式就是我们HTTP响应的格式 ,其中关键字是什么意思,我们将在后文展开。
HTTP请求是由客户端发的 ,因此HTTP请求当中表明的是客户端的http版本。
而HTTP响应是由服务器发的, 因此HTTP响应当中表明的是服务器的http版本。
客户端和服务器双方在进行通信时会交互双方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方法也可以用来上传数据 ,POST方法也可以用来请求数据 ,比如说我们使用百度提交数据的时候我们使用的就是GET方法。
get与post的区别 |
---|
GET方法和POST方法都可以带参 但是它们的传参方式有所不同
从它们的传参方式我们就可以看出 GET方法传递的参数是有限的 ,因为url的长度是有限的。
而POST方法能够传递更多的参数 ,因为正文的限制长度比url的限制长度多得多。
此外我们使用POST传参更加私密一点。
因为当我们使用GET方法传参的时候参数会暴露在url中, 而POST方法则不会。
所以我们可以说POST方法比GET方法安全一点。
但是实际上这两种方法都是不安全的 ,它们都能够被抓包工具抓包从而导致信息泄漏, 想要数据安全我们只能对于这些信息进行加密,后面我们讲的https就是来专门处理这种情况的。
编号 | 类别 | 意义 |
---|---|---|
1XX | Informational(信息性状态码) | 接收的请求正在处理 |
2XX | Success(成功状态码) | 请求正常处理完毕 |
3XX | Redirection(重定向状态码) | 需要进行附加操作以完成请求 |
4XX | Client Error(客户端错误状态码) | 服务器无法处理请求 |
5XX | Server Error(服务器错误状态码) | 服务器处理请求出错 |
下面会介绍一些常用的:
101: 信息请求中
200: OK(访问网页成功的时候网页返回的响应行)
301: 永久重定向(比如果一个老的网站废弃不用了使用一个新的网站 ,那么此时这个网站就可以使用永久重定向 ,如果有人还在访问这个网站就会跳转到新网站)
302 307:临时重定向( 和301永久重定向相比一个是永久的一个是临时的 ,它并不会覆盖掉收藏夹中的老网站)
403 :权限不足(这个常见于我们去实习的时候 ,自己的权限特别低 如果leader丢给你一个文档而你没有观看的权限就会出现这个状态码)
404 :NOT FOUND(常见于资源消失不见(被删除或过期) 又或者说资源根本不存在)
504 :Bad Gateway(常见于服务器出现问题 和客户端无关)
HTTP常见的Header如下:
HTTP实际上是一种无状态协议 ,HTTP的每次请求/响应之间是没有任何关系的 。
但是日常生活中往往有这样的现象,比如你用账号密码登录b站后,下一次在打开b站,你会发现账号是自动登录状态。这是为什么呢?
原来在我们的cookie中保存了这些信息,使得我们自动完成了登录。
Cookie是一种用于在客户端和服务器之间存储小段数据的技术。Cookie通常由服务器发送到客户端,然后客户端将其存储并在后续HTTP请求中将其发送回服务器。这些小段数据可以包含用户的会话信息、首选项、状态信息等,以便在不同HTTP请求之间保持持久性。
内存级别&文件级别 |
---|
cookie就是在浏览器当中的一个小文件, 文件里记录的就是用户的私有信息。
cookie文件可以分为两种: 一种是内存级别的cookie文件, 另一种是文件级别的cookie文件。
如果单纯的使用cookie技术的化那么cookie里面直接存放着的是你的私人信息,如果被人偷取了显然会造成很糟糕的结果。
那么为了避免这样的情况产生,我们可以给你的私人信息套上一层转换机制,这就是我们的SessionID的由来。
当我们首次登录某个网站的时候 ,该网站对应的服务器会生成一个SessionID 。该ID是和用户的信息是一一对应的 ,生成该ID之后网站会将用户的信息保存到服务器的某个特定目录当中。
这样子就算用户的SessionID被盗取了也不会影响用户隐私安全。
其实这样做也不是绝对安全的,黑客也可以直接截取你sessionid的值来以用户的方式访问网站。(说白了就是往一个人身上套了一层皮套,你看不到他的真实长相,但是皮套人和那个真人还是呈现一一对应关系)
具体如何更进一步的使我们的消息更加安全便不是我们主要讨论的能容,在这里也就不深入探讨了。
好终于到了这一部分,前面的文章基本是介绍了一些概念相关的东西,仔细看都能看懂,而这一部分我们将涉及到一定的推理,有点小难度,当然也不必害怕,我会尽可能的将这些繁杂的内容一一道来,好废话不多说,直接进入正文。
Https实质也是一个应用层协议,上面我们说的http协议内容都是按照明文的方式传输的,非常不安全,因此在http的基础下我们的https引入了一层加密层。
什么是加密?
加密就是把 明⽂ (要传输的信息)进⾏⼀系列变换, ⽣成 密⽂ 。
解密就是把 密⽂ 再进⾏⼀系列变换, 还原成 明⽂ 。
在这个加密和解密的过程中, 往往需要⼀个或者多个中间的数据, 辅助进⾏这个过程, 这样的数据称为 密
钥。 (正确发⾳ yue 四声, 不过⼤家平时都读作 yao 四声)
采⽤单钥密码系统的加密⽅法,同⼀个密钥可以同时⽤作信息的加密和解密,这种加密⽅法称为对
称加密,也称为单密钥加密。特征:加密和解密所⽤的密钥是相同的
特点:算法公开、计算量⼩、加密速度快、加密效率⾼
当然密钥这玩意到底是如何加密解密的下面举个简单的例子:
⼀个简单的对称加密, 按位异或
比特就业课假设 明⽂ a = 1234, 密钥 key = 8888
则加密 a ^ key 得到的密⽂ b 为 9834.
然后针对密⽂ 9834 再次进⾏运算 b ^ key, 得到的就是原来的明⽂ 1234.
(对于字符串的对称加密也是同理, 每⼀个字符都可以表⽰成⼀个数字)
当然, 按位异或只是最简单的对称加密. HTTPS 中并不是使⽤按位异或.
需要两个密钥来对明文进⾏加密和解密,这两个密钥是公开密钥(public key,简称公钥)和私有密钥(private key,简称私钥)。
可以用公钥来加密,私钥来解密,当然也可以用私钥来加密,公钥来解密。
公钥和私钥是配对的,最⼤的缺点就是运算速度⾮常慢,⽐对称加密要慢很多
• 数字指纹(数据摘要),其基本原理是利⽤单向散列函数(Hash函数)对信息进⾏运算,⽣成⼀串固定⻓度
的数字摘要。数字指纹并不是⼀种加密机制,但可以⽤来判断数据有没有被窜改。
• 摘要常见算法:有MD5、SHA1、SHA256、SHA512等,算法把⽆限的映射成有限,因此可能会有
碰撞(两个不同的信息,算出的摘要相同,但是概率非常低)
• 摘要特征:和加密算法的区别是,摘要严格意义不是加密,因为没有解密,只不过从摘要很难反推
原信息,通常⽤来进⾏数据对⽐
简单例子:
首先,选择一个哈希函数。常见的哈希函数包括MD5、SHA-1、SHA-256等。这里我们使用一个简单的示例,假设我们有一个自定义的哈希函数,它将字符串中的每个字符转换为ASCII码,然后将这些ASCII码值相加,最后取余数,以得到一个固定长度的数字摘要。
通信双⽅都各⾃持有同⼀个密钥X
引⼊对称加密之后, 即使数据被截获, 由于⿊客不知道密钥是啥, 因此就⽆法进⾏解密, 也就不知道请求
的真实内容是啥了。
但事情没这么简单. 服务器同⼀时刻其实是给很多客⼾端提供服务的. 这么多客⼾端, 每个⼈⽤的秘钥都
必须是不同的。
当然比较理想的做法是下面这幅图:
但是如果直接把密钥明⽂传输, 那么⿊客也就能获得密钥了~~ 此时后续的加密操作就形同虚设了
这种情况看似很安全,其实也不安全(后面会一一道来),而且效率太低。
当然针对上面的情况来说已经很接近正确答案了,但是仍然会存在安全问题。
在方案2/3/4中,第一步都是将公钥发送给对方,但如果此时黑客就进行拦截呢?
比如:中间人劫持数据报文,提取公钥S并保存好,然后将被劫持报⽂中的公钥S替换成为自己的公钥M,
并将伪造报文发给客户端
如果这样会发生什么,我们以方案4为例子为大家画一幅图进行讲解。
为了解决上述问题我们引进了证书这一概念。
CA认证
服务端在使⽤HTTPS前,需要向CA机构申领⼀份数字证书,数字证书⾥含有证书申请者信息、公钥信
息等。服务器把证书传输给浏览器,浏览器从证书⾥获取公钥就⾏了,证书就如⾝份证,证明服务端
公钥的权威性
当服务端申请CA证书时候,CA机构会对该服务端进行审核,并专门为该网站形成数字签名,过程如下:
1.CA机构拥有非对称加密的私钥A和公钥A’
2.CA机构对服务端申请的证书明文数据进行hash
3.然后对数据摘要用CA的密钥A’加密,形成签名
图示:
为什么签名不直接加密,⽽是要先hash形成摘要?
• 缩⼩签名密⽂的⻓度,加快数字签名的验证签名的运算速度