目录
1.认识URL
2.urlencode和urldecode
3.HTTP基本特征
4.HTTP协议格式
5.HTTP方法?
6.HTTP的状态码
?
1.认识URL
URL(Uniform Resource Lacator)叫做统一资源定位符,也就是我们通常所说的网址,是因特网的万维网服务程序上用于指定信息位置的表示方法。
一个URL大致由如下几部分构成:
?????????????????
?(1)协议方案名
HTTP是协议名称,表示请求时需要使用的协议,通常使用的是HTTP协议或安全协议HTTPS。HTTPS是以安全为目标的HTTP通道,在HTTP的基础上通过传输加密和身份认证保证了传输过程的安全性。
常见应用层协议:
- DNS(Domain Name System)协议:域名系统。
- FTP(File Transfer Protocol)协议:文件传输协议。
- TELNET(Telnet)协议:远程终端协议。
- HTTP(Hyper Text Transfer Protocol)协议:超文本传输协议。
- HTTPS(Hyper Text Transfer Protocol over SecureSocket Layer)协议:安全数据传输协议。
- SMTP(Simple Mail Transfer Protocol)协议:电子邮件传输协议。
- POP3(Post Office Protocol - Version 3)协议:邮件读取协议。
- SNMP(Simple Network Management Protocol)协议:简单网络管理协议。
- TFTP(Trivial File Transfer Protocol)协议:简单文件传输协议。
?????????????????????????
(2)登陆信息
user:pass是登录认证信息,包括登录用户的用户名和密码。虽然登录认证信息可以在URL中体现出来,但绝大多数URL的这个字段都是被省略的,因为登录信息可以通过其他方案交付给服务器? ?
(3)服务器地址
- www.example.jp是服务器地址,也叫做域名。我们用IP地址标识公网内的一台主机,但IP地址本身并不适合给用户看,人更容易记住字符串,计算机更容易记住数字.?
- 实际我们可以认为域名和IP地址是等价的,在计算机当中使用的时候既可以使用域名,也可以使用IP地址。但URL呈现出来是可以让用户看到的,因此URL当中是以域名的形式表示服务器地址的。
(4)端口号
- 80是服务器端口号。HTTP协议和套接字编程一样都是位于应用层的,在进行套接字编程时我们需要给服务器绑定对应的IP和端口,而这里的应用层协议也同样需要有明确的端口号。
- HTTP:80 , HTTPS:443 ,SSH : 22
- 听有的URL都是要向服务器发起请求的,客户端会根据你所采用的协议自动的向服务器发送特定端口的请求。我们把这些众所周知的,明确的,协议本身强绑的的端口号称之为经被某种协议包办了,内置的端口,是不会被你绑的。
?
(5)带层次的文件路径
- /dir/index.html是要访问的资源所在的路径。访问服务器的目的是获取服务器上的某种资源,通过前面的域名和端口已经能够找到对应的服务器进程了,此时要做的就是指明该资源所在的路径。?
- /基本不是根目录,是web根目录 ,它可以是Linux中任何—个路径。这里可能是一长串路径之后的路径,如果它是根目录,我可以通过URL访问根路径下的所有内容,这是非常危险的。
(6)查询字符串?
- uid=1是请求时提供的额外的参数,这些参数是以键值对的形式,通过&符号分隔开的。
- 在百度上面搜索HTTP,此时可以看到URL中有很多参数,而在这众多的参数当中有一个参数wd(word),表示的就是我们搜索时的搜索关键字wd = MIUI
- 因此双方在进行网络通信时,是能够通过URL进行用户数据传送的
?
(7)片段标识符?
ch1是片段标识符,是对资源的部分补充。
当我们在看组图的时候,URL当中就会出现片段标识符
?在翻看组图时,这个片段标识符就会发生变化。
?
?????????????????????????????????????????
2.urlencode和urldecode
如果在搜索关键字当中出现了像/?: 这样的字符,由于这些字符已经被URL当作特殊意义理解了,因此URL在呈现时会对这些特殊字符进行转义。
转义的规则如下:
- 将需要转码的字符转为十六进制,然后从右到左,取4位(不足4位直接处理),每两位做一位,前面加上%,编码成%XY格式。
?
(1)举例:
比如当我们搜索C++时,由于 “+” 加号在URL当中也是特殊符号,而 “+” 字符转为十六进制后的值就是0x2B因此一个 “+” 就会被编码成一个 %2B
?
(2)urlencode(编码)和urldecode(解码)
- URL当中除了会对这些特殊符号做编码,对中文也会进行编码。
- 实际当服务器拿到对应的URL后,也需要对编码后的参数进行解码,此时服务器才能拿到你想要传递的参数,解码实际就是编码的逆过程。?
????????????????????????
3.HTTP基本特征
(1)无连接?
TCP建立链接和http无关,一旦链接建立好,http直接向对方发送http request即可
- http是建立在tcp之上的,http本身不关心tcp所有的通信细节,tcp面向连接和http无关。不管tcp链或者不链,只要我把数据可靠的发过去就觉得OK。所以http并不关心底层是怎么做的,链不连,连多少不关心。所以http的无连接性和tcp的连接性是无关的。
- http本身无链接重点体现在一旦tcp链接成功,不需要http在应用层再继续建立通信链接。如果今天底层把tcp建立好,http的请求直接发,也就意味着对于接收方来讲,我不需要针对于http通信建立http链接,不需要协商关于http的细节。只要tcp信道建立好了,你有http请求,直接发送给我就完了。
?
(2)无状态
http本身是无状态,并不会记录任何用户信息,http本身只进行基本的request和response响应
- http无状态最典型特征是给用户用的。所谓的无状态其实是服务器和客户端双方在你访问的时候并不记得你来过。http本身并不关心用户有哪些数据传上去了,现在是什么状态,http协议只关心我们用户发送了一个request,服务器就应该给我构建—个response然后给你解释出来。
- 记录基本信息的技术是cookie+session
?
(3)简单快速?
- 基于短连接进行文本(html,img,css,js ...)传输的http/1.0版本 和 http/1.1长连接
- 简单: request和response? 快速:底层是tcp
4.HTTP协议格式
- HTTP是基于请求和响应的应用层服务,作为客户端,你可以向服务器发起request,服务器收到这个request后,会对这个request做数据分析,得出你想要访问什么资源,然后服务器再构建response,完成这一次HTTP的请求。这种基于request&response这样的工作方式,我们称之为cs或bs模式,其中c表示client,s表示server,b表示browser。
- 由于HTTP是基于请求和响应的应用层访问,因此我们必须要知道HTTP对应的请求格式和响应格式,
????????????????
HTTP请求格式
?????????????????
?(1)HTTP请求由以下四部分组成:
- 请求行:[请求方法]+[url]+[http版本]
- 请求报头:请求的属性,这些属性都是以key: value的形式按行陈列的。
- 空行:遇到空行表示请求报头结束。
- 请求正文:请求正文允许为空字符串,如果请求正文存在,则在请求报头中会有一个Content-Length属性来标识请求正文的长度。
(2)前面三部分是一般是HTTP协议自带的,是由HTTP协议自行设置的,而请求正文一般是用户的相关信息或数据,如果用户在请求时没有信息要上传给服务器,此时请求正文就为空字符串。 ?
(3)如何将HTTP请求的报头与有效载荷进行分离?
- 当应用层收到一个HTTP请求时,它必须想办法将HTTP的报头与有效载荷进行分离。对于HTTP请求来讲,这里的请求行和请求报头就是HTTP的报头信息,而这里的请求正文实际就是HTTP的有效载荷。
- 我们可以根据HTTP请求当中的空行来进行分离,当服务器收到一个HTTP请求后,是按照行进行读取的,如果读取到空行则说明已经将报头读取完毕,实际HTTP请求当中的空行就是用来分离报头和有效载荷的。
- 如果将HTTP请求想象成一个大的线性结构,此时每行的内容都是用\r\n隔开的,因此在读取过程中,如果连续读取到了两个\r\n,就说明已经将报头读取完毕了,后面剩下的就是有效载荷了。
?
(4)获取浏览器HTTP请求
- 在网络协议栈中,应用层的下一层叫做传输层,而HTTP协议底层通常使用的传输层协议是TCP协议,因此我们可以用套接字编写一个TCP服务器,然后启动浏览器访问我们的这个服务器。
- 由于我们的服务器是直接用TCP套接字读取浏览器发来的HTTP请求,此时在服务端没有应用层对这个HTTP请求进行过任何解析,因此我们可以直接将浏览器发来的HTTP请求进行打印输出,此时就能看到HTTP请求的基本构成。
????????????????
①简单的TCP服务器,这个服务器要做的就是把浏览器发来的HTTP请求进行打印
#include <iostream>
#include <string>
#include <fstream>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
int listen_sock = socket(AF_INET, SOCK_STREAM, 0);
if(listen_sock < 0){
std::cout << "socket error" << std::endl;
return 1;
}
int flag = 1;
setsockopt(listen_sock,SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag));
struct sockaddr_in local;
memset(&local, 0, sizeof(local));
local.sin_family = AF_INET;
local.sin_port = htons(8080);
local.sin_addr.s_addr = INADDR_ANY;
if(bind(listen_sock, (struct sockaddr*)&local, sizeof(local)) < 0){
std::cout << "bind error" << std::endl;
return 2;
}
if(listen(listen_sock, 5) < 0){
std::cout << "listen error" <<std::endl;
return 3;
}
struct sockaddr_in peer;
for(;;){
socklen_t len = sizeof(peer);
int sock = accept(listen_sock, (struct sockaddr*)&peer, &len);
if(sock < 0){
std::cout << "accept error" <<std::endl;
continue;
}
if(fork() == 0){
if(fork() > 0) exit(0);
close(listen_sock);
//read http request
char buffer[1024];
recv(sock, buffer, sizeof(buffer), 0);
std::cout << "############### http request begin ###############"<<std::endl;
std::cout << buffer << std::endl;
std::cout << "############### http request end ###############"<<std::endl;
close(sock);
exit(0);
}
close(sock);
waitpid(-1, nullptr, 0);
}
}
?????????????????
②运行服务器程序后,然后用浏览器进行访问,此时我们的服务器就会收到浏览器发来的HTTP请求,并将收到的HTTP请求进行打印输出。?
- 浏览器向我们的服务器发起HTTP请求后,因为我们的服务器没有对其进行响应,此时浏览器就会认为服务器没有收到,然后再不断发起新的HTTP请求,因此虽然我们只用浏览器访问了一次,但会受到多次HTTP请求。
- 由于浏览器发起请求时默认用的就是HTTP协议,因此我们在浏览器的url框当中输入网址时可以不用指明HTTP协议。
- url当中的/不能称之为我们云服务器上根目录,这个/表示的是web根目录,这个web根目录可以是你的机器上的任何一个目录,这个是可以自己指定的,不一定就是Linux的根目录。
?????????????????
③请求行当中的url一般是不携带域名以及端口号的,因为在请求报头中的Host字段当中会进行指明,请求行当中的url表示你要访问这个服务器上的哪一路径下的资源。如果浏览器在访问我们的服务器时指明要访问的资源路径,那么此时浏览器发起的HTTP请求当中的url也会跟着变成该路径。?
而请求报头当中全部都是以Key : value 形式按行陈列的各种请求属性,请求属性陈列完后紧接着的就是一个空行,空行后的就是本次HTTP请求的请求正文,此时请求正文为空字符串,因此这里有两个空行。?
?
HTTP响应格式
?
(1)HTTP响应由以下四部分组成:
- 状态行:[http版本]+[状态码]+[状态码描述]
- 响应报头:响应的属性,这些属性都是以key: value的形式按行陈列的。
- 空行:遇到空行表示响应报头结束。
- 响应正文:响应正文允许为空字符串,如果响应正文存在,则响应报头中会有一个Content-Length属性来标识响应正文的长度。比如服务器返回了一个html页面,那么这个html页面的内容就是在响应正文当中的。
(2)如何将HTTP响应的报头与有效载荷进行分离?
对于HTTP响应来讲,这里的状态行和响应报头就是HTTP的报头信息,而这里的响应正文实际就是HTTP的有效载荷。与HTTP请求相同,当应用层收到一个HTTP响应时,也是根据HTTP响应当中的空行来分离报头和有效载荷的。当客户端收到一个HTTP响应后,就可以按行进行读取,如果读取到空行则说明报头已经读取完毕。
?????????????????
(3)构建HTTP响应给浏览器
- 服务器读取到客户端发来的HTTP请求后,需要对这个HTTP请求进行各种数据分析,然后构建成对应的HTTP响应发回给客户端。而我们的服务器连接到客户端后,实际就只读取了客户端发来的HTTP请求就将连接断开了。
- 我们可以构建一个HTTP请求给浏览器,鉴于现在还没有办法分析浏览器发来的HTTP请求,这里我们可以给浏览器返回一个固定的HTTP响应。我们就将当前服务程序所在的路径作为我们的web根目录,我们可以在该目录下创建一个html文件,然后编写一个简单的html作为当前服务器的首页。
①代码,当浏览器向服务器发起HTTP请求时,不管浏览器发来的是什么请求,我们都将这个网页响应给浏览器,此时这个html文件的内容就应该放在响应正文当中,我们只需读取该文件当中的内容,然后将其作为响应正文即可。
#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;
#define PAGE "index.html" //网站首页
//读取index.html文件
ifstream in(PAGE);
if (in.is_open()){
in.seekg(0, in.end);
int len = in.tellg();
in.seekg(0, in.beg);
char* file = new char[len];
in.read(file, len);
in.close();
//构建HTTP响应
string status_line = "http/1.1 200 OK\n"; //状态行
string response_header = "Content-Length: " + to_string(len) + "\n"; //响应报头
string blank = "\n"; //空行
string response_text = file; //响应正文
string response = status_line + response_header + blank + response_text; //响应报文
//响应HTTP请求
send(sock, response.c_str(), response.size(), 0);
delete[] file;
}
close(sock);
exit(0);
}
//爷爷进程
close(sock);
waitpid(-1, nullptr, 0); //等待爸爸进程
}
return 0;
}
?????????????????
?②当浏览器访问我们的服务器时,服务器会将这个index.html文件响应给浏览器,而该html文件被浏览器解释后就会显示出相应的内容。
?????????????????????????
?③以通过telnet命令来访问我们的服务器,此时也是能够得到这个HTTP响应的
- ?实际我们在进行网络请求的时候,如果不指明请求资源的路径,此时默认你想访问的就是目标网站的首页,也就是web根目录下的index.html文件。
- 由于只是作为示例,我们在构建HTTP响应时,在响应报头当中只添加了一个属性信息Content-Length,表示响应正文的长度,实际HTTP响应报头当中的属性信息还有很多。
?????????????????
(4)HTTP为什么要交互版本信息?
- HTTP请求当中的请求行和HTTP响应当中的状态行,当中都包含了http的版本信息。其中HTTP请求是由客户端发的,因此HTTP请求当中表明的是客户端的http版本,而HTTP响应是由服务器发的,因此HTTP响应当中表明的是服务器的http版本。
- 客户端和服务器双方在进行通信时会交互双方http版本,主要还是为了兼容性的问题。因为服务器和客户端使用的可能是不同的http版本,为了让不同版本的客户端都能享受到对应的服务,此时就要求通信双方需要进行版本协商。
- 客户端在发起HTTP请求时告诉服务器自己所使用的http版本,此时服务器就可以根据客户端使用的http版本,为客户端提供对应的服务,而不至于因为双方使用的http版本不同而导致无法正常通信。因此为了保证良好的兼容性,通信双方需要交互一下各自的版本信息。
?
?
????????????????????????
5.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方法
(1)GET方法一般用于获取某种资源信息,而POST方法一般用于将数据上传给服务器。但实际我们上传数据时也有可能使用GET方法.
(2)GET方法和POST方法都可以带参:
- GET方法是通过url传参的。
- POST方法是通过正文传参的。
(3)从GET方法和POST方法的传参形式可以看出,POST方法能传递更多的参数,因为url的长度是有限制的,POST方法通过正文传参就可以携带更多的数据。
(4)因为POST方法不会将你的参数回显到url当中,? 使用POST方法传参更加私密,此时也就不会被别人轻易看到。不能说POST方法比GET方法更安全,因为POST方法和GET方法实际都不安全,要做到安全只能通过加密来完成。
????????????????
Postman演示GET和POST的区别?
(1)如果访问我们的服务器时使用的是GET方法,此时应该通过url进行传参,可以在Params下进行参数设置,因为Postman当中的Params就相当于url当中的参数,你在设置参数时可以看到对应的url也在随之变化。?
?????????????????
(2)此时在我们的服务器收到的HTTP请求当中,可以看到请求行中的url就携带上了我们刚才在Postman当中设置的参数。?
?????????????????
(3如果我们使用的是POST方法,此时就应该通过正文进行传参,可以在Body下进行参数设置,在设置时可以选中Postman当中的raw方式传参,表示原始传参,也就是你输入的参数是什么样的实际传递的参数就是什么样的。?
?????????????????????????
?(4)服务器收到的HTTP请求的请求正文就不再是空字符串了,而是我们通过正文传递的参数;此时响应正文不为空字符串,因此响应报头当中出现了Content-Length属性,表示响应正文的长度
????????????????
TCP套接字演示GET和POST区别?
(1) 演示GET方法和POST方法传参的区别,就需要让浏览器提交参数,此时我们可以在index.html当中再加入两个表单,用作用户名和密码的输入,然后再新增一个提交按钮,此时就可以让浏览器提交参数了。
- 可以通过修改表单当中的method属性指定参数提交的方法,还有一个属性叫做action,表示想把这个表单提交给服务器上的哪个资源。
?????????????????
(2)此时当我们用浏览器访问我们的服务器时,就会显示这两个表单。
?
?????????????????
(3)用的GET方法提交参数的,当我们填充完用户名和密码进行提交时,我们的用户名和密码就会自动被同步到url当中?
?????????????????
(4)同时在服务器这边也通过url收到了刚才我们在浏览器提交的参数。?
?????????????????????????
(5)如果我们将提交表单的方法改为POST方法,此时当我们填充完用户名和密码进行提交时,对应提交的参数就不会在url当中体现出来,而会通过正文将这两个参数传递给了服务器。?
?????????????????????????
?(6)用户名和密码就通过正文的形式传递给服务器了。
?????????????????
(7)小结
- 当我们使用GET方法时,我们提交的参数会回显到url当中,因此GET方法一般是处理数据不敏感的。
- 如果你要传递的数据比较私密的话你一定要用POST方法,倒不是因为POST方法更安全,实际上GET和POST方法传参时都是明文传送,所以都不安全,但是POST方法更私密,因为POST是通过正文传参的,不会将参数立马回显到浏览器的url框当中的,所以相对更私密。
?
?????????????????????????
6.HTTP的状态码
(1)HTTP的状态码如下:?
最常见的状态码,比如200(OK),404(Not Found),403(Forbidden请求权限不够),302(Redirect),504(Bad Gateway)。 ????????????????
(2)Redirection(重定向状态码)
- 重定向就是通过各种方法将各种网络请求重新定个方向转到其它位置,此时这个服务器相当于提供了一个引路的服务。
- 重定向又可分为临时重定向和永久重定向,其中状态码301表示的就是永久重定向,而状态码302和307表示的是临时重定向。
- 临时重定向和永久重定向本质是影响客户端的标签,决定客户端是否需要更新目标地址。如果某个网站是永久重定向,那么第一次访问该网站时由浏览器帮你进行重定向,但后续再访问该网站时就不需要浏览器再进行重定向了,此时你访问的直接就是重定向后的网站。而如果某个网站是临时重定向,那么每次访问该网站时如果需要进行重定向,都需要浏览器来帮我们完成重定向跳转到目标网站。
(3)临时重定向演示
- 进行临时重定向时需要用到Location字段,Location字段是HTTP报头当中的一个属性信息,该字段表明了你所要重定向到的目标网站。
- 这里要演示临时重定向,可以将HTTP响应当中的状态码改为307,然后跟上对应的状态码描述,此外,还需要在HTTP响应报头当中添加Location字段,这个Location后面跟的就是你需要重定向到的网页
①代码
#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.mi.com/\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后面的网址,然后继续自动对该网站继续发起请求,此时就完成了页面跳转这样的功能,这样就完成了重定向功能;此时当浏览器访问我们的服务器时,就会立马跳转到小米的首页。
????????????????????????
- Content-Type:数据类型(text/html等)。
- Content-Length:正文的长度。
- Host:客户端告知服务器,所请求的资源是在哪个主机的哪个端口上。
- User-Agent:声明用户的操作系统和浏览器的版本信息。
- Referer:当前页面是哪个页面跳转过来的。
- Location:搭配3XX状态码使用,告诉客户端接下来要去哪里访问。
- Cookie:用于在客户端存储少量信息,通常用于实现会话(session)的功能。
?(1)Host
- Host字段表明了客户端要访问的服务的IP和端口,比如当浏览器访问我们的服务器时,浏览器发来的HTTP请求当中的Host字段填的就是我们的IP和端口。但客户端不就是要访问服务器吗?为什么客户端还要告诉服务器它要访问的服务对应的IP和端口?
- 因为有些服务器实际提供的是一种代理服务,也就是代替客户端向其他服务器发起请求,然后将请求得到的结果再返回给客户端。在这种情况下客户端就必须告诉代理服务器它要访问的服务对应的IP和端口,此时Host提供的信息就有效了。
(2)User-Agent
- User-Agent代表的是客户端对应的操作系统和浏览器的版本信息。
- 比如当我们用电脑下载某些软件时,它会自动向我们展示与我们操作系统相匹配的版本,这实际就是因为我们在向目标网站发起请求的时候,User-Agent字段当中包含了我们的主机信息(LInux,windows,Android),此时该网站就会向你推送相匹配的软件版本。
(3)Referer
Referer代表的是你当前是从哪一个页面跳转过来的。Referer记录上一个页面的好处一方面是方便回退,另一方面可以知道我们当前页面与上一个页面之间的相关性。
(4)Keep-Alive(长连接)
- HTTP/1.0是通过request&response的方式来进行请求和响应的,HTTP/1.0常见的工作方式就是客户端和服务器先建立链接,然后客户端发起请求给服务器,服务器再对该请求进行响应,然后立马端口连接。
- 但如果一个连接建立后客户端和服务器只进行一次交互,就将连接关闭,就太浪费资源了,因此现在主流的HTTP/1.1是支持长连接的。所谓的长连接就是建立连接后,客户端可以不断的向服务器一次写入多个HTTP请求,而服务器在上层依次读取这些请求就行了,此时一条连接就可以传送大量的请求和响应,这就是长连接。
- 如果HTTP请求或响应报头当中的Connect字段对应的值是Keep-Alive,就代表支持长连接。
????????????????? ? ? ? ? ??
|