【Linux网络】Linux 网络编程:HTTP(二)HTTP协议请求应答宏观格式(附代码演示)
《Linux操作系统编程详解》《笔试/面试常见算法:从基础到进阶》《Python干货分享》
🎬 艾莉丝的简介:

文章目录

导入语
很多uu初学 HTTP 时,总觉得它是一个很 “高层” 的神秘协议,但实际上,HTTP 底层完完全全依托于 TCP,本质就是一套基于套接字的文本约定。这篇文章我们不搞抽象理论,直接从代码出发,把 TCP 服务器、HTTP 报文格式、请求解析、响应构建、序列化反序列化、URI 路径映射全部串起来,手搓一个可运行的 HTTP 服务器。学完你会真正明白:浏览器发的是什么、服务端怎么解、数据怎么回,HTTP 到底为什么可以被称为 “远程文件访问协议”。
大纲

1 整体认知:HTTP 底层依托 TCP
1.1 核心定位
HTTP 属于应用层协议,底层完全建立在 TCP 协议 之上,本质依旧是 TCP 套接字通信。
客户端无需手动编写,浏览器即为天然 HTTP 客户端,直接发送标准 HTTP 请求(如下图)。

1.2 代码复用与解耦
可直接复用原有 TCP 相关组件:
TcpServer.hpp
Socket.hpp
InetAddr.hpp
Logger.hpp(日志)
Mutex.hpp(互斥锁)
与网络计算器项目对比,仅上层应用协议不同,网络通信层完全一致,良好的解耦让协议替换变得简单。
1.3 整体代码模块
HttpProtocol.hpp:定义 HttpRequest、HttpResponse,负责反序列化(解析请求) 与序列化(构建响应)。HttpServer.hpp:HTTP 服务器主类,基于 TCP 封装,提供请求处理入口。Main.cc:程序入口,完成服务器初始化、端口绑定、服务启动。
2 HttpServer 实现与 TCP 联动
2.1 类结构定义
#pragma once
#include <iostream>
#include <string>
#include <memory>
#include "TcpServer.hpp"
#include "Logger.hpp"
using namespace LogModule;
class HttpServer {
public:
HttpServer(uint16_t port)
: _port(port), _tsvr(std::make_unique<TcpServer>(port))
{}
std::string HandlerHttpRequest(std::string &streamstr);
void Run() {
_tsvr->Run([this](std::string &streamstr) -> std::string {
return this->HandlerHttpRequest(streamstr);
});
}
~HttpServer(){}
private:
uint16_t _port;
std::unique_ptr<TcpServer> _tsvr;
};
2.2 核心运行逻辑
- TcpServer 仅负责网络 IO:创建套接字、绑定、监听、接收 / 发送字节流。
- HttpServer 负责协议处理:解析请求、构建响应。
- 二者通过回调函数联动,TCP 收到的字节流直接交给
HandlerHttpRequest处理。
2.3 Main.cc 入口实现
#include "HttpServer.hpp"
void Usage(std::string procname) {
std::cout << "Usage:" << procname << " ServerPort" << std::endl;
}
int main(int argc, char *argv[]) {
if (argc != 2) {
Usage(argv[0]);
exit(1);
}
ENABLE_CONSOLE_LOG_STRATEGY();
uint16_t port = std::stoi(argv[1]);
std::unique_ptr<HttpServer> hsvr = std::make_unique<HttpServer>(port);
hsvr->Run();
return 0;
}
3 HTTP 请求报文格式与解析
3.1 请求报文完整结构

请求行\r\n
请求报头 Key: Value\r\n
请求报头 Key: Value\r\n
...\r\n
空行\r\n
请求正文 DATA
3.1.1 请求行
格式:请求方法 空格 URI 空格 HTTP 版本 \r\n
示例:GET / HTTP/1.1
3.1.2 请求报头
多行 Key: Value 结构,描述客户端信息、连接方式、接收类型等。
3.1.3 空行
必须存在,用于分隔报头与正文,是判断报头是否完整的标志。
3.1.4 请求正文
可选内容,如登录信息、提交数据,长度由报头中的 Content-Length 字段决定。
3.2 对 HTTP 请求的本质理解
HTTP 请求本质是一串按固定规则组织的字节流(字符串),分行显示仅因 \r\n 分隔符,协议本身只识别分隔符。
请求行 + 请求报头 = 报头部分;请求正文 = 有效载荷部分,二者以空行分隔。
3.3 HTTP请求代码的编写:验证http请求
需要现场基于历史代码,先架构处⼀个基本的HTTP服务器,然后⽤浏览器进⾏验证
需要验证前端内容,直接AI即可
3.4 HttpRequest 类定义
#ifndef _HTTP_PROTOCOL_HPP
#define _HTTP_PROTOCOL_HPP
#include <iostream>
#include <string>
#include <unordered_map>
class HttpRequest {
public:
HttpRequest(){}
~HttpRequest(){}
void Deserialize(std::string &streamstr);
void ParseReqLine(std::string &status_line);
int ReadOneline(std::string &streamstr, std::string *line);
void SplitString(std::string &line, std::string *key, std::string *value, std::string sep);
void PrintDebug();
std::string operator[](const std::string &key) const;
private:
std::string _method;
std::string _uri;
std::string _http_version;
std::unordered_map<std::string, std::string> _headerkv;
std::string _blankline;
std::string _body;
std::string _path;
};
class HttpResponse {
public:
HttpResponse(){}
~HttpResponse(){}
void Serialize(std::string *outstr);
private:
std::string _http_version;
int _status_code;
std::string _status_code_desc;
std::unordered_map<std::string, std::string> _resp_headerkv;
std::string _blankline;
std::string _body;
};
#endif
3.5 请求反序列化流程
1、按行读取:以 \r\n 为分隔符,逐行提取并删除已读内容。
2、解析请求行:通过 stringstream 按空格拆分出方法、URI、HTTP 版本。
3、解析请求报头:循环读取非空行,以:拆分 Key-Value 存入 unordered_map。
4、识别空行:读取到空行标志报头读取完毕。
5、读取正文:根据 Content-Length 截取剩余字符串作为正文。
6、路径映射:将 URI 映射为本地 wwwroot 目录下的路径,_path = webroot + _uri。
3.6 operator [] 重载实现
std::string HttpRequest::operator[](const std::string &key) const
{
if (key == "method") return _method;
else if (key == "uri") return _uri;
else if (key == "httpversion") return _http_version;
else if (key == "body") return _body;
else if (key == "path") return _path;
else
{
auto iter = _headerkv.find(key);
if (iter != _headerkv.end()) return iter->second;
}
return std::string();
}
未来在Web就可以访问Request的内容了(以[]的方式)。
4 HTTP 响应报文格式与构建
4.1 响应报文完整结构

状态行\r\n
响应报头 Key: Value\r\n
响应报头 Key: Value\r\n
...\r\n
空行\r\n
响应正文 DATA
4.1.1 状态行
格式:HTTP 版本 空格 状态码 空格 状态描述\r\n
示例:HTTP/1.0 200 OK\r\n
4.1.2 响应报头
必带 Content-Length 字段,标识响应正文长度。
4.1.3 空行
分隔报头与正文,固定格式要求。
4.1.4 响应正文
浏览器展示的内容,如 HTML、文本、图片、视频等资源。
4.2 基本的应答格式

4.3 最简响应构建示例
std::string resp_status = "HTTP/1.0 200 OK\r\n";
std::string resp_content = "hello world, hello http!";
std::string cl = "Content-Length: " + std::to_string(resp_content.size()) + "\r\n";
std::string blankLine = "\r\n";
return resp_status + cl + blankLine + resp_content;
4.4 响应序列化流程
将 HttpResponse 中的版本、状态码、状态描述、响应报头、空行、响应正文,按 HTTP 响应格式拼接为字符串,返回给 TCP 层发送至客户端。
5 ~> 报文完整性与连接模式
5.1 报文完整性判断
- 报头完整:读取到 \r\n 构成的空行。
- 正文完整:通过报头中 Content-Length 字段确定长度。
- TCP 粘包:本文做了简化处理,默认读取完整报文,粘包问题肯定存在,但是粘包问题是一个小概率事件,不是不处理,而是本文先不管。后续可通过
Unpack解包解决。
5.2 连接模式
默认采用短连接,一次请求 - 响应完成后,服务端主动关闭 TCP 连接,简化逻辑;后续可扩展支持 keep-alive 长连接。
我复用了之前写的代码,这里把while(true)和判断条件里面的break去掉就行。
6 ~> 服务运行、测试与现象
6.1 启动方式
编译后执行命令:./httpserver 端口号(如 8080)
日志输出:socket 创建成功 → bind 成功 → listen 成功
6.2 测试方式
- 浏览器访问:
服务器 IP: 端口,自动发送 GET 请求。 - telnet 连接:手动模拟 HTTP 请求。
6.3 测试现象
- 服务端打印完整 HTTP 请求报文。
- 未返回响应:浏览器提示 ERR EMPTY RESPONSE。
- 正常返回响应:浏览器展示响应正文。
- 浏览器会自动请求
/favicon.ico图标文件。
7 ~> HTTP 版本协商
请求携带客户端支持的 HTTP 版本,响应携带服务端支持的 HTTP 版本,双方通过版本号完成握手,决定协议特性的启用与兼容性。
8 ~> HTTP方法 && 核心本质
8.1 HTTP方法

其中最常用的就是GET方法和POST方法。
8.2 核心本质
HTTP 是纯文本格式的应用层协议, 请求与响应均为格式化字节流;
核心作用是远程资源本地化访问,请求 URI 对应服务器本地文件,通过网络将文件资源传输给浏览器。
9 ~> 总结 && 已经可以跑通的代码(浏览器无响应是因为云服务器端口号没有设置开放) && 后续完善方向
9.1 总结
本文从底层原理到代码实现,完整还原了 HTTP 基于 TCP 的运行模型、请求 / 响应报文结构、反序列化解析流程、URI 映射到本地资源的逻辑,以及服务端构建响应的完整过程。我们不仅看懂了 HTTP 报文,更用 C++ 实现了解析、封装、回调、IO 处理的全套流程,真正做到从协议格式到代码落地,一次性吃透 HTTP 的核心本质。
9.1.1 补充问题:可以通过 … 访问到其他文件吗?
可以!浏览器的回退就是cd ..真正上网的时候就是靠点击的,点击来查看某个文件。
9.1.2 补充知识点:index.html
默认情况下,做一个网站,需要有一个首页:

这个index.html的命名是固定的!
9.2 已经可以跑的代码
- 浏览器无响应是因为云服务器端口号没有设置开放;
- telnet可以顺利连接
这里不多赘言,就把有变化或者说新增的代码放上来了。
9.2.1 HttpProtocol.hpp
#ifndef __HTTP_PROTOCOL_HPP
#define __HTTP_PROTOCOL_HPP
#include <iostream>
#include <string>
#include <unordered_map> // key_value
#include <sstream>
// 日志
#include "Logger.hpp"
// 怎么提取?分隔符 --> 我要把分隔符暴露出来
const std::string linesep = "\r\n";
// 我需要一个报头的分隔符
const std::string headersep = ": ";
// tuturoot
const std::string webroot = "tuturoot";
// 命名空间
using namespace LogModule;
// 示例:协议格式
// 前3行:带长度前缀的自定义协议格式("长度"\r\nJSON内容)
// "40"\r\n{"float":3.14, "int": 100, "s1": "helloworld", "s2": "hello bit"}
// "40"\r\n{"float":3.14, "int": 100, "s1": "helloworld", "s2": "hello bit"}
// "40"\r\n{"float":3.14, "int": 100, "s1": "helloworld", "s2": "hello bit"}
// 最后1行:纯JSON格式,没有长度前缀
// {"float":3.14, "int": 100, "s1": "helloworld", "s2": "hello bit"}
// HTTP协议底层就是TCP协议
// 也需要请求和应答
class HttpRequest
{
private:
// 把请求行读出来
int ReadOneLine(std::string &streamstr,std::string *line) // \r\n
{
// 1. 在输入字节流中查找行分隔符 "\r\n" 的位置
auto pos = streamstr.find(linesep);
// 2. 如果没找到行分隔符,说明数据不完整,返回空字符串
if(pos == std::string::npos) // 整个字符串里没有分隔符
// return std::string(); // 返回值字符串是空串
return -1; // 参数要匹配
// 3. 从字节流开头截取到分隔符之前,得到一行完整内容
*line = streamstr.substr(0,pos); // 截止到pos位置
// 4. 从字节流中删除已经读取的这一行(包括 "\r\n" 分隔符),
// 方便下次调用时读取下一行
streamstr.erase(0,pos + linesep.size()); // 删的时候删到换行符
// // 5. 返回读取到的这一行
// return line;
return line->size(); // >=(表示截取成功;等于0的时候说明读到空串)
}
// 把stringstream以文件的形式写到网络
void ParseReqLine(std::string &status_line)
{
std::stringstream ss(status_line);
ss >> _method >> _uri >> _http_version;
// 比如说资源是:/a/b/c.html
_path = webroot + _uri; // tuturoot/a/b/c.html
}
// 打印
void PrintDebug()
{
std::cout << _method << "#" << _uri << "#" << _http_version << std::endl;
// 遍历headerkv
for(auto &item : _header_kv)
{
std::cout << item.first << "->" << item.second << std::endl;
}
// 把空行打印出来
std::cout << "blankline: " << _blankline << std::endl; // 空行这里再加个换行
// 我的body现在没什么东西,加点东西
std::cout << "body: " << _body << std::endl;
}
// 单独设计一个分割字符串的接口
void SplitString(std::string &line,std::string *key,std::string *value,std::string sep = headersep)
{
// headersep设置为“:”,就是以:为分隔符
auto pos = line.find(sep);
if(pos == std::string::npos) // 没有返回空串
return;
*key = line.substr(0,pos);
*value = line.substr(pos + sep.size());
}
public:
HttpRequest()
{}
// 提供一个反序列化的接口
void Deserialize(std::string &streamstr)
{
// 从网络当中传递一个字节流信息,然后转换成结构化信息
// 1.读取第一行
std::string status_line;
int n = ReadOneLine(streamstr,&status_line);
(void)n;
// GET /a/b/c/d/e.html HTTP/1.1
// GET / HTTP/1.1
// 2.解析请求行
ParseReqLine(status_line);
PrintDebug();
// 3.提取请求报头
n = 0; // 第一行已经被移除了
do
{
std::string line;
n = ReadOneLine(streamstr,&line);
if(n > 0)
{
std::string key,value;
SplitString(line,&key,&value,headersep);
// 都不为空说明取到了合法的key_value值
if(!key.empty() && !value.empty())
{
_header_kv[key] = value;
}
}
else if(n == 0)
{
_blankline = "\r\n";
break;
}
else
{
LOG(LogLevel::DEBUG) << "bug???";
break;
}
}while(n > 0);
// 请求的正文是否存在
if(_header_kv.find("Content-Length") != _header_kv.end())
{
// 不存在就什么都不做
int len = std::stoi(_header_kv["Content-Length"]);
// 前面都已经去掉了,这里就读到body了
_body = streamstr.substr(0,len);
// _body = streamstr;
}
PrintDebug();
}
// 在Request里面做一个运算符重载(只读),重载一个中括号,因为原来获取URI的方式不太优雅
// 这里引用不能引用常量字符串,把const和&去掉
std::string operator [](const std::string &key) const // 未来提供一个key就行
{
// 把一堆GET方法,封装从接口,以只读方式访问
if(key == "method")
return _method;
else if(key == "uri")
return _uri;
else if(key == "httpversion")
return _http_version;
else if(key == "body")
return _body;
// else if(key == "uri")
// return _uri;
// 不要请求uri了,在当前代码中还存在像/a/b/c.html这样的特殊情况
else if(key == "path")
return _path;
else{
auto iter = _header_kv.find(key);
if(iter != _header_kv.end())
return iter->second;
}
return std::string(); // 返回一个空串
}
~HttpRequest()
{}
private:
// 请求方法、URI、HTTP版本、Key:[空格]Value,还有空行
std::string _method;
std::string _uri; // /a/b/c.html /(特殊情况)
std::string _http_version;
std::unordered_map<std::string,std::string> _header_kv;
std::string _blankline;
// 除了请求还要有一个请求的正文
std::string _body;
private:
std::string _path;
};
// 处理请求,构建应答
class HttpResponse
{
public:
HttpResponse() {}
~HttpResponse() {}
void Serialize(std::string *outstr)
{}
private:
// 结构化字段
std::string _http_version; // HTTP协议版本,如 "HTTP/1.1"
int _status_code; // 状态码,如 200(成功)、404(未找到)、500(服务器错误)
std::string _status_code_desc; // 状态码描述,如 "OK"、"Not Found"、"Internal Server Error"
std::unordered_map<std::string,std::string> _resp_headerkv; // 响应头键值对,如 Content-Type、Content-Length
std::string _blankline; // 空行,用于分隔响应头和响应体
std::string _body; // 正文:响应体,如网页内容、JSON数据等
};
#endif
9.2.2 HttpServer.hpp
#ifndef __HTTPSERVER_HPP
#define __HTTPSERVER_HPP
// Http就不用写客户端了,因为是和浏览器连接,浏览器就是客户端
#include <iostream>
#include <string>
#include <memory>
#include "TcpServer.hpp"
#include "Logger.hpp"
#include "HttpProtocol.hpp"
// 命名空间包一下
using namespace LogModule;
class HttpServer
{
public:
// ===== 原来的代码(已注释)=====
HttpServer(uint16_t port)
: _port(port),
_tsvr(std::make_unique<TcpServer>(port))
{}
// 不想这样写就在类里面写这样一个接口
std::string HandlerHttpRequest(std::string &streamstr)
{
// 1.报文完整性这里不管啦,不是不存在粘包问题,粘包问题是个小概率事件,这里先不考虑,默认报文是完整的
// 2.反序列化
HttpRequest httpreq; // 服务端接收到的是一个请求
httpreq.Deserialize(streamstr);
std::cout << "HandlerHttpRequest: " << httpreq["method"] << std::endl;
std::cout << "HandlerHttpRequest: " << httpreq["uri"] << std::endl;
std::cout << "HandlerHttpRequest: " << httpreq["httpversion"] << std::endl;
std::string target = httpreq["path"]; // /a/b/c.html
std::cout << "http req: " << target << std::endl;
// 3. 处理请求,构建应答:httpreq --> httpresp
HttpResponse httpresp;
// 4.应答序列化:把结构化的字段变成字符串
std::string httprespstr;
httpresp.Serialize(&httprespstr);
// 5.返回:返回应答的序列化字段
return httprespstr;
// 函数会回调到这里
// 这样main函数就简单了,到时候直接包一下头文件HttpServer.hpp
// 打印日志
// LOG(LogLevel::DEBUG) << "http request:\r\n"; // 将来把回调的HTTP请求从哪来的?
// // LOG(LogLevel::DEBUG) << "http request:\r\n" << streamstr;
// LOG(LogLevel::DEBUG) << "##################################\r\n";
// LOG(LogLevel::DEBUG) << streamstr;
// LOG(LogLevel::DEBUG) << "##################################\r\n";
// =============> 上面第5步返回:就是把这些结构化字段按照这种格式发回去,回到TcpServer <=============
// // ----> 这个应答测试可以,但是太草率了 <----
// // 状态行
// std::string resp_status = "HTTP/1.0 200 OK\r\n"; // HTTP版本 状态码 状态码描述
// // 响应正文
// std::string resp_content = "hello world,hello http!";
// // 响应报头
// std::string c1 = "Content-Length: " + std::to_string(resp_content.size()) + "\r\n";
// // 空行
// std::string blankline = "\r\n";
// // 怎么返回应答报文,直接拼接一个字符串
// return resp_status + c1 + blankline + resp_content; // 返回之后就会回到TcpServer的回调当中
}
// 回调有了
void Run()
{
// _tsvr->Run();
// 写一个lambda
_tsvr->Run([this](std::string &streamstr)->std::string{
return this->HandlerHttpRequest(streamstr);
});
}
~HttpServer()
{}
private:
// 端口号
uint16_t _port;
// 这里要有一个协议字段
// std::unique_ptr<HttpProtocol> _protocol; // 不想这样写就在类里面写一个接口
std::unique_ptr<TcpServer> _tsvr;
};
#endif
9.2.3 TcpServer.hpp
这里我暂时把长连接改成短连接了。
#ifndef __TcpServer_HPP
#define __TcpServer_HPP
#include <iostream>
#include <string>
#include "Socket.hpp"
#include <memory>
#include <unistd.h>
#include <signal.h>
#include "Logger.hpp"
#include <functional>
// 处理客户端请求的回调函数类型
// 参数: 接收到的客户端数据 (inbuffer)
// 返回值: 要发送给客户端的响应数据 (outbuffer)
using handler_t = std::function<std::string(std::string &)>;
// TCP服务器类 - 提供多进程并发的TCP网络服务
// 核心功能:
// 1. 在指定端口创建并监听套接字
// 2. 接收客户端连接请求
// 3. 为每个客户端fork子进程进行独立处理
// 4. 通过回调函数实现业务逻辑与网络层的解耦
class TcpServer
{
public:
TcpServer(uint16_t port, handler_t handler = nullptr)
: _port(port),
_listensockfd(std::make_unique<TcpSocket>()),
_isrunning(false),
_handlerstream(handler)
{
_listensockfd->BuildSocketMethod(_port); // 模版方法模式
}
// =================
// 启动TCP服务器,开始监听并接受客户端连接
// 功能:
// 1. 进入无限循环,持续等待客户端连接
// 2. 每次接收到新连接时,fork子进程处理该客户端的IO
// 3. 父进程继续监听下一个连接
void Run(handler_t handler)
{
_handlerstream = handler;
_isrunning = true;
signal(SIGCHLD,SIG_IGN);
while(_isrunning)
{
InetAddr clientaddr;
auto sockfd = _listensockfd->Accept(&clientaddr); // 1. clientaddress 2. 得到socket
if(!sockfd)
{
LOG(LogLevel::WARNING) << "Accepter error";
continue;
}
LOG(LogLevel::DEBUG) << "client addr: " << clientaddr.StringAddress() << " socket: " << sockfd->Socketfd();
// 处理链接的问题
if(fork() == 0)
{
_listensockfd->Close();
// 子进程
HanderIO(sockfd, clientaddr);
exit(0);
}
// 父进程
sockfd->Close();
}
}
// 处理已连接客户端的IO操作
// 参数1: 与客户端建立连接的socket对象
// 参数2: 客户端的地址信息
// 功能: 循环接收客户端数据
void HanderIO(std::shared_ptr<Socket> sockfd, InetAddr &clientaddr)
{
std::string inbuffer;
// 改成短连接
int n = sockfd->Recv(&inbuffer); // 假设我们默认读到的就是完整的报文
if(n < 0)
{
LOG(LogLevel::WARNING) << "recv error";
}
if(n == 0)
{
LOG(LogLevel::INFO) << "client quit: " << clientaddr.StringAddress() << " sockfd: " << sockfd->Socketfd();
}
LOG(LogLevel::DEBUG) << inbuffer;
// 1. 分析inbuffer保证报文的完整性
// 2. a. 完整: 提取,处理 b.不完整:什么都不做
// 3:这个工作谁做?协议来做!
// 谁不做??TcpServer
std::string outbuffer;
if(_handlerstream)
outbuffer = _handlerstream(inbuffer); // 回调函数会调出去,也会再回来!
if(!outbuffer.empty())
sockfd->Send(outbuffer);
sockfd->Close();
}
~TcpServer()
{
// 析构
}
private:
int _port;
std::unique_ptr<Socket> _listensockfd;
bool _isrunning;
handler_t _handlerstream;
};
#endif
9.2.4 Main.cc
#include "HttpServer.hpp" // 先把头文件包好,头文件只要包含这个就可以了,日志在这个头文件那里就已经包含了
void Usage(std::string procname)
{
std::cout << "Usage: " << procname << " Serverport" << std::endl;
}
int main(int argc,char *argv[])
{
// 输出一个手册
if(argc != 2)
{
Usage(argv[0]);
exit(0);
}
// 控制台策略
ENABLE_CONSOLE_LOG_STRATEGY();
// 解析命令行参数,将字符串端口号转换为uint16_t整数类型
uint16_t port = std::stoi(argv[1]);
// 创建HttpServer智能指针对象,初始化服务器监听端口
std::unique_ptr<HttpServer> hsvr = std::make_unique<HttpServer>(port);
// 启动HTTP服务器,开始接收并处理客户端请求
hsvr->Run();
return 0;
}
9.2.5 运行
9.2.5.1 客户端:telnet连接
alice@VM-4-17-ubuntu:~/Alice/bit_118_ubuntu/HTTP/HttpServer_v2$ telnet 127.0.0.1 8088
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
aaaa
Connection closed by foreign host.
9.2.5.2 服务器端
alice@VM-4-17-ubuntu:~/Alice/bit_118_ubuntu/HTTP/HttpServer_v2$ ./httpserver 8088
[2026-05-16 01:19:53][INFO][2221403][Socket.hpp][92]- create socket success
[2026-05-16 01:19:53][INFO][2221403][Socket.hpp][104]- bind socket success
[2026-05-16 01:19:53][INFO][2221403][Socket.hpp][115]- listen socket success
[2026-05-16 01:20:24][DEBUG][2221403][TcpServer.hpp][68]- client addr: [127.0.0.1:39758] socket: 4
[2026-05-16 01:28:07][DEBUG][2222050][TcpServer.hpp][101]- aaaa
aaaa##
blankline:
body:
[2026-05-16 01:28:07][DEBUG][2222050][HttpProtocol.hpp][132]- bug???
aaaa##
blankline:
body:
HandlerHttpRequest: aaaa
HandlerHttpRequest:
HandlerHttpRequest:
http req: tuturoot
9.3 后续完善
-
实现完整 Unpack 解包逻辑,解决 TCP 粘包问题。
-
完善 200、404、403、500 等状态码及对应响应。
-
实现文件读取功能,根据 URI 返回
wwwroot目录下真实的 HTML、CSS、JS、图片等资源。 -
支持 POST、PUT 等更多请求方法。
-
支持长连接、高并发优化。
-
将端口、webroot 等配置参数文件化。

结尾
uu们,本文的内容到这里就全部结束了,艾莉丝在这里再次感谢您的阅读!
|
结语:希望对学习Linux相关内容的uu有所帮助,不要忘记给博主“一键四连”哦!
往期回顾:
【Linux网络】Linux 网络编程:HTTP(一)协议初识
🗡博主在这里放了一只小狗,大家看完了摸摸小狗放松一下吧!🗡 ૮₍ ˶ ˊ ᴥ ˋ˶₎ა
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)