✨个人主页:熬夜学编程的小林
💗系列专栏: 【C语言详解】 【数据结构详解】【C++详解】【Linux系统编程】【Linux网络编程】
目录
[1、HTTP 协议](#1、HTTP 协议)
[1.1、认识 URL](#1.1、认识 URL)
[1.2、urlencode 和 urldecode](#1.2、urlencode 和 urldecode)
[1.3、HTTP 协议请求与响应格式](#1.3、HTTP 协议请求与响应格式)
[1.3.6、HTTP 请求](#1.3.6、HTTP 请求)
[1.3.7、HTTP 响应](#1.3.7、HTTP 响应)
[1.4、HTTP 的方法](#1.4、HTTP 的方法)
[1.4.1、HTTP 常见方法](#1.4.1、HTTP 常见方法)
[1.5、HTTP 常见 Header](#1.5、HTTP 常见 Header)
[1.5.1、关于 connection 报头](#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
特性:指定资源经服务器端解析后返回响应内容。
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 |