强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

C/C++ Linux 开发教程(GCC + CMake) / 网络编程基础(Socket/epoll)

网络编程基础(Socket/epoll)

1. TCP Socket 编程基本流程

服务器端                           客户端
┌───────────────┐                ┌───────────────┐
│  socket()     │                │  socket()     │
│     ↓         │                │     ↓         │
│  bind()       │                │  connect()    │
│     ↓         │   三次握手      │     ↓         │
│  listen()     │ ←────────────→ │               │
│     ↓         │                │     ↓         │
│  accept()     │                │  send/recv    │
│     ↓         │   数据交换      │     ↓         │
│  send/recv    │ ←────────────→ │               │
│     ↓         │                │     ↓         │
│  close()      │   四次挥手      │  close()      │
└───────────────┘ ←────────────→ └───────────────┘

2. 基本 TCP 服务器

#include <iostream>
#include <cstring>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main() {
    // 1. 创建 socket
    int server_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (server_fd < 0) {
        perror("socket");
        return 1;
    }

    // 设置 SO_REUSEADDR(避免 "Address already in use")
    int opt = 1;
    setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    // 2. 绑定地址
    struct sockaddr_in addr{};
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;  // 监听所有接口
    addr.sin_port = htons(8080);         // 端口 8080

    if (bind(server_fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
        perror("bind");
        close(server_fd);
        return 1;
    }

    // 3. 监听
    if (listen(server_fd, SOMAXCONN) < 0) {
        perror("listen");
        close(server_fd);
        return 1;
    }

    std::cout << "服务器监听端口 8080...\n";

    // 4. 接受连接
    struct sockaddr_in client_addr{};
    socklen_t client_len = sizeof(client_addr);
    int client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &client_len);
    if (client_fd < 0) {
        perror("accept");
        close(server_fd);
        return 1;
    }

    char client_ip[INET_ADDRSTRLEN];
    inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, sizeof(client_ip));
    std::cout << "客户端连接: " << client_ip << ":" << ntohs(client_addr.sin_port) << "\n";

    // 5. 收发数据
    char buffer[1024];
    while (true) {
        memset(buffer, 0, sizeof(buffer));
        ssize_t n = recv(client_fd, buffer, sizeof(buffer) - 1, 0);
        if (n <= 0) {
            if (n == 0) std::cout << "客户端断开连接\n";
            else perror("recv");
            break;
        }

        std::cout << "收到: " << buffer;

        // 回显
        send(client_fd, buffer, n, 0);
    }

    // 6. 关闭
    close(client_fd);
    close(server_fd);

    return 0;
}

编译运行:

g++ -std=c++17 -o server server.cpp && ./server

3. TCP 客户端

#include <iostream>
#include <cstring>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main() {
    // 1. 创建 socket
    int sock_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (sock_fd < 0) {
        perror("socket");
        return 1;
    }

    // 2. 连接服务器
    struct sockaddr_in server_addr{};
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(8080);
    inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr);

    if (connect(sock_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
        perror("connect");
        close(sock_fd);
        return 1;
    }

    std::cout << "已连接服务器\n";

    // 3. 收发数据
    const char* messages[] = {"Hello\n", "World\n", "你好\n"};
    for (const auto* msg : messages) {
        send(sock_fd, msg, strlen(msg), 0);

        char buffer[1024] = {};
        ssize_t n = recv(sock_fd, buffer, sizeof(buffer) - 1, 0);
        if (n > 0) {
            std::cout << "服务器回复: " << buffer;
        }

        sleep(1);
    }

    // 4. 关闭
    close(sock_fd);
    std::cout << "连接已关闭\n";

    return 0;
}

4. UDP Socket

#include <iostream>
#include <cstring>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

// UDP 服务器(接收端)
void udp_server() {
    int fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (fd < 0) { perror("socket"); return; }

    struct sockaddr_in addr{};
    addr.sin_family = AF_INET;
    addr.sin_port = htons(9090);
    addr.sin_addr.s_addr = INADDR_ANY;

    if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
        perror("bind"); close(fd); return;
    }

    std::cout << "UDP 服务器监听端口 9090\n";

    char buffer[1024];
    struct sockaddr_in client_addr{};
    socklen_t client_len = sizeof(client_addr);

    while (true) {
        ssize_t n = recvfrom(fd, buffer, sizeof(buffer) - 1, 0,
                             (struct sockaddr*)&client_addr, &client_len);
        if (n < 0) { perror("recvfrom"); break; }
        buffer[n] = '\0';

        char ip[INET_ADDRSTRLEN];
        inet_ntop(AF_INET, &client_addr.sin_addr, ip, sizeof(ip));
        std::cout << "来自 " << ip << ":" << ntohs(client_addr.sin_port)
                  << " → " << buffer;

        // 回复
        sendto(fd, buffer, n, 0, (struct sockaddr*)&client_addr, client_len);
    }

    close(fd);
}

// UDP 客户端(发送端)
void udp_client() {
    int fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (fd < 0) { perror("socket"); return; }

    struct sockaddr_in server_addr{};
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(9090);
    inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr);

    const char* msg = "UDP Hello\n";
    sendto(fd, msg, strlen(msg), 0,
           (struct sockaddr*)&server_addr, sizeof(server_addr));

    char buffer[1024] = {};
    ssize_t n = recvfrom(fd, buffer, sizeof(buffer) - 1, 0, nullptr, nullptr);
    if (n > 0) {
        buffer[n] = '\0';
        std::cout << "服务器回复: " << buffer;
    }

    close(fd);
}
特性TCPUDP
连接面向连接(三次握手)无连接
可靠性可靠(保证送达、顺序)不可靠
速度相对较慢
适用场景HTTP、文件传输、数据库视频流、DNS、游戏
APIsend / recvsendto / recvfrom

5. Socket 地址结构与字节序转换

#include <iostream>
#include <cstring>
#include <arpa/inet.h>

int main() {
    // sockaddr_in 结构
    struct sockaddr_in addr{};
    addr.sin_family = AF_INET;
    addr.sin_port = htons(8080);           // 主机字节序 → 网络字节序
    inet_pton(AF_INET, "192.168.1.100", &addr.sin_addr);

    // 字节序转换
    uint16_t host_port = 8080;
    uint16_t net_port = htons(host_port);   // host to network short
    uint16_t back = ntohs(net_port);         // network to host short

    uint32_t host_val = 0x12345678;
    uint32_t net_val = htonl(host_val);      // host to network long
    uint32_t host_back = ntohl(net_val);     // network to host long

    std::cout << "端口: " << host_port << " → " << net_port << " → " << back << "\n";
    std::cout << "0x12345678 → 0x" << std::hex << net_val
              << " → 0x" << host_back << "\n";

    // IP 地址转换
    // 文本 → 二进制
    struct in_addr ip;
    inet_pton(AF_INET, "192.168.1.100", &ip);
    std::cout << "IP (binary): 0x" << std::hex << ip.s_addr << "\n";

    // 二进制 → 文本
    char ip_str[INET_ADDRSTRLEN];
    inet_ntop(AF_INET, &ip, ip_str, sizeof(ip_str));
    std::cout << "IP (text): " << ip_str << "\n";

    // 旧函数(不推荐,但代码中常见)
    // inet_addr("192.168.1.100") → in_addr_t
    // inet_ntoa(ip) → char*

    return 0;
}
函数作用
htons()16 位主机序 → 网络序
ntohs()16 位网络序 → 主机序
htonl()32 位主机序 → 网络序
ntohl()32 位网络序 → 主机序
inet_pton()文本 IP → 二进制(推荐)
inet_ntop()二进制 → 文本 IP(推荐)

⚠️ 注意:Linux x86/x86_64 是小端序(little-endian),网络字节序是大端序(big-endian),所以必须转换。


6. 多客户端处理:fork / select / poll

fork 多进程模型

#include <iostream>
#include <cstring>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <signal.h>
#include <sys/wait.h>

void handle_client(int client_fd) {
    char buffer[1024];
    while (true) {
        ssize_t n = recv(client_fd, buffer, sizeof(buffer) - 1, 0);
        if (n <= 0) break;
        buffer[n] = '\0';
        send(client_fd, buffer, n, 0);
    }
    close(client_fd);
}

int main() {
    signal(SIGCHLD, SIG_IGN);  // 自动回收子进程

    int server_fd = socket(AF_INET, SOCK_STREAM, 0);
    int opt = 1;
    setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    struct sockaddr_in addr{};
    addr.sin_family = AF_INET;
    addr.sin_port = htons(8080);
    addr.sin_addr.s_addr = INADDR_ANY;
    bind(server_fd, (struct sockaddr*)&addr, sizeof(addr));
    listen(server_fd, 128);

    std::cout << "fork 模型服务器启动\n";

    while (true) {
        struct sockaddr_in client_addr{};
        socklen_t len = sizeof(client_addr);
        int client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &len);
        if (client_fd < 0) continue;

        pid_t pid = fork();
        if (pid == 0) {
            close(server_fd);
            handle_client(client_fd);
            exit(0);
        }
        close(client_fd);
    }

    return 0;
}

select 多路复用

#include <iostream>
#include <cstring>
#include <unistd.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <vector>

int main() {
    int server_fd = socket(AF_INET, SOCK_STREAM, 0);
    int opt = 1;
    setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    struct sockaddr_in addr{};
    addr.sin_family = AF_INET;
    addr.sin_port = htons(8080);
    addr.sin_addr.s_addr = INADDR_ANY;
    bind(server_fd, (struct sockaddr*)&addr, sizeof(addr));
    listen(server_fd, 128);

    fd_set readfds;
    int max_fd = server_fd;
    std::vector<int> clients;

    std::cout << "select 模型服务器启动\n";

    while (true) {
        FD_ZERO(&readfds);
        FD_SET(server_fd, &readfds);

        for (int fd : clients) {
            FD_SET(fd, &readfds);
            if (fd > max_fd) max_fd = fd;
        }

        int activity = select(max_fd + 1, &readfds, nullptr, nullptr, nullptr);
        if (activity < 0) { perror("select"); continue; }

        // 新连接
        if (FD_ISSET(server_fd, &readfds)) {
            struct sockaddr_in client_addr{};
            socklen_t len = sizeof(client_addr);
            int client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &len);
            if (client_fd >= 0) {
                clients.push_back(client_fd);
                std::cout << "新客户端: fd=" << client_fd << "\n";
            }
        }

        // 客户端数据
        for (auto it = clients.begin(); it != clients.end(); ) {
            int fd = *it;
            if (FD_ISSET(fd, &readfds)) {
                char buffer[1024];
                ssize_t n = recv(fd, buffer, sizeof(buffer), 0);
                if (n <= 0) {
                    std::cout << "客户端断开: fd=" << fd << "\n";
                    close(fd);
                    it = clients.erase(it);
                    continue;
                }
                send(fd, buffer, n, 0);
            }
            ++it;
        }
    }

    return 0;
}
模型最大连接数性能适用场景
fork受进程数限制高内存开销少量长连接
selectFD_SETSIZE (1024)O(n) 遍历小规模连接
poll无硬限制O(n) 遍历中等规模
epoll百万级O(1) 事件通知高并发服务器

7. epoll:边缘触发 vs 水平触发

#include <iostream>
#include <cstring>
#include <unistd.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <vector>
#include <unordered_map>

// 设置非阻塞
void set_nonblocking(int fd) {
    int flags = fcntl(fd, F_GETFL, 0);
    fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}

int main() {
    int server_fd = socket(AF_INET, SOCK_STREAM, 0);
    int opt = 1;
    setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    setsockopt(server_fd, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt));

    struct sockaddr_in addr{};
    addr.sin_family = AF_INET;
    addr.sin_port = htons(8080);
    addr.sin_addr.s_addr = INADDR_ANY;
    bind(server_fd, (struct sockaddr*)&addr, sizeof(addr));
    listen(server_fd, 128);

    // 创建 epoll 实例
    int epoll_fd = epoll_create1(0);
    if (epoll_fd < 0) { perror("epoll_create1"); return 1; }

    // 注册 server_fd
    struct epoll_event ev{};
    ev.events = EPOLLIN;
    ev.data.fd = server_fd;
    epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &ev);

    std::unordered_map<int, std::string> client_names;
    constexpr int MAX_EVENTS = 64;

    std::cout << "epoll 服务器启动,端口 8080\n";

    while (true) {
        struct epoll_event events[MAX_EVENTS];
        int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);

        for (int i = 0; i < nfds; ++i) {
            int fd = events[i].data.fd;

            if (fd == server_fd) {
                // 新连接
                struct sockaddr_in client_addr{};
                socklen_t len = sizeof(client_addr);
                int client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &len);
                if (client_fd < 0) continue;

                set_nonblocking(client_fd);
                ev.events = EPOLLIN | EPOLLET;  // 边缘触发
                ev.data.fd = client_fd;
                epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &ev);

                client_names[client_fd] = "client_" + std::to_string(client_fd);
                std::cout << "新连接: fd=" << client_fd << "\n";

            } else if (events[i].events & EPOLLIN) {
                // 边缘触发必须一次性读完所有数据
                char buffer[4096];
                while (true) {
                    ssize_t n = recv(fd, buffer, sizeof(buffer), 0);
                    if (n < 0) {
                        if (errno == EAGAIN || errno == EWOULDBLOCK) break;  // 数据读完
                        perror("recv");
                        close(fd);
                        epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, nullptr);
                        client_names.erase(fd);
                        break;
                    } else if (n == 0) {
                        std::cout << "客户端断开: fd=" << fd << "\n";
                        close(fd);
                        epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, nullptr);
                        client_names.erase(fd);
                        break;
                    } else {
                        send(fd, buffer, n, 0);  // 回显
                    }
                }

            } else if (events[i].events & (EPOLLERR | EPOLLHUP)) {
                std::cout << "连接错误: fd=" << fd << "\n";
                close(fd);
                epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, nullptr);
                client_names.erase(fd);
            }
        }
    }

    close(epoll_fd);
    close(server_fd);
    return 0;
}
触发模式行为编程复杂度适用场景
水平触发 (LT)只要缓冲区有数据就持续通知简单默认模式
边缘触发 (ET)仅在状态变化时通知一次复杂高性能场景

⚠️ 注意:边缘触发(ET)必须使用非阻塞 socket,并且必须循环读取直到 EAGAIN,否则会丢失数据。


8. 非阻塞 Socket

#include <iostream>
#include <cstring>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

void set_nonblocking(int fd) {
    int flags = fcntl(fd, F_GETFL, 0);
    fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}

int main() {
    int sock_fd = socket(AF_INET, SOCK_STREAM, 0);
    set_nonblocking(sock_fd);

    struct sockaddr_in addr{};
    addr.sin_family = AF_INET;
    addr.sin_port = htons(8080);
    inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr);

    // 非阻塞 connect
    int ret = connect(sock_fd, (struct sockaddr*)&addr, sizeof(addr));
    if (ret < 0) {
        if (errno == EINPROGRESS) {
            std::cout << "连接正在进行中(非阻塞)\n";
            // 用 epoll/select 等待可写事件
        } else {
            perror("connect");
        }
    }

    // 非阻塞 recv
    char buffer[1024];
    ssize_t n = recv(sock_fd, buffer, sizeof(buffer), 0);
    if (n < 0) {
        if (errno == EAGAIN || errno == EWOULDBLOCK) {
            std::cout << "没有数据可读(非阻塞)\n";
        } else {
            perror("recv");
        }
    }

    close(sock_fd);
    return 0;
}

9. 简单 HTTP 请求解析

#include <iostream>
#include <string>
#include <sstream>
#include <unordered_map>
#include <algorithm>

struct HttpRequest {
    std::string method;
    std::string path;
    std::string version;
    std::unordered_map<std::string, std::string> headers;
    std::string body;
};

// 解析 HTTP 请求
HttpRequest parse_http_request(const std::string& raw) {
    HttpRequest req;
    std::istringstream stream(raw);
    std::string line;

    // 请求行: GET /path HTTP/1.1
    if (std::getline(stream, line)) {
        // 移除 \r
        if (!line.empty() && line.back() == '\r') line.pop_back();

        std::istringstream line_stream(line);
        line_stream >> req.method >> req.path >> req.version;
    }

    // 头部
    while (std::getline(stream, line)) {
        if (!line.empty() && line.back() == '\r') line.pop_back();
        if (line.empty()) break;  // 空行分隔头部和正文

        auto colon = line.find(':');
        if (colon != std::string::npos) {
            std::string key = line.substr(0, colon);
            std::string value = line.substr(colon + 1);
            // 去除前导空格
            value.erase(0, value.find_first_not_of(" \t"));
            req.headers[key] = value;
        }
    }

    // 正文(读取剩余内容)
    std::string remaining;
    while (std::getline(stream, line)) {
        remaining += line + "\n";
    }
    req.body = remaining;

    return req;
}

int main() {
    std::string raw =
        "GET /index.html HTTP/1.1\r\n"
        "Host: example.com\r\n"
        "User-Agent: curl/7.68.0\r\n"
        "Accept: */*\r\n"
        "\r\n";

    auto req = parse_http_request(raw);
    std::cout << "方法: " << req.method << "\n";
    std::cout << "路径: " << req.path << "\n";
    std::cout << "版本: " << req.version << "\n";
    std::cout << "头部:\n";
    for (const auto& [k, v] : req.headers) {
        std::cout << "  " << k << ": " << v << "\n";
    }

    return 0;
}

10. 简单 HTTP 服务器实现

#include <iostream>
#include <string>
#include <sstream>
#include <cstring>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <unordered_map>

void set_nonblocking(int fd) {
    fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
}

std::string build_response(int status, const std::string& status_text,
                           const std::string& content_type,
                           const std::string& body) {
    std::ostringstream resp;
    resp << "HTTP/1.1 " << status << " " << status_text << "\r\n"
         << "Content-Type: " << content_type << "\r\n"
         << "Content-Length: " << body.size() << "\r\n"
         << "Connection: close\r\n"
         << "\r\n"
         << body;
    return resp.str();
}

std::string handle_request(const std::string& method, const std::string& path) {
    if (path == "/" || path == "/index.html") {
        return build_response(200, "OK", "text/html; charset=utf-8",
            "<html><body><h1>Hello from C++ HTTP Server!</h1>"
            "<p>这是用纯 C++ 写的 HTTP 服务器</p></body></html>");
    } else if (path == "/api/time") {
        time_t now = time(nullptr);
        return build_response(200, "OK", "application/json",
            "{\"time\": \"" + std::string(ctime(&now)) + "\"}");
    } else {
        return build_response(404, "Not Found", "text/html",
            "<html><body><h1>404 Not Found</h1></body></html>");
    }
}

int main() {
    int server_fd = socket(AF_INET, SOCK_STREAM, 0);
    int opt = 1;
    setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    setsockopt(server_fd, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt));

    struct sockaddr_in addr{};
    addr.sin_family = AF_INET;
    addr.sin_port = htons(8080);
    addr.sin_addr.s_addr = INADDR_ANY;
    bind(server_fd, (struct sockaddr*)&addr, sizeof(addr));
    listen(server_fd, 128);
    set_nonblocking(server_fd);

    int epoll_fd = epoll_create1(0);
    struct epoll_event ev{};
    ev.events = EPOLLIN;
    ev.data.fd = server_fd;
    epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &ev);

    std::cout << "HTTP 服务器启动: http://0.0.0.0:8080\n";

    constexpr int MAX_EVENTS = 64;
    while (true) {
        struct epoll_event events[MAX_EVENTS];
        int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);

        for (int i = 0; i < nfds; ++i) {
            int fd = events[i].data.fd;

            if (fd == server_fd) {
                struct sockaddr_in client_addr{};
                socklen_t len = sizeof(client_addr);
                int client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &len);
                if (client_fd >= 0) {
                    set_nonblocking(client_fd);
                    ev.events = EPOLLIN | EPOLLET;
                    ev.data.fd = client_fd;
                    epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &ev);
                }
            } else if (events[i].events & EPOLLIN) {
                char buffer[4096];
                std::string request_data;
                while (true) {
                    ssize_t n = recv(fd, buffer, sizeof(buffer), 0);
                    if (n < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) break;
                    if (n <= 0) break;
                    request_data.append(buffer, n);
                }

                if (!request_data.empty()) {
                    // 简单解析请求行
                    std::istringstream stream(request_data);
                    std::string method, path, version;
                    stream >> method >> path >> version;

                    std::string response = handle_request(method, path);
                    send(fd, response.c_str(), response.size(), 0);
                }
                close(fd);
                epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, nullptr);
            }
        }
    }

    close(epoll_fd);
    close(server_fd);
    return 0;
}

编译测试:

g++ -std=c++17 -o http_server http_server.cpp && ./http_server
# 另一个终端:
curl http://localhost:8080/
curl http://localhost:8080/api/time

11. 网络库简介

特点适用场景
libevent成熟稳定,跨平台通用网络编程
libuvNode.js 底层,异步 I/O高性能异步服务
Boost.AsioC++ 标准提案,功能全面C++ 项目首选
libev轻量级事件循环嵌入式/轻量场景
muduo陈硕出品,Reactor 模式学习网络编程

libevent 最简示例

// 编译: g++ -std=c++17 -o demo demo.cpp -levent
#include <event2/listener.h>
#include <event2/bufferevent.h>
#include <event2/buffer.h>
#include <iostream>

void read_cb(struct bufferevent* bev, void* ctx) {
    struct evbuffer* input = bufferevent_get_input(bev);
    char buffer[1024];
    ssize_t n;
    while ((n = evbuffer_remove(input, buffer, sizeof(buffer))) > 0) {
        bufferevent_write(bev, buffer, n);  // 回显
    }
}

void accept_cb(struct evconnlistener* listener, evutil_socket_t fd,
               struct sockaddr* addr, int socklen, void* ctx) {
    struct event_base* base = (struct event_base*)ctx;
    struct bufferevent* bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
    bufferevent_setcb(bev, read_cb, nullptr, nullptr, nullptr);
    bufferevent_enable(bev, EV_READ | EV_WRITE);
}

int main() {
    struct event_base* base = event_base_new();

    struct sockaddr_in addr{};
    addr.sin_family = AF_INET;
    addr.sin_port = htons(8080);
    addr.sin_addr.s_addr = INADDR_ANY;

    struct evconnlistener* listener = evconnlistener_new_bind(
        base, accept_cb, base, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE,
        128, (struct sockaddr*)&addr, sizeof(addr));

    std::cout << "libevent 回显服务器启动\n";
    event_base_dispatch(base);

    evconnlistener_free(listener);
    event_base_free(base);
    return 0;
}

扩展阅读