✨个人主页: 熬夜学编程的小林
💗系列专栏: 【C语言详解】 【数据结构详解】【C++详解】【Linux系统编程】【Linux网络编程】
目录
1、HTTP 协议
1.1、认识 URL
1.2、urlencode 和 urldecode
1.3、HTTP 协议请求与响应格式
1.3.1、代码修改
1.3.2、Http.hpp
1.3.3、ServerMain.cc
1.3.4、腾讯云配置安全组(防火墙)
1.3.5、在浏览器中访问
1.3.5、修改Http.hpp中执行方法
1.3.6、HTTP 请求
1.3.7、HTTP 响应
1.3.8、基本的应答格式
1.4、HTTP 的方法
1.4.1、HTTP 常见方法
1.5、HTTP 常见 Header
1.5.1、关于 connection 报头
1、HTTP 协议
虽然我们说, 应用层协议是我们程序猿自己定的. 但实际上, 已经有大佬们定义了一些现成的, 又非常好用的应用层协议, 供我们直接参考使用. HTTP(超文本传输协议)就是其中之一。
在互联网世界中,HTTP(HyperText Transfer Protocol,超文本传输协议)是一个至关重要的协议。它定义了客户端(如浏览器)与服务器之间如何通信,以交换或传输超文本(如 HTML 文档)。
HTTP 协议是客户端与服务器之间通信的基础。客户端通过 HTTP 协议向服务器发送请求,服务器收到请求后处理并返回响应。HTTP 协议是一个无连接、无状态的协议,即每次请求都需要建立新的连接,且服务器不会保存客户端的状态信息。
1.1、认识 URL
平时我们俗称的 "网址" 其实就是说的 URL
初步认识
深刻认识
1.2、urlencode 和 urldecode
像 / ? : 等这样的字符, 已经被 url 当做特殊意义理解了. 因此这些字符不能随意出现.比如, 某个参数中需要带有这些特殊字符, 就必须先对特殊字符进行转义.
转义的规则如下:
将需要转码的字符转为 16 进制,然后从右到左,取 4 位(不足 4 位直接处理),每 2 位做一位,前面加上%,编码成%XY 格式!
例如:
使用特殊字符访问浏览器时,会进行编码(urlencode)与解码(urldecode)的过程!
1.3、HTTP 协议请求与响应格式
编写代码见见http 请求与应答
拷贝上一弹的网络计算器的部分代码(InetAddr.hpp,LockGuard.hpp,Log.hpp,Makefile,ServerMain.cc,Socket.hpp,TcpServer.hpp),并适当修改代码进行测试!
1.3.1、代码修改
修改TcpServer.hpp文件中的执行方法声明:
using service_t = std::function<std::string (std::string &requeststr)>;
TcpServer类的成员变量与构造函数同理!
class TcpServer
{
public:
TcpServer(service_t service,uint16_t port = gport)
:_port(port),
_listensock(std::make_shared<TcpSocket>()),
_isrunning(false),
_service(service)
{
_listensock->BuildListenSocket(_port);
}
private:
uint16_t _port;
SockSPtr _listensock;
bool _isrunning;
service_t _service; // // 执行方法
};
Execute()函数也需修改!
// 无法调用类内成员 无法看到sockfd
static void *Execute(void *args)
{
ThreadData *td = static_cast<ThreadData *>(args);
pthread_detach(pthread_self()); // 分离新线程,无需主线程回收
std::string requeststr;
// 后面不做请求的分离,我们认为读到的是一个完整的请求 -bug
ssize_t n = td->_sockfd->Recv(&requeststr);
if(n > 0)
{
std::string responsestr = td->_self->_service(requeststr); // 执行回调
td->_sockfd->Send(responsestr);
}
td->_sockfd->Close();
delete td;
return nullptr;
}
1.3.2、Http.hpp
该类用于处理服务的回调函数!
#pragma once
#include <iostream>
#include <string>
class HttpServer
{
public:
HttpServer()
{}
std::string HandlerHttpRequest(std::string &req)
{
std::cout << "---------------------------------------------" << std::endl;
std::cout << req;
return std::string();
}
~HttpServer()
{}
};
注意: 此处的执行方法返回的是空串,后序再进行修改!
1.3.3、ServerMain.cc
ServerMain.cc文件创建TcpServer智能指针对象!
#include "TcpServer.hpp" // 会话层
#include "Http.hpp"
// ./calserver 8888
int main(int argc, char *argv[])
{
if (argc != 2)
{
std::cerr << "Usage: " << argv[0] << "local-port" << std::endl;
exit(0);
}
uint16_t port = std::stoi(argv[1]);
HttpServer hserver;
std::unique_ptr<TcpServer> tsvr = std::make_unique<TcpServer>(
std::bind(&HttpServer::HandlerHttpRequest, &hserver, std::placeholders::_1),
port);
tsvr->Loop();
return 0;
}
1.3.4、腾讯云配置安全组(防火墙)
云服务器只有配置了安全组(防火墙)才能直接使用IP + 端口号在浏览器访问!
步骤一:执行服务端程序:
步骤二:安装telnet服务
步骤三:判断是否配置了安全组:
在cmd中执行命令 telnet ip port !
出现上面的情况表示没有配置安全组!
步骤四:配置安全组(防火墙):
在腾讯云官网的服务器中配置!
步骤五:再次判断是否配置了安全组
在cmd中执行命令 telnet ip port !
此时表示配置成功!
1.3.5、在浏览器中访问
在浏览器中访问的前提是将服务端启动起来,在浏览器输入公网IP:端口号!
1.3.5、修改Http.hpp中执行方法
执行方法中给客户端发送一些信息!
std::string HandlerHttpRequest(std::string &reqstr)
{
std::cout << "---------------------------------------------" << std::endl;
std::cout << reqstr;
// return std::string();
std::string responsestr = "HTTP/1.1 200 OK\r\n";
responsestr += "Content-Type: test/html\r\n";
responsestr += "\r\n";
responsestr += "<html><h1>hello linux,hello net!<h2></html>";
return responsestr;
}
错误示范(X)
此处是因为方法中的代码错了一个英文,导致没有打印出来东西,而是下载内容!
正确示范(√)
修改HandlerHttpRequest()函数中的一个英文字母即可!
1.3.6、HTTP 请求
- 首行: [方法] + [url] + [版本]
- Header: 请求的属性, 冒号分割的键值对;每组属性之间使用\r\n 分隔;遇到空行表示 Header 部分结束
- Body: 空行后面的内容都是 Body. Body 允许为空字符串. 如果 Body 存在, 则在Header 中会有一个 Content-Length 属性来标识 Body 的长度;
1.3.7、HTTP 响应
- 首行: [版本号] + [状态码] + [状态码解释]
- Header: 请求的属性, 冒号分割的键值对;每组属性之间使用\r\n 分隔;遇到空行表示 Header 部分结束
- Body: 空行后面的内容都是 Body. Body 允许为空字符串. 如果 Body 存在, 则在Header 中会有一个 Content-Length 属性来标识 Body 的长度; 如果服务器返回了一个 html 页面, 那么 html 页面内容就是在 body 中.
1.3.8、基本的应答格式
1.4、HTTP 的方法
其中最常用的就是 GET 方法和 POST 方法.
1.4.1、HTTP 常见方法
1. GET 方法(重点)
用途:用于请求 URL 指定的资源。
示例:GET /index.html HTTP/1.1
特性:指定资源经服务器端解析后返回响应内容。
form表单
2. POST 方法(重点)
用途:用于传输实体的主体,通常用于提交表单数据。
示例:POST /submit.cgi HTTP/1.1
特性:可以发送大量的数据给服务器,并且数据包含在请求体中。
3. PUT 方法(不常用)
用途:用于传输文件,将请求报文主体中的文件保存到请求 URL 指定的位置。
示例:PUT /example.html HTTP/1.1
特性:不太常用,但在某些情况下,如 RESTful API 中,用于更新资源。
4. HEAD 方法
用途:与 GET 方法类似,但不返回报文主体部分,仅返回响应头。
示例:HEAD /index.html HTTP/1.1
特性:用于确认 URL 的有效性及资源更新的日期时间等。
5. DELETE 方法(不常用)
用途:用于删除文件,是 PUT 的相反方法。
示例:DELETE /example.html HTTP/1.1
特性:按请求 URL 删除指定的资源。
6. OPTIONS 方法
用途:用于查询针对请求 URL 指定的资源支持的方法。
示例:OPTIONS * HTTP/1.1
特性:返回允许的方法,如 GET、POST 等。
1.5、HTTP 常见 Header
- Content-Type: 数据类型(text/html 等)
- Content-Length: Body 的长度
- Host: 客户端告知服务器, 所请求的资源是在哪个主机的哪个端口上;
- User-Agent: 声明用户的操作系统和浏览器版本信息;
- referer: 当前页面是从哪个页面跳转过来的;
- Location: 搭配 3xx 状态码使用, 告诉客户端接下来要去哪里访问;
- Cookie: 用于在客户端存储少量信息. 通常用于实现会话(session)的功能;
1.5.1、关于 connection 报头
HTTP 中的 Connection 字段是 HTTP 报文头的一部分,它主要用于控制和管理客户端与服务器之间的连接状态。
核心作用
- 管理持久连接:Connection 字段还用于管理持久连接(也称为长连接)。持久连接允许客户端和服务器在请求/响应完成后不立即关闭 TCP 连接,以便在同一个连接上发送多个请求和接收多个响应。
持久连接(长连接)
- HTTP/1.1:在 HTTP/1.1 协议中,默认使用持久连接。当客户端和服务器都不明确指定关闭连接时,连接将保持打开状态,以便后续的请求和响应可以复用同一个连接。
- HTTP/1.0:在 HTTP/1.0 协议中,默认连接是非持久的。如果希望在 HTTP/1.0上实现持久连接,需要在请求头中显式设置 Connection: keep-alive。
语法格式
- Connection: keep-alive:表示希望保持连接以复用 TCP 连接。
- Connection: close:表示请求/响应完成后,应该关闭 TCP 连接。
下面附上一张关于 HTTP 常见 header 的表格
字段名 | 含义 | 样例 |
---|---|---|
Accept | 客户端可接受的 响应内容类型 | Accept: text/html,application/xhtml+xml,app lication/xml;q=0.9,image/webp,image /apng,*/*;q=0.8 |
Accept Encoding | 客户端支持的数 据压缩格式 | Accept-Encoding: gzip, deflate, br |
Accept Language | 客户端可接受的 语言类型 | Accept-Language: zh CN,zh;q=0.9,en;q=0.8 |
Host | 请求的主机名和 端口号 | Host: www.example.com:8080 |
User-Agent | 客户端的软件环 境信息 | User-Agent: Mozilla/5.0 (Windows NT10.0; Win64; x64) AppleWebKit/537.36 (KHTML, likeGecko) Chrome/91.0.4472.124 Safari/537.36 |
Cookie | 客户端发送给服 务器的 HTTP cookie 信息 | Cookie: session_id=abcdefg12345;user_id=123 |
Referer | 请求的来源 URL | Referer: http://www.example.com/previous_page.html |
Content-Type | 实体主体的媒体 类型 | Content-Type: application/x-wwwform-urlencoded (对于表单提交) 或 Content-Type: application/json (对于JSON 数据) |
Content-Length | 实体主体的字节 大小 | Content-Length: 150 |
Authorization | 认证信息, 如用 户名和密码 | Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== (Base64 编码后的用户名:密码) |
Cache-Control | 缓存控制指令 | 请求时: Cache-Control: no-cache 或 Cache-Control: max-age=3600; 响应时: Cache-Control: public, max age=3600 |
Connection | 请求完后是关闭 还是保持连接 | Connection: keep-alive 或 Connection: close |
Date | 请求或响应的日 期和时间 | Date: Wed, 21 Oct 2023 07:28:00 GMT |
Location | 重定向的目标 URL(与 3xx 状 态码配合使用) | Location: http://www.example.com/new_location.html (与 302 状态码配合使用) |
Server | 服务器类型 | Server: Apache/2.4.41 (Unix) |
Last-Modified | 资源的最后修改 时间 | Last-Modified: Wed, 21 Oct 2023 07:20:00 GMT |
ETag | 资源的唯一标识 符, 用于缓存 | ETag: "3f80f-1b6-5f4e2512a4100" |
Expires | 响应过期的日期 和时间 | Expires: Wed, 21 Oct 2023 08:28:00 GMT |