跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++

Linux 网络编程:理解 Web 路径与实现简单 HTTP 服务

基于 C++ 和 Socket 编程实现简单的 HTTP 服务器。内容涵盖文件准备、Makefile 配置、类封装、线程处理请求、HTTP 响应编写以及 Web 路径解析。通过解析请求行提取路径,结合 Web 根目录映射文件资源,实现了静态网页的访问功能。

链路追踪发布于 2026/4/6更新于 2026/9/1078 浏览
Linux 网络编程:理解 Web 路径与实现简单 HTTP 服务

准备文件

首先准备以下文件:

  • HttpServer.cc:用于运行接收 HTTP 请求的服务。
  • HttpServer.hpp:定义 HTTP 请求相关类。
  • Log.hpp:打印日志的小组件。
  • Socket.hpp:套接字组件。

直接调用相关接口即可(Log.hpp 和 Socket.hpp 的实现细节不在此讲解)。

Makefile

HttpServer:Httpserver.cc 
	g++ -o $@ $^ -std=c++11 -g -lpthread
.PHONY:clean
clean:
	rm -rf HttpServer

HttpServer.hpp

类内成员

class HttpServer {
public:
    HttpServer(uint16_t port = defaultport) :port_(port) {}
    static void* ThreadRun(void* args) { }
    void start() { }
    ~HttpServer() {};
private:
    Socket listensock_;
    uint16_t port_;
};

类内的成员变量包括端口号 port_,启动服务时输入端口号。listensock_ 用于接收主机请求。ThreadRun 是线程执行的方法,用于管理服务。

封装 sockfd

为了将 sockfd 传给线程,使用 ThreadData 结构体封装:

struct ThreadData {
    int sockfd;
};

ThreadRun 必须是静态成员函数才能作为线程执行方法。通过传递 ThreadData* 指针给线程,使其能访问 sockfd。

start

start 函数负责服务启动流程:初始化、绑定、监听,然后循环接收请求。建立连接后获取 sockfd,封装后传给线程处理。

void start() {
    listensock_.InitSocket(); // 初始化 sockfd
    listensock_.Bind(port_);  // 绑定
    listensock_.Listen();     // 监听
    for (;;) {
        string clientip;
        uint16_t clientport;
        int sockfd = listensock_.Accept(&clientip, &clientport);
        pthread_t tid;
        ThreadData* td = new ThreadData();
        td->sockfd = sockfd;
        pthread_create(&tid, nullptr, ThreadRun, td);
    }
}

ThreadRun

线程执行过程:创建缓冲区,从 sockfd 读取数据。

static void* ThreadRun(void* args) {
    pthread_detach(pthread_self());
    ThreadData* td = static_cast<ThreadData*>(args);
    char buffer[10240];
    ssize_t n = read(td->sockfd, buffer, sizeof(buffer) - 1);
    if(n > 0) {
        buffer[n] = 0;
        cout << buffer;
    }
    close(td->sockfd);
    delete td;
    return nullptr;
}

响应书写

可以将响应单独封装。HandlerHttp 函数负责接收请求并返回响应。

HTTP 响应格式:第一行是响应行,下面是多行报头,最后是空行和正文。

static void HandlerHttp(int sockfd) {
    char buffer[10240];
    ssize_t n = recv(sockfd, buffer, sizeof(buffer) - 1, 0);
    if(n > 0) {
        buffer[n] = 0;
        cout << buffer;

        string text = "hello world";
        string response_line = "HTTP/1.0 200 OK\r\n";
        string response_header = "Content-Length: ";
        response_header += to_string(text.size());
        response_header += "\r\n";
        string blank_line = "\r\n";

        string response;
        response += response_line;
        response += response_header;
        response += blank_line;
        response += text;

        send(sockfd, response.c_str(), response.size(), 0);
    }
    close(sockfd);
}

Web 路径

Web 根目录可以自定义,例如当前目录下的 wwwroot 文件夹。用户访问的 URL 路径对应服务器上的文件路径。

定义 Web 根目录:

const string wwwroot = "./wwwroot";

读取网页内容:

static string ReadWebContent(string path) {
    ifstream in(path);
    if (!in.is_open()) return "404";
    string content;
    string line;
    while (getline(in, line)) {
        content += line;
    }
    in.close();
    return content;
}

解析请求路径:

class Request {
public:
    void Deserialize(string req) {
        string tmp;
        int pos = 0;
        const string sep = "\r\n";
        while (true) {
            pos = req.find(sep);
            if (pos == string::npos) break;
            string temp = req.substr(0, pos);
            if (temp.empty()) break;
            req_header.push_back(temp);
            req.erase(0, pos + sep.size());
        }
        parse();
        text = req;
        DebugPrint();
    }

    void parse() {
        stringstream ss(req_header[0]);
        ss >> method >> url >> http_version;
        path = wwwroot;
        if (url == "/" || url == "/index.html") {
            path += "/";
            path += homepage;
        } else {
            path += url;
        }
    }

    void DebugPrint() {
        cout << "------------------------------------------------------------" << endl;
        for(auto& e : req_header) {
            cout << e << endl << endl;
        }
        cout << method << endl << url << endl << http_version << endl << path << endl;
        cout << text << endl;
    }

public:
    vector<string> req_header;
    string text;
    string method;
    string url;
    string http_version;
    string path;
};

完整代码示例:

#ifndef BE0E1813_421A_4BCD_A33B_77432A3CA8D7
#define BE0E1813_421A_4BCD_A33B_77432A3CA8D7
#include<iostream>
#include"Socket.hpp"
#include"Log.hpp"
#include<sstream>
#include<pthread.h>
#include<vector>
#include<fstream>

static const int defaultport = 8080;
const string wwwroot = "./wwwroot";
const string sep = "\r\n";
const string homepage = "index.html";

struct ThreadData {
    ThreadData(int sock) : sockfd(sock) {}
    int sockfd;
};

class Request {
public:
    void Deserialize(string req) {
        string tmp;
        int pos = 0;
        while (true) {
            pos = req.find(sep);
            if (pos == string::npos) break;
            string temp = req.substr(0, pos);
            if (temp.empty()) break;
            req_header.push_back(temp);
            req.erase(0, pos + sep.size());
        }
        parse();
        text = req;
        DebugPrint();
    }

    void parse() {
        stringstream ss(req_header[0]);
        ss >> method >> url >> http_version;
        path = wwwroot;
        if (url == "/" || url == "/index.html") {
            path += "/";
            path += homepage;
        } else {
            path += url;
        }
    }

    void DebugPrint() {
        cout << "------------------------------------------------------------" << endl;
        for(auto& e : req_header) {
            cout << e << endl << endl;
        }
        cout << method << endl << url << endl << http_version << endl << path << endl;
        cout << text << endl;
    }

public:
    vector<string> req_header;
    string text;
    string method;
    string url;
    string http_version;
    string path;
};

class HttpServer {
public:
    HttpServer(uint16_t port = defaultport) :port_(port) {}
    static string ReadWebContent(string str) {
        ifstream in(str);
        if (!in.is_open()) return "404";
        string content;
        string line;
        while (getline(in, line)) {
            content += line;
        }
        in.close();
        return content;
    }
    static void HandlerHttp(int sockfd) {
        char buffer[10240];
        ssize_t n = recv(sockfd, buffer, sizeof(buffer) - 1, 0);
        if(n > 0) {
            buffer[n] = 0;
            Request req;
            req.Deserialize(buffer);
            req.DebugPrint();

            string text = ReadWebContent(req.path);
            string response_line = "HTTP/1.0 200 OK\r\n";
            string response_header = "Content-Length: ";
            response_header += to_string(text.size());
            response_header += "\r\n";
            string blank_line = "\r\n";

            string response;
            response += response_line;
            response += response_header;
            response += blank_line;
            response += text;

            send(sockfd, response.c_str(), response.size(), 0);
        }
        close(sockfd);
    }
    static void* ThreadRun(void* args) {
        pthread_detach(pthread_self());
        ThreadData* td = static_cast<ThreadData*>(args);
        HandlerHttp(td->sockfd);
        delete td;
        return nullptr;
    }
    void start() {
        listensock_.InitSocket();
        listensock_.Bind(port_);
        listensock_.Listen();
        for (;;) {
            string clientip;
            uint16_t clientport;
            int sockfd = listensock_.Accept(&clientip, &clientport);
            pthread_t tid;
            ThreadData* td = new ThreadData(sockfd);
            td->sockfd = sockfd;
            pthread_create(&tid, nullptr, ThreadRun, td);
        }
    }
    ~HttpServer() {};
private:
    Socket listensock_;
    uint16_t port_;
};
#endif /* BE0E1813_421A_4BCD_A33B_77432A3CA8D7 */

通过以上步骤,我们实现了基于 C++ 的简单 HTTP 服务器,能够根据 Web 路径解析请求并返回对应的静态资源。

目录

  1. 准备文件
  2. Makefile
  3. HttpServer.hpp
  4. 类内成员
  5. 封装 sockfd
  6. start
  7. ThreadRun
  8. 响应书写
  9. Web 路径

更多推荐文章

查看全部
  • HTML 标签详解:网页结构、文本、表单与列表基础
  • OpenClaw 跨平台安装指南:Windows、macOS 及 Linux 环境配置
  • Spec-Kit 与 Copilot 实现 AI 规格驱动开发
  • 基于 Web Components 的跨框架组件库实践
  • 常见 Web 漏洞渗透及防护方法
  • Stable Diffusion 入门:稳定生成人物脸部与姿势控制技巧
  • 大模型辅助强化学习的四条主流技术路线综述
  • Linux 进程间通信进阶:管道与共享内存详解
  • Java 响应式编程:Flux 与 SseEmitter 深度解析
  • 基于 WebGL 的广义相对论黑洞吸积盘着色器实现
  • 软件解耦与扩展:基于 C++ 与 C# 的插件式开发
  • 前端三年职业回顾:理想与现实的碰撞
  • Spring Boot 数据仓库与 ETL 工具集成
  • NativeScript-Vue 实战:用 Vue 语法构建高性能原生应用
  • OpenClaw、猎豹 EasyClaw 与腾讯 WorkBuddy 安装及对比指南
  • Coze 全解析:100 个落地用途与发布使用指南
  • AIGC 驱动下的虚拟人物创作与智能交互技术解析
  • AI 视频生成模型开源实战:从选型到生产环境部署的避坑指南
  • 基于 ESP32-C5 的 Moji 2.0 小智 AI 桌面机器人复刻方案
  • Spring Boot 药品进销存管理系统:前后端分离架构与核心功能解析

相关免费在线工具

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online

  • JSON 压缩

    通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online

  • JSON美化和格式化

    将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online