本章Gitee地址:http
1. 域名和url
在浏览器的url
输入框当中,我们输入类似https://www.4399.com/
这样的域名,就能够直接访问到这个客户端。
从技术上我们客户端要访问服务端,是直接通过ip:port
直接访问的,但是日常中,并不会采用这种方式,而是采用上面域名。
为什么不拿
ip:port
直接访问呢?因为这样用户体验不好,安全性不高。比如说给我们一个
ip
地址,我们并不知道它只指哪儿,擅自点击可能会出问题,而使用域名,我们一看到就能知道它是谁的官网,例如www.baidu.com
、www.aliyun.con
不过这个域名,是会被域名解析这个服务解析成ip
地址的,所以最终访问,也是通过ip
地址进行访问的。
访问是通过
ip:port
,但这里并没端口号,http://45.113.192.102/
这里会在前面默认加上http
或者https
这个前缀,这个前缀就是http
、https
协议,这种知名的服务,它们的端口号一般是固定的:
http
端口一般是80
https
端口一般是443
而我们平时说说的网址,例如https://blog.csdn.net/Dirty_artist
这种叫做url
,域名表示互联网中唯一一台主机,然后协议+默认端口号就能找到唯一的服务。
这里如果搜索的内容,与它们特定的分隔符冲突了,此时要求
BS(Browser/Server)
双方要进行编码encode
和解码decode
这里会将冲突的字符转成对应的ASCII码表的16进制,然后从右向左取4位,每2位做一位,前面加上
%
,编码成%XY
格式。例如:假设冲突字符的ASCII码是
65
,其十六进制表示是41
,相应的二进制表示是01000001
。我们从右向左取出四位,得到0100
和0001
。然后,将每两位二进制数转换为对应的十六进制数,得到4
和1
,最后将每个新的十六进制数前面加上%
符号,得到编码后的格式为%41
。
2. http请求和响应
http
的请求和响应是以行为分式
http request
:
-
请求行,空格为分隔符:
- Method(请求方法):大部分都是
GET
或者POST
- URL:需要请求的资源
- HTTP Version(请求协议版本):1.0、1.1(常见)、2.0
- Method(请求方法):大部分都是
-
请求报头,这里由多行内容构成,每一行都是
http
的请求属性,大多数都是Key:Value
式的 -
空行:这里也涉及到需要之后将报文和报头分开,
http
协议规定在报头和正文中间,用空行为分隔符 -
请求正文:正文部分需要读多少内容,报头里面有一个
Content-Length:xxx
,表明需要在请求正文读多少内容
http response
:
它的格式和request
格式几乎一样
3. 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 | fiddler的原理类似 | 1.1 |
其中
GET
方法用的是最多的,其次就是POST
,其他的方法要么是过期被淘汰,要么人家web
服务器把其他方法关闭,不让用。
我们想服务器提交数据,就是采用的POST
方法,例如我们在搜索引擎检索,这个在前端上叫做表单。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<form action="1/2/ikun.html" method="get">
username: <input type="text" name="name"><br>
password: <input type="password" name="passwd"><br>
<input type="submit" value="提交">
</form>
</body>
</html>
这里发现,如果提供
get
方法提交的时候,参数是通过url
提交的
将get
方法改为post
方法
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<form action="1/2/ikun.html" method="post">
username: <input type="text" name="name"><br>
password: <input type="password" name="passwd"><br>
<input type="submit" value="提交">
</form>
</body>
</html>
这里发现
post
方法也支持参数提交,但是它采用的是请求中文提交参数
如果
form
表单不指定,则默认采用get
方法:
get
方法通过url
提交,参数数量是受限制的;而且get
方法提参的时候,会将参数回显,不私密post
方法采用正文提交,不会回显,相对私密,但是内容也会被抓包抓到如果要数据安全,就得做加密——
https
4. http状态码
状态码 | 类别 | 含义 |
---|---|---|
1XX | Information(信息状态码) | 收到的请求在处理 |
2XX | Success(成功状态码) | 请求正常处理完毕 |
3XX | Redirection(重定向状态码) | 需要进行附加操作完成请求 |
4XX | Client Error(客户端错位状态码) | 服务器无法处理请求 |
5XX | Server Error(服务器错位状态码) | 服务器处理请求错误 |
常见状态码:
200
:OK404
:Not Found403
:Forbidden302
:Redirect504
:Bad Getaway
5. http常见Header
-
Content-Type
:数据类型要读取的是文件类型:HTTP Content-Type对照表
-
Content-Length
:正文部分的长度 -
Host
:要请求的主机 -
User-Agent
:客户端的身份 -
referer
:当前页面是从哪个页面跳转过来 -
location
:搭配3XX状态码使用,告诉客户端去哪里 -
Cookie
:原理:
有的网站第一次访问需要注册登录,通过post方法将账号密码提交给目标主机,然后主机做认证认,之后可能就重定向到首页,此时服务器返回的报头当中就含有
Set-Cookie
这个字段,里面就包含了账号密码,然后浏览器就回将这个保存起来,这就是cookie
,之后访问这个网站的时候,每一次请求都会包含cookie
的文件内容。保存
cookie
文件有2种保存方法:文件级、内存级这样可能有一个风险就是,如果一个黑客将我们的
cookie
文件扫码走了,那么就可以和我们访问一样的资源,也就是信息泄露。有一种解决办法就是服务端提供一个
session id
,这个在服务器是唯一的,然后给浏览器返回这个session id
,它是有期限的,这样本来有浏览器自己管理的cookie
交给了服务端。 -
Connection
:选择长连接或短连接一个页面包含了许多的元素,这里的一个元素就代表着一个资源,如果发起一个请求响应一次资源,然后关闭连接,这就是短连接;如果建立一个
tcp
连接,发起多个请求返回多次资源,这就是长连接。在一起网页并没有那么多的资源,所以大多数都是短连接
http/1.0
,而现在网页的资源越来越多,采用短连接的方式效率就十分低下,所以要采用长连接http/1.1
,请求全部完毕之后再关闭。但是可能存在版本不同的情况,所以这时候就要协商是采用哪种连接方式。
6. 简单httpserver
http
协议是应用层,应用层底层协议可以选择tcp
和udp
,而http
底层协议是tcp
协议
#pragma once
#include<iostream>
#include<pthread.h>
#include<unordered_map>
#include"Log.hpp"
#include"Socket.hpp"
#include"ReadHtml.hpp"
#include"Protocol.hpp"
static const int defaultport = 8899;
class HttpServer;
class ThreadData
{
public:
ThreadData(int fd, HttpServer *s)
:sockfd(fd), svr(s)
{}
public:
int sockfd;
HttpServer *svr;
};
class HttpServer
{
public:
HttpServer(uint16_t port = defaultport)
:port_(port)
{
content_type_.insert({".html", "text/html"});
content_type_.insert({".png", "image/png"});
content_type_.insert({".mp4", "video/mp4"});
}
void HandlerHttp(int sockfd)
{
char buffer[10240];
ssize_t n = recv(sockfd, buffer, sizeof(buffer)-1, 0);
if(n > 0)
{
buffer[n] = 0;
std::cout << buffer << std::endl;
Request req;
//收到的内容反序列化
req.Deserialize(buffer);
req.Parse();
//req.PrintDebug();
//返回响应
std::string text = Html().ReadContent(req.file_path_); //模型响应正文
bool isOk = true;
if(text.empty())
{
//无法处理请求
isOk = false;
std::string err_html = wwwroot;
err_html += "/";
err_html += "err.html";
text = Html().ReadContent(err_html);
}
std::string response_line;
if(isOk)
{
response_line = "HTTP/1.0 200 OK\r\n"; //模拟状态行
}
else
{
response_line = "HTTP/1.0 404 Not Found\r\n";
}
//重定向
//response_line = "HTTP/1.1 302 Found\r\n";
//模拟报头
std::string response_header = "Content-Length: ";
response_header += std::to_string(text.size());
response_header += "\r\n";
response_header += "Content-Type: ";
response_header += Html().SuffixToDesc(content_type_, req.suffix_);
response_header += "\r\n";
response_header += "Set-Cookie: name=Pyh";
response_header += "\r\n";
//response_header += "Location: https://gitee.com\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);
}
//防止挂太多链接,处理一次链接直接关掉
close(sockfd);
}
//内类有this指针,线程执行函数返回值为void*,所以设置为static
static void *ThreadRun(void *args)
{
// 线程分离
pthread_detach(pthread_self());
ThreadData *td = static_cast<ThreadData*>(args);
td->svr->HandlerHttp(td->sockfd);
delete td;
return nullptr;
}
bool Start()
{
listensock_.Socket();
listensock_.Bind(port_);
listensock_.Listen();
while(true)
{
std::string clientip;
uint16_t clientport;
int sockfd = listensock_.Accept(&clientip, &clientport);
if(sockfd < 0) continue;
log(Info, "get a new link, sockfd: %d", sockfd);
pthread_t tid;
ThreadData *td = new ThreadData(sockfd, this);
pthread_create(&tid, nullptr, ThreadRun, td);
}
}
~HttpServer()
{}
private:
MySocket listensock_;
uint16_t port_;
std::unordered_map<std::string, std::string> content_type_;
};
关于访问:
当使用浏览器进行访问一个web
站点的时候,浏览器会默认加上/
,这个表示访问web根目录 。以如果在访问的时候,加上路径,就可以访问到我们服务器上的特定文件。
这里的根目录,并不是指的
linux
的根目录,是**web
根目录**!!!但是有可能根目录里面有很多内容,但是我们并不能全部给人家,所以一般情况下的
/
表面访问的是网站的首页内容index.html
wget
的底层就是http
协议,它能获取网页的内容,例如: