@[TOC]
HTTP
超文本传输协议,是一个简单的请求-响应协议,HTTP通常运行在TCP之上
URL(Uniform Resource Lacator)
一资源定位符,也就是通常所说的网址,是因特网的万维网服务程序上用于指定信息位置的表示方法。
URL的构成:
协议方案名:
- http:// 表示的是协议名称,表示请求时需要使用的协议,通常使用的是HTTP协议或安全协议HTTPS。HTTPS是以安全为目标的HTTP通道,在HTTP的基础上通过传输加密和身份认证保证了传输过程的安全性
登录信息: - usr:pass表示的是登录认证信息,包括登录用户的用户名和密码。虽然登录认证信息可以在URL中体现出来,但绝大多数URL的这个字段都是被省略的,因为登录信息可以通过其他方案交付给服务器
服务器地址:
- www.example.jp表示的是服务器地址,也叫做域名,例:www.baidu.com
- 可以通过ping命令,获得www.baidu.com这个域名解析后的IP地址
服务器端口号:
- HTTP协议和套接字编程一样都是位于应用层的,在进行套接字编程时我们需要给服务器绑定对应的IP和端口,而这里的应用层协议也同样需要有明确的端口号
- 常见协议对应的端口号:HTTP :80 、HTTPS: 443、SSH: 22
在使用某种协议时实际是不需要指明该协议对应的端口号的,因此在URL当中,服务器的端口号一般也是被省略的
带层次的文件路径:
- /dir/index.htm表示的是要访问的资源所在的路径。访问服务器的目的是获取服务器上的某种资源,通过前面的域名和端口能找到对应的服务器进程了,此时要做的就是指明该资源所在的路径。
查询字符串:
uid=1表示的是请求时提供的额外的参数,这些参数是以键值对的形式,通过&符号分隔开的。
比如我们在bing上面搜索HTTP,此时可以看到URL中有很多参数,而在这众多的参数当中有一个参数q,表示的就是我们搜索时的搜索关键字q=http
因此双方在进行网络通信时,是能够通过URL进行用户数据传送的。
片段标识符:
- ch1表示的是片段标识符,是对资源的部分补充。
HTTP协议格式
HTTP请求由以下四部分组成:
请求行:[请求方法]+[url]+[http版本]
请求报头:请求的属性,这些属性都是以key: value的形式按行陈列的。
空行:遇到空行表示请求报头结束。
请求正文:请求正文允许为空字符串,如果请求正文存在,则在请求报头中会有一个Content-Length属性来标识请求正文的长度
- 请求行、请求报头、空行是一般是HTTP协议自带的,是由HTTP协议自行设置的,而请求正文一般是用户的相关信息或数据,如果用户在请求时没有信息要上传给服务器,此时请求正文就为空字符串
如何将HTTP请求的报头与有效载荷进行分离?
对于应用层收到的一个HTTP请求来说,这里的请求行和请求报头就是HTTP的报头信息,而这里的请求正文实际就是HTTP的有效载荷。
根据HTTP请求当中的空行来进行分离,当服务器收到一个HTTP请求后,就可以按行进行读取,如果读取到空行则说明已经将报头读取完毕,后面剩下的就是有效载荷了
HTTP的方法
HTTP常见的方法如下:
GET : 获取资源 支持的HTTP协议版本1.0、1.1
GET方法通过URL进行提参,参数数量受限的,不私密
POST: 传输实体主体 支持的HTTP协议版本1.0、1.1
POST方法支持参数提交,用请求的正文来提交参数,和GET相比,POST更加私密一些
在index.html当中再加入两个表单,用作用户名和密码的输入
将提交表单的方法改为POST方法,此时当我们填充完用户名和密码进行提交时,对应提交的参数就不会在url当中体现出来,而会通过正文将这两个参数传递给了服务器。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<form action="/a/b/hello.html" method="post">
name: <input type="text" name="name"><br>
password: <input type="password" name="passwd"><br>
<input type="submit" value="提交">
<!-- </form>
<h1>这个是我们的首页</h1>
<h1>这个是我们的首页</h1>
<h1>这个是我们的首页</h1>
<h1>这个是我们的首页</h1>
<h1>这个是我们的首页</h1> -->
<!-- <img src="/image/1.png" alt="这是一直猫" width="100" height="100"> 根据src向我们的服务器浏览器自动发起二次请求 -->
<!-- <img src="/image/2.jpg" alt="这是花"> -->
</body>
</html>
打开浏览器的开发者工具,可以看出此时表单已经是POST了
将name填写为cxq ,密码123456
[cxq@iZwz9fjj2ssnshikw14avaZ lesson45]$ ./HttpServer 8080
HTTP的状态码
Redirection(重定向状态码)
重定向状态码本质:让服务器指导浏览器,让浏览器访问新的地址
重定向又可分为临时重定向和永久重定向,其中状态码301表示的就是永久重定向,而状态码302和307表示的是临时重定向。
临时重定向:
临时重定向时需要用到Location字段,Location字段是HTTP报头当中的一个属性信息,该字段表明了你所要重定向到的目标网站。
将HTTP响应当中的状态码改为302,然后跟上对应的状态码描述,此外,还需要在HTTP响应报头当中添加Location字段,这个Location后面跟的就是你需要重定向到的网页,比如我们这里将其设置为https://www.qq.com
测试HTTP的临时重定向
HTTP常见的Header
Content-Type:数据类型(text/html等)。
Content-Length:正文的长度。
Host:客户端告知服务器,所请求的资源是在哪个主机的哪个端口上。
User-Agent:声明用户的操作系统和浏览器的版本信息。
Referer:当前页面是哪个页面跳转过来的。
Location:搭配3XX状态码使用,告诉客户端接下来要去哪里访问。
Cookie:用于在客户端存储少量信息,通常用于实现会话(session)的功能
Keep-Alive(长连接)
HTTP/1.0是通过request&response的方式来进行请求和响应的,HTTP/1.0常见的工作方式就是客户端和服务器先建立链接,然后客户端发起请求给服务器,服务器再对该请求进行响应,然后立马端口连接。
短连接:一个连接建立后,客户端和服务器只进行一次交互,就将连接关闭
长连接:现在主流的HTTP/1.1是支持长连接的。建立连接后,客户端可以不断的向服务器一次写入多个HTTP请求,服务器在上层依次读取这些请求,此时一条连接就可以传送大量的请求和响应
如果HTTP请求或响应报头当中的Connect字段对应的值是Keep-Alive,就代表支持长连接。
Content-Type:
void HandlerHttp(int sockfd)
{
char buffer[10240];
// 接受请求
ssize_t n = recv(sockfd, buffer, sizeof(buffer) - 1, 0); // 与read函数类似
if (n > 0) // 接受成功
{
buffer[n] = 0; // 将buffer当成字符串处理
std::cout << buffer << std::endl; // 假设我们读取到的就是一个完整的,独立的http 请求
HttpRequest req;
// 将Http请求反序列化
req.Deserialize(buffer);
req.Parse();
// 构建响应
std::string text;
text = ReadHtmlContent(req.file_path);
// 打开文件失败
bool ok = true; // 标致位
if (text.empty())
{
ok = false;
std::string err_html = wwwroot;
err_html += "/";
err_html += "err.html";
text = ReadHtmlContent(err_html);
}
std::string response_line;
if (ok)
{
response_line = "HTTP/1.0 200 OK\r\n";
}
else
{
response_line = "HTTP/1.0 404 Not Found\r\n";
}
std::string response_header = "Content-Length:";
response_header += std::to_string(text.size()); // Content-Length:11
response_header += "\r\n";
//根据文件后缀来添加对应
response_header += "Content-Type: ";
response_header += this->SuffixToDesc(req.suffix);
response_header += "\r\n";
std::string blank_line = "\r\n"; // 空行,表示头部和正文的分隔
std::string response = response_line;
response += response_header;
response += blank_line;
response += text;
// 返回响应
send(sockfd, response.c_str(), response.size(), 0); // send函数类似于write
}
close(sockfd);
}
Cookie:
第一次登录某个网站时,是需要输入账号和密码进行身份认证,此时如果服务器经过数据比对后判定你是一个合法的用户,为了后续在进行某些网页请求时不用重新输入账号和密码,此时服务器就会进行Set-Cookie的设置。(Set-Cookie也是HTTP报头当中的一种属性信息)
当认证通过,并在服务端进行Set-Cookie设置后,服务器在对浏览器进行HTTP响应时就会将这个Set-Cookie响应给浏览器。而浏览器收到响应后会自动提取出Set-Cookie的值,将其保存在浏览器的cookie文件当中,下一次浏览器发起http请求,http请求中就会携带Cookie文件中的内容,所以后续在进行某些网页请求时就不用再次输入账号和密码
cookie文件可以分为两种
1、内存级别的cookie文件
将浏览器关掉后再打开,访问之前登录过的网站,如果需要重新输入账号和密码,说明浏览器当中保存的cookie信息是内存级别
2、文件级别的cookie文件
将浏览器关掉后再打开,访问之前登录过的网站,如果不需要重新输入账号和密码,说明浏览器当中保存的cookie信息是文件级别
Cookie原理:
[cxq@iZwz9fjj2ssnshikw14avaZ lesson45]$ ./HttpServer 8080
SessionID:
第一次登录某个网站输入账号和密码后,服务器认证成功后还会服务端生成一个对应的SessionID,这个SessionID与用户信息是不相关的。服务器会将所有登录用户的SessionID值统一维护起来
服务端在对浏览器进行HTTP响应时,就会将这个生成的SessionID值响应给浏览器。浏览器收到响应后会自动提取出SessionID的值,将其保存在浏览器的cookie文件当中。后续浏览器访问服务器时,浏览器发起的HTTP请求当中会自动携带上这个SessionID
引入SessionID后,用户登录的账号信息由服务器去维护的,比较安全