网络编程所有通信方式总结 C++ 实现模板(基于 C++11+)

本文档总结《第五章:网络编程》中所有提及的通信方式,按 "核心特点→实现步骤→C++ 简要模板" 结构整理,突出核心流程,适配 C++11 + 语法(含 auto、范围 for 等现代特性)。

一、TCP 面向连接通信(可靠传输)

核心特点
  • 面向连接、可靠传输(无丢失 / 重复 / 失序)、字节流、三次握手 / 四次挥手
  • 适用场景:登录、文件传输、关键数据交互(如订单、支付)

实现步骤(服务器端)

  1. 创建 TCP 套接字(SOCK_STREAM
  2. 设置地址重用(避免端口占用)
  3. 绑定 IP 和端口(bind
  4. 启动监听(listen
  5. 循环阻塞等待客户端连接(accept
  6. 与客户端收发数据(recv/send
  7. 关闭套接字(close

实现步骤(客户端)

  1. 创建 TCP 套接字(SOCK_STREAM
  2. 连接服务器(connect
  3. 与服务器收发数据(send/recv
  4. 关闭套接字(close

C++ 模板代码

服务器端

cpp

运行

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

constexpr int SER_PORT = 8888;
const std::string SER_IP = "192.168.1.100";

int main() {
    // 1. 创建TCP套接字
    int sfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sfd == -1) { perror("socket"); return -1; }

    // 2. 设置地址重用
    int opt = 1;
    setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    // 3. 绑定IP和端口
    struct sockaddr_in ser_addr;
    memset(&ser_addr, 0, sizeof(ser_addr));
    ser_addr.sin_family = AF_INET;
    ser_addr.sin_port = htons(SER_PORT);
    ser_addr.sin_addr.s_addr = inet_addr(SER_IP.c_str());
    if (bind(sfd, (struct sockaddr*)&ser_addr, sizeof(ser_addr)) == -1) { perror("bind"); return -1; }

    // 4. 启动监听
    if (listen(sfd, 128) == -1) { perror("listen"); return -1; }
    std::cout << "TCP服务器监听中..." << std::endl;

    // 5. 等待客户端连接
    struct sockaddr_in cli_addr;
    socklen_t cli_len = sizeof(cli_addr);
    int newfd = accept(sfd, (struct sockaddr*)&cli_addr, &cli_len);
    if (newfd == -1) { perror("accept"); return -1; }
    std::cout << "客户端[" << inet_ntoa(cli_addr.sin_addr) << ":" << ntohs(cli_addr.sin_port) << "]连接成功" << std::endl;

    // 6. 收发数据
    char buf[1024] = {0};
    while (true) {
        memset(buf, 0, sizeof(buf));
        ssize_t n = recv(newfd, buf, sizeof(buf)-1, 0);
        if (n <= 0) { break; } // 客户端下线或出错
        std::cout << "收到客户端消息: " << buf << std::endl;
        strcat(buf, "*_*");
        send(newfd, buf, strlen(buf), 0); // 回显
    }

    // 7. 关闭套接字
    close(newfd);
    close(sfd);
    return 0;
}
客户端

cpp

运行

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

constexpr int SER_PORT = 8888;
const std::string SER_IP = "192.168.1.100";

int main() {
    // 1. 创建TCP套接字
    int cfd = socket(AF_INET, SOCK_STREAM, 0);
    if (cfd == -1) { perror("socket"); return -1; }

    // 2. 连接服务器
    struct sockaddr_in ser_addr;
    memset(&ser_addr, 0, sizeof(ser_addr));
    ser_addr.sin_family = AF_INET;
    ser_addr.sin_port = htons(SER_PORT);
    ser_addr.sin_addr.s_addr = inet_addr(SER_IP.c_str());
    if (connect(cfd, (struct sockaddr*)&ser_addr, sizeof(ser_addr)) == -1) { perror("connect"); return -1; }

    // 3. 收发数据
    char buf[1024] = {0};
    while (true) {
        memset(buf, 0, sizeof(buf));
        std::cout << "请输入消息: ";
        fgets(buf, sizeof(buf), stdin);
        buf[strcspn(buf, "\n")] = '\0'; // 去除换行符
        send(cfd, buf, strlen(buf), 0);
        
        memset(buf, 0, sizeof(buf));
        ssize_t n = recv(cfd, buf, sizeof(buf)-1, 0);
        if (n <= 0) { break; }
        std::cout << "服务器回复: " << buf << std::endl;
    }

    // 4. 关闭套接字
    close(cfd);
    return 0;
}

二、UDP 面向无连接通信(快速传输)

核心特点
  • 无连接、不可靠(可能丢失 / 乱序)、数据报、低延迟
  • 适用场景:通知、广播、组播、实时音视频

实现步骤(服务器端 + 客户端)

  1. 创建 UDP 套接字(SOCK_DGRAM
  2. 绑定 IP 和端口(服务器必选,客户端可选)
  3. 收发数据(recvfrom/sendto,需指定对端地址)
  4. 关闭套接字

C++ 模板代码

服务器端

cpp

运行

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

constexpr int SER_PORT = 9999;
const std::string SER_IP = "192.168.1.100";

int main() {
    // 1. 创建UDP套接字
    int sfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sfd == -1) { perror("socket"); return -1; }

    // 2. 绑定IP和端口
    struct sockaddr_in ser_addr;
    memset(&ser_addr, 0, sizeof(ser_addr));
    ser_addr.sin_family = AF_INET;
    ser_addr.sin_port = htons(SER_PORT);
    ser_addr.sin_addr.s_addr = inet_addr(SER_IP.c_str());
    if (bind(sfd, (struct sockaddr*)&ser_addr, sizeof(ser_addr)) == -1) { perror("bind"); return -1; }

    // 3. 收发数据
    char buf[1024] = {0};
    struct sockaddr_in cli_addr;
    socklen_t cli_len = sizeof(cli_addr);
    while (true) {
        memset(buf, 0, sizeof(buf));
        ssize_t n = recvfrom(sfd, buf, sizeof(buf)-1, 0, (struct sockaddr*)&cli_addr, &cli_len);
        if (n <= 0) { continue; }
        std::cout << "收到[" << inet_ntoa(cli_addr.sin_addr) << ":" << ntohs(cli_addr.sin_port) << "]消息: " << buf << std::endl;
        
        strcat(buf, "*_*");
        sendto(sfd, buf, strlen(buf), 0, (struct sockaddr*)&cli_addr, cli_len); // 回显
    }

    // 4. 关闭套接字
    close(sfd);
    return 0;
}
客户端

cpp

运行

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

constexpr int SER_PORT = 9999;
const std::string SER_IP = "192.168.1.100";

int main() {
    // 1. 创建UDP套接字
    int cfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (cfd == -1) { perror("socket"); return -1; }

    // 2. 初始化服务器地址
    struct sockaddr_in ser_addr;
    memset(&ser_addr, 0, sizeof(ser_addr));
    ser_addr.sin_family = AF_INET;
    ser_addr.sin_port = htons(SER_PORT);
    ser_addr.sin_addr.s_addr = inet_addr(SER_IP.c_str());

    // 3. 收发数据
    char buf[1024] = {0};
    socklen_t ser_len = sizeof(ser_addr);
    while (true) {
        memset(buf, 0, sizeof(buf));
        std::cout << "请输入消息: ";
        fgets(buf, sizeof(buf), stdin);
        buf[strcspn(buf, "\n")] = '\0';
        sendto(cfd, buf, strlen(buf), 0, (struct sockaddr*)&ser_addr, ser_len);
        
        memset(buf, 0, sizeof(buf));
        recvfrom(cfd, buf, sizeof(buf)-1, 0, NULL, NULL);
        std::cout << "服务器回复: " << buf << std::endl;
    }

    // 4. 关闭套接字
    close(cfd);
    return 0;
}

三、多进程 TCP 并发服务器

核心特点
  • 主进程监听连接,子进程独立处理单个客户端通信
  • 解决单进程只能处理一个客户端的问题,需回收僵尸进程

实现步骤

  1. 创建 TCP 套接字、绑定、监听(同基础 TCP 服务器)
  2. 注册信号处理函数(回收僵尸进程)
  3. 循环accept接收客户端连接
  4. fork创建子进程,子进程处理收发数据
  5. 父进程关闭newfd,循环等待新连接

C++ 模板代码

cpp

运行

复制代码
#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>

constexpr int SER_PORT = 8888;
const std::string SER_IP = "192.168.1.100";

// 回收僵尸进程
void sig_handler(int sig) {
    while (waitpid(-1, NULL, WNOHANG) > 0); // 非阻塞回收所有子进程
}

int main() {
    // 注册信号处理函数
    signal(SIGCHLD, sig_handler);

    // 1. 创建TCP套接字、绑定、监听(同基础TCP服务器)
    int sfd = socket(AF_INET, SOCK_STREAM, 0);
    int opt = 1;
    setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    struct sockaddr_in ser_addr = {AF_INET, htons(SER_PORT), inet_addr(SER_IP.c_str())};
    bind(sfd, (struct sockaddr*)&ser_addr, sizeof(ser_addr));
    listen(sfd, 128);

    std::cout << "多进程TCP服务器启动,监听端口" << SER_PORT << std::endl;
    while (true) {
        // 2. 接收客户端连接
        struct sockaddr_in cli_addr;
        socklen_t cli_len = sizeof(cli_addr);
        int newfd = accept(sfd, (struct sockaddr*)&cli_addr, &cli_len);
        if (newfd == -1) { continue; }
        std::cout << "客户端[" << inet_ntoa(cli_addr.sin_addr) << "]连接,newfd=" << newfd << std::endl;

        // 3. 创建子进程
        pid_t pid = fork();
        if (pid == 0) { // 子进程
            close(sfd); // 子进程不需要监听套接字
            char buf[1024] = {0};
            while (true) {
                memset(buf, 0, sizeof(buf));
                ssize_t n = recv(newfd, buf, sizeof(buf)-1, 0);
                if (n <= 0) { break; }
                std::cout << "子进程[" << getpid() << "]收到消息: " << buf << std::endl;
                strcat(buf, "*_*");
                send(newfd, buf, strlen(buf), 0);
            }
            close(newfd);
            exit(EXIT_SUCCESS); // 子进程退出
        } else if (pid > 0) { // 父进程
            close(newfd); // 父进程不需要通信套接字
        }
    }

    close(sfd);
    return 0;
}

四、多线程 TCP 并发服务器

核心特点
  • 主线程监听连接,分支线程处理客户端通信
  • 资源开销比多进程小,需注意线程安全(本模板简化,无共享资源)

实现步骤

  1. 创建 TCP 套接字、绑定、监听
  2. 循环accept接收连接
  3. 创建分支线程,传入newfd和客户端地址
  4. 线程分离(自动回收资源),线程内处理收发数据
  5. 线程退出(pthread_exit

C++ 模板代码

cpp

运行

复制代码
#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>

constexpr int SER_PORT = 8888;
const std::string SER_IP = "192.168.1.100";

// 线程参数结构体
struct ThreadArgs {
    int newfd;
    struct sockaddr_in cli_addr;
};

// 线程处理函数
void* cli_handler(void* arg) {
    ThreadArgs* args = static_cast<ThreadArgs*>(arg);
    int newfd = args->newfd;
    auto& cli_addr = args->cli_addr;
    delete args; // 释放参数内存

    pthread_detach(pthread_self()); // 线程分离,自动回收

    char buf[1024] = {0};
    while (true) {
        memset(buf, 0, sizeof(buf));
        ssize_t n = recv(newfd, buf, sizeof(buf)-1, 0);
        if (n <= 0) { break; }
        std::cout << "线程[" << pthread_self() << "]收到[" << inet_ntoa(cli_addr.sin_addr) << "]消息: " << buf << std::endl;
        strcat(buf, "*_*");
        send(newfd, buf, strlen(buf), 0);
    }

    close(newfd);
    pthread_exit(NULL);
}

int main() {
    // 1. 创建TCP套接字、绑定、监听
    int sfd = socket(AF_INET, SOCK_STREAM, 0);
    int opt = 1;
    setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    struct sockaddr_in ser_addr = {AF_INET, htons(SER_PORT), inet_addr(SER_IP.c_str())};
    bind(sfd, (struct sockaddr*)&ser_addr, sizeof(ser_addr));
    listen(sfd, 128);

    std::cout << "多线程TCP服务器启动,监听端口" << SER_PORT << std::endl;
    while (true) {
        // 2. 接收客户端连接
        struct sockaddr_in cli_addr;
        socklen_t cli_len = sizeof(cli_addr);
        int newfd = accept(sfd, (struct sockaddr*)&cli_addr, &cli_len);
        if (newfd == -1) { continue; }

        // 3. 创建线程参数
        ThreadArgs* args = new ThreadArgs{newfd, cli_addr};
        pthread_t tid;
        if (pthread_create(&tid, NULL, cli_handler, args) != 0) {
            perror("pthread_create");
            close(newfd);
            delete args;
        }
    }

    close(sfd);
    return 0;
}

五、select IO 多路复用(TCP 并发)

核心特点
  • 单进程管理多个文件描述符,监听可读 / 可写 / 异常事件
  • 缺点:文件描述符上限(1024)、每次需重置 fd_set

实现步骤

  1. 创建 TCP 套接字、绑定、监听
  2. 初始化fd_set(读集合),添加监听 fd 和标准输入
  3. 循环select阻塞等待就绪事件
  4. 判断哪个 fd 就绪,分别处理(监听 fd→accept;客户端 fd→收发;标准输入→终端命令)

C++ 模板代码

cpp

运行

复制代码
#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/select.h>

constexpr int SER_PORT = 8888;
const std::string SER_IP = "192.168.1.100";
constexpr int MAX_FD = 1024;

int main() {
    // 1. 创建TCP套接字、绑定、监听
    int sfd = socket(AF_INET, SOCK_STREAM, 0);
    int opt = 1;
    setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    struct sockaddr_in ser_addr = {AF_INET, htons(SER_PORT), inet_addr(SER_IP.c_str())};
    bind(sfd, (struct sockaddr*)&ser_addr, sizeof(ser_addr));
    listen(sfd, 128);

    // 2. 初始化fd_set
    fd_set readfds, tempfds;
    FD_ZERO(&readfds);
    FD_SET(sfd, &readfds); // 添加监听fd
    FD_SET(0, &readfds);  // 添加标准输入fd
    int max_fd = sfd;

    std::cout << "select服务器启动,监听端口" << SER_PORT << std::endl;
    while (true) {
        tempfds = readfds; // 备份(select会修改集合)
        // 3. 阻塞等待就绪事件
        int nfds = select(max_fd + 1, &tempfds, NULL, NULL, NULL);
        if (nfds == -1) { perror("select"); break; }

        // 4. 遍历所有fd,判断是否就绪
        for (int fd = 0; fd <= max_fd; ++fd) {
            if (FD_ISSET(fd, &tempfds)) {
                if (fd == sfd) { // 监听fd就绪→新连接
                    struct sockaddr_in cli_addr;
                    socklen_t cli_len = sizeof(cli_addr);
                    int newfd = accept(sfd, (struct sockaddr*)&cli_addr, &cli_len);
                    FD_SET(newfd, &readfds);
                    max_fd = std::max(max_fd, newfd);
                    std::cout << "客户端[" << inet_ntoa(cli_addr.sin_addr) << "]连接,newfd=" << newfd << std::endl;
                } else if (fd == 0) { // 标准输入就绪→终端命令
                    char buf[1024] = {0};
                    fgets(buf, sizeof(buf), stdin);
                    if (strcmp(buf, "quit\n") == 0) { goto END; } // 退出命令
                } else { // 客户端fd就绪→收发数据
                    char buf[1024] = {0};
                    ssize_t n = recv(fd, buf, sizeof(buf)-1, 0);
                    if (n <= 0) { // 客户端下线
                        FD_CLR(fd, &readfds);
                        close(fd);
                        std::cout << "客户端fd=" << fd << "下线" << std::endl;
                        continue;
                    }
                    std::cout << "收到fd=" << fd << "消息: " << buf << std::endl;
                    strcat(buf, "*_*");
                    send(fd, buf, strlen(buf), 0);
                }
            }
        }
    }

END:
    for (int fd = 0; fd <= max_fd; ++fd) {
        if (FD_ISSET(fd, &readfds)) { close(fd); }
    }
    return 0;
}

六、poll IO 多路复用(TCP 并发)

核心特点
  • pollfd结构体管理 fd,无 fd 上限,无需备份集合
  • 缺点:仍需遍历所有 fd 判断就绪状态

实现步骤

  1. 创建 TCP 套接字、绑定、监听
  2. 初始化pollfd数组(包含监听 fd 和标准输入)
  3. 循环poll阻塞等待就绪事件
  4. 遍历pollfd数组,判断revents,处理对应事件

C++ 模板代码

cpp

运行

复制代码
#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <poll.h>

constexpr int SER_PORT = 8888;
const std::string SER_IP = "192.168.1.100";
constexpr int MAX_POLLFD = 1024;

int main() {
    // 1. 创建TCP套接字、绑定、监听
    int sfd = socket(AF_INET, SOCK_STREAM, 0);
    int opt = 1;
    setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    struct sockaddr_in ser_addr = {AF_INET, htons(SER_PORT), inet_addr(SER_IP.c_str())};
    bind(sfd, (struct sockaddr*)&ser_addr, sizeof(ser_addr));
    listen(sfd, 128);

    // 2. 初始化pollfd数组
    struct pollfd fds[MAX_POLLFD];
    fds[0].fd = sfd;
    fds[0].events = POLLIN; // 监听读事件
    fds[1].fd = 0;
    fds[1].events = POLLIN;
    int pollfd_count = 2;

    std::cout << "poll服务器启动,监听端口" << SER_PORT << std::endl;
    while (true) {
        // 3. 阻塞等待就绪事件
        int nfds = poll(fds, pollfd_count, -1);
        if (nfds == -1) { perror("poll"); break; }

        // 4. 遍历处理就绪事件
        for (int i = 0; i < pollfd_count; ++i) {
            if (fds[i].revents & POLLIN) {
                if (fds[i].fd == sfd) { // 新连接
                    struct sockaddr_in cli_addr;
                    socklen_t cli_len = sizeof(cli_addr);
                    int newfd = accept(sfd, (struct sockaddr*)&cli_addr, &cli_len);
                    fds[pollfd_count].fd = newfd;
                    fds[pollfd_count].events = POLLIN;
                    pollfd_count++;
                    std::cout << "客户端[" << inet_ntoa(cli_addr.sin_addr) << "]连接,newfd=" << newfd << std::endl;
                } else if (fds[i].fd == 0) { // 终端命令
                    char buf[1024] = {0};
                    fgets(buf, sizeof(buf), stdin);
                    if (strcmp(buf, "quit\n") == 0) { goto END; }
                } else { // 客户端数据
                    char buf[1024] = {0};
                    ssize_t n = recv(fds[i].fd, buf, sizeof(buf)-1, 0);
                    if (n <= 0) { // 下线
                        close(fds[i].fd);
                        fds[i] = fds[--pollfd_count]; // 覆盖下线fd
                        std::cout << "客户端fd=" << fds[i].fd << "下线" << std::endl;
                        i--; // 重新检查当前位置
                        continue;
                    }
                    std::cout << "收到fd=" << fds[i].fd << "消息: " << buf << std::endl;
                    strcat(buf, "*_*");
                    send(fds[i].fd, buf, strlen(buf), 0);
                }
            }
        }
    }

END:
    for (int i = 0; i < pollfd_count; ++i) {
        close(fds[i].fd);
    }
    return 0;
}

七、epoll IO 多路复用(TCP 并发,LT/ET 模式)

核心特点
  • 高效 IO 多路复用,基于红黑树和回调机制,无 fd 上限
  • LT 模式(默认):水平触发,多次通知未处理数据;ET 模式:边沿触发,仅通知一次(需非阻塞 IO)

实现步骤(LT 模式)

  1. 创建 epoll 实例(epoll_create
  2. 添加监听 fd 到 epoll(epoll_ctl(EPOLL_CTL_ADD)
  3. 循环epoll_wait阻塞等待就绪事件
  4. 遍历就绪事件数组,处理新连接或客户端数据

C++ 模板代码(LT 模式)

cpp

运行

复制代码
#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/epoll.h>

constexpr int SER_PORT = 8888;
const std::string SER_IP = "192.168.1.100";
constexpr int MAX_EVENTS = 1024;

int main() {
    // 1. 创建TCP套接字、绑定、监听
    int sfd = socket(AF_INET, SOCK_STREAM, 0);
    int opt = 1;
    setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    struct sockaddr_in ser_addr = {AF_INET, htons(SER_PORT), inet_addr(SER_IP.c_str())};
    bind(sfd, (struct sockaddr*)&ser_addr, sizeof(ser_addr));
    listen(sfd, 128);

    // 2. 创建epoll实例
    int epfd = epoll_create(1);
    struct epoll_event ev, events[MAX_EVENTS];
    ev.events = EPOLLIN; // LT模式(默认)
    ev.data.fd = sfd;
    epoll_ctl(epfd, EPOLL_CTL_ADD, sfd, &ev);

    std::cout << "epoll(LT)服务器启动,监听端口" << SER_PORT << std::endl;
    while (true) {
        // 3. 阻塞等待就绪事件
        int nfds = epoll_wait(epfd, events, MAX_EVENTS, -1);
        if (nfds == -1) { perror("epoll_wait"); break; }

        // 4. 处理就绪事件
        for (int i = 0; i < nfds; ++i) {
            int fd = events[i].data.fd;
            if (fd == sfd) { // 新连接
                struct sockaddr_in cli_addr;
                socklen_t cli_len = sizeof(cli_addr);
                int newfd = accept(sfd, (struct sockaddr*)&cli_addr, &cli_len);
                ev.events = EPOLLIN;
                ev.data.fd = newfd;
                epoll_ctl(epfd, EPOLL_CTL_ADD, newfd, &ev);
                std::cout << "客户端[" << inet_ntoa(cli_addr.sin_addr) << "]连接,newfd=" << newfd << std::endl;
            } else { // 客户端数据
                char buf[1024] = {0};
                ssize_t n = recv(fd, buf, sizeof(buf)-1, 0);
                if (n <= 0) { // 下线
                    epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);
                    close(fd);
                    std::cout << "客户端fd=" << fd << "下线" << std::endl;
                    continue;
                }
                std::cout << "收到fd=" << fd << "消息: " << buf << std::endl;
                strcat(buf, "*_*");
                send(fd, buf, strlen(buf), 0);
            }
        }
    }

    // 5. 清理资源
    close(sfd);
    close(epfd);
    return 0;
}

ET 模式修改点(核心差异)

  1. 添加 fd 到 epoll 时,事件设为EPOLLIN | EPOLLET
  2. 客户端 fd 设为非阻塞(避免单次读取不完整)

cpp

运行

复制代码
// 客户端fd设为非阻塞
int flags = fcntl(newfd, F_GETFL, 0);
fcntl(newfd, F_SETFL, flags | O_NONBLOCK);
// 添加到epoll时指定ET模式
ev.events = EPOLLIN | EPOLLET;

八、广播(UDP 基础)

核心特点
  • 一对多通信,同一网络下所有主机接收,基于 UDP
  • 发送端需设置SO_BROADCAST属性,接收端必须绑定端口

实现步骤(发送端 + 接收端)

发送端
  1. 创建 UDP 套接字
  2. 设置SO_BROADCAST允许广播
  3. 向广播地址(如 192.168.1.255)发送数据
接收端
  1. 创建 UDP 套接字
  2. 绑定广播地址和端口
  3. 循环接收广播数据

C++ 模板代码

发送端

cpp

运行

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

constexpr int BROADCAST_PORT = 8888;
const std::string BROADCAST_IP = "192.168.1.255"; // 广播地址

int main() {
    // 1. 创建UDP套接字
    int sfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sfd == -1) { perror("socket"); return -1; }

    // 2. 设置允许广播
    int broad = 1;
    if (setsockopt(sfd, SOL_SOCKET, SO_BROADCAST, &broad, sizeof(broad)) == -1) {
        perror("setsockopt"); return -1;
    }

    // 3. 初始化广播地址
    struct sockaddr_in broad_addr;
    memset(&broad_addr, 0, sizeof(broad_addr));
    broad_addr.sin_family = AF_INET;
    broad_addr.sin_port = htons(BROADCAST_PORT);
    broad_addr.sin_addr.s_addr = inet_addr(BROADCAST_IP.c_str());

    // 4. 发送广播
    char buf[1024] = {0};
    while (true) {
        memset(buf, 0, sizeof(buf));
        std::cout << "请输入广播消息: ";
        fgets(buf, sizeof(buf), stdin);
        buf[strcspn(buf, "\n")] = '\0';
        sendto(sfd, buf, strlen(buf), 0, (struct sockaddr*)&broad_addr, sizeof(broad_addr));
    }

    close(sfd);
    return 0;
}
接收端

cpp

运行

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

constexpr int BROADCAST_PORT = 8888;
const std::string BROADCAST_IP = "192.168.1.255";

int main() {
    // 1. 创建UDP套接字
    int rfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (rfd == -1) { perror("socket"); return -1; }

    // 2. 绑定广播地址和端口
    struct sockaddr_in broad_addr;
    memset(&broad_addr, 0, sizeof(broad_addr));
    broad_addr.sin_family = AF_INET;
    broad_addr.sin_port = htons(BROADCAST_PORT);
    broad_addr.sin_addr.s_addr = inet_addr(BROADCAST_IP.c_str());
    if (bind(rfd, (struct sockaddr*)&broad_addr, sizeof(broad_addr)) == -1) {
        perror("bind"); return -1;
    }

    // 3. 接收广播
    char buf[1024] = {0};
    struct sockaddr_in sender_addr;
    socklen_t sender_len = sizeof(sender_addr);
    std::cout << "广播接收端启动,监听端口" << BROADCAST_PORT << std::endl;
    while (true) {
        memset(buf, 0, sizeof(buf));
        recvfrom(rfd, buf, sizeof(buf)-1, 0, (struct sockaddr*)&sender_addr, &sender_len);
        std::cout << "收到[" << inet_ntoa(sender_addr.sin_addr) << "]广播: " << buf << std::endl;
    }

    close(rfd);
    return 0;
}

九、组播(UDP 基础)

核心特点
  • 一对多通信,仅加入组播组的主机接收,基于 UDP
  • 组播地址为 D 类地址(224.0.0.0-239.255.255.255)

实现步骤(发送端 + 接收端)

发送端
  1. 创建 UDP 套接字
  2. 向组播地址发送数据(无需加入组播组)
接收端
  1. 创建 UDP 套接字
  2. 设置IP_ADD_MEMBERSHIP加入组播组
  3. 绑定组播地址和端口
  4. 循环接收组播数据

C++ 模板代码

发送端

cpp

运行

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

constexpr int MULTICAST_PORT = 8888;
const std::string MULTICAST_IP = "224.1.2.3"; // D类组播地址

int main() {
    // 1. 创建UDP套接字
    int sfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sfd == -1) { perror("socket"); return -1; }

    // 2. 初始化组播地址
    struct sockaddr_in multi_addr;
    memset(&multi_addr, 0, sizeof(multi_addr));
    multi_addr.sin_family = AF_INET;
    multi_addr.sin_port = htons(MULTICAST_PORT);
    multi_addr.sin_addr.s_addr = inet_addr(MULTICAST_IP.c_str());

    // 3. 发送组播
    char buf[1024] = {0};
    while (true) {
        memset(buf, 0, sizeof(buf));
        std::cout << "请输入组播消息: ";
        fgets(buf, sizeof(buf), stdin);
        buf[strcspn(buf, "\n")] = '\0';
        sendto(sfd, buf, strlen(buf), 0, (struct sockaddr*)&multi_addr, sizeof(multi_addr));
    }

    close(sfd);
    return 0;
}
接收端

cpp

运行

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

constexpr int MULTICAST_PORT = 8888;
const std::string MULTICAST_IP = "224.1.2.3";
const std::string LOCAL_IP = "192.168.1.100"; // 本地网卡IP

int main() {
    // 1. 创建UDP套接字
    int rfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (rfd == -1) { perror("socket"); return -1; }

    // 2. 加入组播组
    struct ip_mreqn imr;
    imr.imr_multiaddr.s_addr = inet_addr(MULTICAST_IP.c_str());
    imr.imr_address.s_addr = inet_addr(LOCAL_IP.c_str());
    imr.imr_ifindex = 2; // 网卡编号(ip ad查看)
    if (setsockopt(rfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(imr)) == -1) {
        perror("setsockopt"); return -1;
    }

    // 3. 绑定组播地址和端口
    struct sockaddr_in multi_addr;
    memset(&multi_addr, 0, sizeof(multi_addr));
    multi_addr.sin_family = AF_INET;
    multi_addr.sin_port = htons(MULTICAST_PORT);
    multi_addr.sin_addr.s_addr = inet_addr(MULTICAST_IP.c_str());
    if (bind(rfd, (struct sockaddr*)&multi_addr, sizeof(multi_addr)) == -1) {
        perror("bind"); return -1;
    }

    // 4. 接收组播
    char buf[1024] = {0};
    std::cout << "组播接收端启动,已加入组播组" << MULTICAST_IP << std::endl;
    while (true) {
        memset(buf, 0, sizeof(buf));
        recvfrom(rfd, buf, sizeof(buf)-1, 0, NULL, NULL);
        std::cout << "收到组播消息: " << buf << std::endl;
    }

    close(rfd);
    return 0;
}

十、域套接字(本地进程通信)

1. 流式域套接字(SOCK_STREAM,可靠)

核心特点
  • 同一主机进程间通信,基于套接字文件,可靠连接
  • 类似本地 TCP,无需 IP 和端口,用文件路径标识

实现步骤(服务器 + 客户端)

服务器端
  1. 创建域套接字(AF_UNIX + SOCK_STREAM
  2. 检查并删除旧套接字文件(避免绑定失败)
  3. 绑定套接字文件路径
  4. 监听、accept、收发数据、关闭
客户端
  1. 创建域套接字
  2. 连接服务器的套接字文件
  3. 收发数据、关闭

C++ 模板代码

服务器端

cpp

运行

复制代码
#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <fcntl.h>

const std::string SOCK_PATH = "./unix_sock"; // 套接字文件路径

int main() {
    // 1. 创建流式域套接字
    int sfd = socket(AF_UNIX, SOCK_STREAM, 0);
    if (sfd == -1) { perror("socket"); return -1; }

    // 2. 删除旧套接字文件
    if (access(SOCK_PATH.c_str(), F_OK) == 0) {
        if (unlink(SOCK_PATH.c_str()) == -1) { perror("unlink"); return -1; }
    }

    // 3. 绑定套接字文件
    struct sockaddr_un sun;
    memset(&sun, 0, sizeof(sun));
    sun.sun_family = AF_UNIX;
    strcpy(sun.sun_path, SOCK_PATH.c_str());
    if (bind(sfd, (struct sockaddr*)&sun, sizeof(sun)) == -1) { perror("bind"); return -1; }

    // 4. 监听、accept、收发
    listen(sfd, 128);
    std::cout << "流式域套接字服务器启动,路径: " << SOCK_PATH << std::endl;

    struct sockaddr_un cun;
    socklen_t cun_len = sizeof(cun);
    int newfd = accept(sfd, (struct sockaddr*)&cun, &cun_len);
    if (newfd == -1) { perror("accept"); return -1; }
    std::cout << "客户端[" << cun.sun_path << "]连接成功" << std::endl;

    char buf[1024] = {0};
    while (true) {
        memset(buf, 0, sizeof(buf));
        ssize_t n = recv(newfd, buf, sizeof(buf)-1, 0);
        if (n <= 0) { break; }
        std::cout << "收到消息: " << buf << std::endl;
        strcat(buf, "*_*");
        send(newfd, buf, strlen(buf), 0);
    }

    close(newfd);
    close(sfd);
    unlink(SOCK_PATH.c_str()); // 清理套接字文件
    return 0;
}
客户端

cpp

运行

复制代码
#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>

const std::string SOCK_PATH = "./unix_sock";
const std::string CLI_SOCK_PATH = "./client_sock";

int main() {
    // 1. 创建流式域套接字
    int cfd = socket(AF_UNIX, SOCK_STREAM, 0);
    if (cfd == -1) { perror("socket"); return -1; }

    // 2. 绑定客户端套接字文件(可选)
    if (access(CLI_SOCK_PATH.c_str(), F_OK) == 0) {
        unlink(CLI_SOCK_PATH.c_str());
    }
    struct sockaddr_un cun;
    memset(&cun, 0, sizeof(cun));
    cun.sun_family = AF_UNIX;
    strcpy(cun.sun_path, CLI_SOCK_PATH.c_str());
    if (bind(cfd, (struct sockaddr*)&cun, sizeof(cun)) == -1) { perror("bind"); return -1; }

    // 3. 连接服务器
    struct sockaddr_un sun;
    memset(&sun, 0, sizeof(sun));
    sun.sun_family = AF_UNIX;
    strcpy(sun.sun_path, SOCK_PATH.c_str());
    if (connect(cfd, (struct sockaddr*)&sun, sizeof(sun)) == -1) { perror("connect"); return -1; }

    // 4. 收发数据
    char buf[1024] = {0};
    while (true) {
        memset(buf, 0, sizeof(buf));
        std::cout << "请输入消息: ";
        fgets(buf, sizeof(buf), stdin);
        buf[strcspn(buf, "\n")] = '\0';
        send(cfd, buf, strlen(buf), 0);
        
        memset(buf, 0, sizeof(buf));
        ssize_t n = recv(cfd, buf, sizeof(buf)-1, 0);
        if (n <= 0) { break; }
        std::cout << "服务器回复: " << buf << std::endl;
    }

    close(cfd);
    unlink(CLI_SOCK_PATH.c_str());
    return 0;
}

2. 报式域套接字(SOCK_DGRAM,无连接)

核心特点
  • 本地无连接通信,基于套接字文件,数据报形式
  • 类似本地 UDP,无需连接,直接收发

实现步骤(服务器 + 客户端)

服务器端
  1. 创建报式域套接字(AF_UNIX + SOCK_DGRAM
  2. 删除旧套接字文件、绑定
  3. 循环recvfrom接收数据、sendto回复
客户端
  1. 创建报式域套接字、绑定(可选)
  2. 向服务器套接字文件发送数据、接收回复

C++ 模板代码(服务器端)

cpp

运行

复制代码
#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>

const std::string SOCK_PATH = "./unix_dgram_sock";

int main() {
    // 1. 创建报式域套接字
    int sfd = socket(AF_UNIX, SOCK_DGRAM, 0);
    if (sfd == -1) { perror("socket"); return -1; }

    // 2. 删除旧文件、绑定
    if (access(SOCK_PATH.c_str(), F_OK) == 0) { unlink(SOCK_PATH.c_str()); }
    struct sockaddr_un sun;
    memset(&sun, 0, sizeof(sun));
    sun.sun_family = AF_UNIX;
    strcpy(sun.sun_path, SOCK_PATH.c_str());
    if (bind(sfd, (struct sockaddr*)&sun, sizeof(sun)) == -1) { perror("bind"); return -1; }

    // 3. 收发数据
    char buf[1024] = {0};
    struct sockaddr_un cli_addr;
    socklen_t cli_len = sizeof(cli_addr);
    std::cout << "报式域套接字服务器启动" << std::endl;
    while (true) {
        memset(buf, 0, sizeof(buf));
        recvfrom(sfd, buf, sizeof(buf)-1, 0, (struct sockaddr*)&cli_addr, &cli_len);
        std::cout << "收到[" << cli_addr.sun_path << "]消息: " << buf << std::endl;
        
        strcat(buf, "*_*");
        sendto(sfd, buf, strlen(buf), 0, (struct sockaddr*)&cli_addr, cli_len);
    }

    close(sfd);
    unlink(SOCK_PATH.c_str());
    return 0;
}

总结

所有通信方式的核心差异在于 "连接可靠性""并发能力""适用场景":

  • 可靠连接:TCP、流式域套接字
  • 快速无连接:UDP、广播、组播、报式域套接字
  • 高并发:epoll(首选)、poll、select、多线程 / 多进程
  • 本地通信:域套接字(效率高于 TCP/UDP)

模板均采用 C++11 + 语法,突出核心流程,可根据实际需求扩展错误处理、线程安全、数据序列化等功能。</think># 网络编程所有通信方式 C++ 实现模板(C++11+)基于《第五章:网络编程》文档,整理所有提及的通信方式,按 "核心特点→实现步骤→C++ 简要模板" 结构总结,突出核心流程,适配 C++ 现代特性(auto、范围 for 等)。

一、TCP 面向连接通信(可靠传输)

核心特点
  • 面向连接、可靠传输(无丢失 / 重复 / 失序)、三次握手 / 四次挥手
  • 适用:登录、文件传输、关键数据交互

实现步骤(服务器 + 客户端)

服务器端
  1. 创建 TCP 套接字(SOCK_STREAM
  2. 设置地址重用(避免端口占用)
  3. 绑定 IP 和端口(bind
  4. 启动监听(listen
  5. 循环接收客户端连接(accept
  6. 收发数据(recv/send
  7. 关闭套接字(close
客户端
  1. 创建 TCP 套接字
  2. 连接服务器(connect
  3. 收发数据
  4. 关闭套接字

C++ 模板代码

服务器端

cpp

运行

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

constexpr int SER_PORT = 8888;
const std::string SER_IP = "192.168.1.100";

int main() {
    // 1. 创建TCP套接字
    int sfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sfd == -1) { perror("socket"); return -1; }

    // 2. 地址重用
    int opt = 1;
    setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    // 3. 绑定IP+端口
    struct sockaddr_in ser_addr{};
    ser_addr.sin_family = AF_INET;
    ser_addr.sin_port = htons(SER_PORT);
    ser_addr.sin_addr.s_addr = inet_addr(SER_IP.c_str());
    if (bind(sfd, (struct sockaddr*)&ser_addr, sizeof(ser_addr)) == -1) { perror("bind"); return -1; }

    // 4. 监听
    listen(sfd, 128);
    std::cout << "TCP服务器监听端口" << SER_PORT << std::endl;

    // 5. 接收客户端连接
    struct sockaddr_in cli_addr{};
    socklen_t cli_len = sizeof(cli_addr);
    int newfd = accept(sfd, (struct sockaddr*)&cli_addr, &cli_len);
    if (newfd == -1) { perror("accept"); return -1; }
    std::cout << "客户端[" << inet_ntoa(cli_addr.sin_addr) << "]连接成功" << std::endl;

    // 6. 收发数据
    char buf[1024]{};
    while (true) {
        memset(buf, 0, sizeof(buf));
        ssize_t n = recv(newfd, buf, sizeof(buf)-1, 0);
        if (n <= 0) break; // 客户端下线
        std::cout << "收到消息: " << buf << std::endl;
        strcat(buf, "*_*");
        send(newfd, buf, strlen(buf), 0); // 回显
    }

    // 7. 关闭
    close(newfd);
    close(sfd);
    return 0;
}
客户端

cpp

运行

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

constexpr int SER_PORT = 8888;
const std::string SER_IP = "192.168.1.100";

int main() {
    // 1. 创建套接字
    int cfd = socket(AF_INET, SOCK_STREAM, 0);
    if (cfd == -1) { perror("socket"); return -1; }

    // 2. 连接服务器
    struct sockaddr_in ser_addr{};
    ser_addr.sin_family = AF_INET;
    ser_addr.sin_port = htons(SER_PORT);
    ser_addr.sin_addr.s_addr = inet_addr(SER_IP.c_str());
    if (connect(cfd, (struct sockaddr*)&ser_addr, sizeof(ser_addr)) == -1) { perror("connect"); return -1; }

    // 3. 收发数据
    char buf[1024]{};
    while (true) {
        memset(buf, 0, sizeof(buf));
        std::cout << "输入消息: ";
        fgets(buf, sizeof(buf), stdin);
        buf[strcspn(buf, "\n")] = '\0'; // 去除换行
        send(cfd, buf, strlen(buf), 0);
        
        memset(buf, 0, sizeof(buf));
        ssize_t n = recv(cfd, buf, sizeof(buf)-1, 0);
        if (n <= 0) break;
        std::cout << "服务器回复: " << buf << std::endl;
    }

    close(cfd);
    return 0;
}

二、UDP 面向无连接通信(快速传输)

核心特点
  • 无连接、不可靠(可能丢失 / 乱序)、低延迟、数据报
  • 适用:通知、广播、组播、实时音视频

实现步骤

  1. 创建 UDP 套接字(SOCK_DGRAM
  2. 绑定 IP + 端口(服务器必选,客户端可选)
  3. 收发数据(recvfrom/sendto,需指定对端地址)
  4. 关闭套接字

C++ 模板代码(服务器端)

cpp

运行

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

constexpr int SER_PORT = 9999;
const std::string SER_IP = "192.168.1.100";

int main() {
    // 1. 创建UDP套接字
    int sfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sfd == -1) { perror("socket"); return -1; }

    // 2. 绑定
    struct sockaddr_in ser_addr{};
    ser_addr.sin_family = AF_INET;
    ser_addr.sin_port = htons(SER_PORT);
    ser_addr.sin_addr.s_addr = inet_addr(SER_IP.c_str());
    if (bind(sfd, (struct sockaddr*)&ser_addr, sizeof(ser_addr)) == -1) { perror("bind"); return -1; }

    // 3. 收发数据
    char buf[1024]{};
    struct sockaddr_in cli_addr{};
    socklen_t cli_len = sizeof(cli_addr);
    while (true) {
        memset(buf, 0, sizeof(buf));
        recvfrom(sfd, buf, sizeof(buf)-1, 0, (struct sockaddr*)&cli_addr, &cli_len);
        std::cout << "收到[" << inet_ntoa(cli_addr.sin_addr) << "]消息: " << buf << std::endl;
        
        strcat(buf, "*_*");
        sendto(sfd, buf, strlen(buf), 0, (struct sockaddr*)&cli_addr, cli_len);
    }

    close(sfd);
    return 0;
}

三、多线程 TCP 并发服务器

核心特点
  • 主线程监听连接,分支线程处理客户端通信
  • 资源开销小,适配高并发场景

实现步骤

  1. 创建 TCP 套接字、绑定、监听
  2. 循环accept接收连接
  3. 创建分支线程(pthread_create),传入通信 fd
  4. 线程分离(自动回收资源),处理收发数据
  5. 线程退出(pthread_exit

C++ 模板代码

cpp

运行

复制代码
#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>

constexpr int SER_PORT = 8888;
const std::string SER_IP = "192.168.1.100";

// 线程参数结构体
struct ThreadArgs { int newfd; struct sockaddr_in cli_addr; };

// 线程处理函数
void* cli_handler(void* arg) {
    auto args = static_cast<ThreadArgs*>(arg);
    int newfd = args->newfd;
    auto& cli_addr = args->cli_addr;
    delete args;

    pthread_detach(pthread_self()); // 线程分离

    char buf[1024]{};
    while (true) {
        memset(buf, 0, sizeof(buf));
        ssize_t n = recv(newfd, buf, sizeof(buf)-1, 0);
        if (n <= 0) break;
        std::cout << "线程[" << pthread_self() << "]收到消息: " << buf << std::endl;
        strcat(buf, "*_*");
        send(newfd, buf, strlen(buf), 0);
    }

    close(newfd);
    pthread_exit(NULL);
}

int main() {
    // 1. 创建套接字、绑定、监听(同基础TCP服务器)
    int sfd = socket(AF_INET, SOCK_STREAM, 0);
    int opt = 1;
    setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    struct sockaddr_in ser_addr{};
    ser_addr.sin_family = AF_INET;
    ser_addr.sin_port = htons(SER_PORT);
    ser_addr.sin_addr.s_addr = inet_addr(SER_IP.c_str());
    bind(sfd, (struct sockaddr*)&ser_addr, sizeof(ser_addr));
    listen(sfd, 128);

    std::cout << "多线程TCP服务器启动" << std::endl;
    while (true) {
        // 2. 接收连接
        struct sockaddr_in cli_addr{};
        socklen_t cli_len = sizeof(cli_addr);
        int newfd = accept(sfd, (struct sockaddr*)&cli_addr, &cli_len);
        if (newfd == -1) continue;

        // 3. 创建线程
        auto args = new ThreadArgs{newfd, cli_addr};
        pthread_t tid;
        if (pthread_create(&tid, NULL, cli_handler, args) != 0) {
            perror("pthread_create");
            close(newfd);
            delete args;
        }
    }

    close(sfd);
    return 0;
}

四、epoll IO 多路复用(高并发首选)

核心特点
  • 基于红黑树 + 回调,无 fd 上限,效率高
  • 支持 LT(水平触发,默认)和 ET(边沿触发)模式
  • 适用:高并发场景(10k + 连接)

实现步骤(LT 模式)

  1. 创建 epoll 实例(epoll_create
  2. 添加监听 fd 到 epoll(epoll_ctl(EPOLL_CTL_ADD)
  3. 循环epoll_wait阻塞等待就绪事件
  4. 遍历就绪事件,处理新连接或客户端数据

C++ 模板代码

cpp

运行

复制代码
#include <iostream>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/epoll.h>

constexpr int SER_PORT = 8888;
const std::string SER_IP = "192.168.1.100";
constexpr int MAX_EVENTS = 1024;

int main() {
    // 1. 创建TCP套接字、绑定、监听
    int sfd = socket(AF_INET, SOCK_STREAM, 0);
    int opt = 1;
    setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    struct sockaddr_in ser_addr{};
    ser_addr.sin_family = AF_INET;
    ser_addr.sin_port = htons(SER_PORT);
    ser_addr.sin_addr.s_addr = inet_addr(SER_IP.c_str());
    bind(sfd, (struct sockaddr*)&ser_addr, sizeof(ser_addr));
    listen(sfd, 128);

    // 2. 创建epoll实例
    int epfd = epoll_create(1);
    struct epoll_event ev, events[MAX_EVENTS];
    ev.events = EPOLLIN; // LT模式
    ev.data.fd = sfd;
    epoll_ctl(epfd, EPOLL_CTL_ADD, sfd, &ev);

    std::cout << "epoll服务器启动" << std::endl;
    while (true) {
        // 3. 等待就绪事件
        int nfds = epoll_wait(epfd, events, MAX_EVENTS, -1);
        if (nfds == -1) { perror("epoll_wait"); break; }

        // 4. 处理事件
        for (int i = 0; i < nfds; ++i) {
            int fd = events[i].data.fd;
            if (fd == sfd) { // 新连接
                struct sockaddr_in cli_addr{};
                socklen_t cli_len = sizeof(cli_addr);
                int newfd = accept(sfd, (struct sockaddr*)&cli_addr, &cli_len);
                ev.events = EPOLLIN;
                ev.data.fd = newfd;
                epoll_ctl(epfd, EPOLL_CTL_ADD, newfd, &ev);
                std::cout << "客户端[" << inet_ntoa(cli_addr.sin_addr) << "]连接" << std::endl;
            } else { // 客户端数据
                char buf[1024]{};
                ssize_t n = recv(fd, buf, sizeof(buf)-1, 0);
                if (n <= 0) { // 下线
                    epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);
                    close(fd);
                    continue;
                }
                std::cout << "收到消息: " << buf << std::endl;
                strcat(buf, "*_*");
                send(fd, buf, strlen(buf), 0);
            }
        }
    }

    close(sfd);
    close(epfd);
    return 0;
}

五、广播(UDP 基础)

核心特点
  • 一对多通信,同一网络下所有主机接收
  • 发送端需设置SO_BROADCAST属性

实现步骤(发送端)

  1. 创建 UDP 套接字
  2. 设置SO_BROADCAST允许广播
  3. 向广播地址(如 192.168.1.255)发送数据

C++ 模板代码(发送端)

cpp

运行

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

constexpr int BROADCAST_PORT = 8888;
const std::string BROADCAST_IP = "192.168.1.255";

int main() {
    // 1. 创建UDP套接字
    int sfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sfd == -1) { perror("socket"); return -1; }

    // 2. 允许广播
    int broad = 1;
    if (setsockopt(sfd, SOL_SOCKET, SO_BROADCAST, &broad, sizeof(broad)) == -1) {
        perror("setsockopt"); return -1;
    }

    // 3. 初始化广播地址
    struct sockaddr_in broad_addr{};
    broad_addr.sin_family = AF_INET;
    broad_addr.sin_port = htons(BROADCAST_PORT);
    broad_addr.sin_addr.s_addr = inet_addr(BROADCAST_IP.c_str());

    // 4. 发送广播
    char buf[1024]{};
    while (true) {
        memset(buf, 0, sizeof(buf));
        std::cout << "输入广播消息: ";
        fgets(buf, sizeof(buf), stdin);
        buf[strcspn(buf, "\n")] = '\0';
        sendto(sfd, buf, strlen(buf), 0, (struct sockaddr*)&broad_addr, sizeof(broad_addr));
    }

    close(sfd);
    return 0;
}

六、组播(UDP 基础)

核心特点
  • 一对多通信,仅加入组播组的主机接收
  • 组播地址为 D 类地址(224.0.0.0-239.255.255.255)

实现步骤(接收端)

  1. 创建 UDP 套接字
  2. 设置IP_ADD_MEMBERSHIP加入组播组
  3. 绑定组播地址和端口
  4. 接收组播数据

C++ 模板代码(接收端)

cpp

运行

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

// 常量定义(可根据实际需求修改)
constexpr int MULTICAST_PORT = 8888;          // 组播端口(需与发送端一致)
const std::string MULTICAST_IP = "224.1.2.3"; // D类组播地址(224.0.0.0-239.255.255.255)
const std::string LOCAL_IP = "192.168.174.128";// 本地网卡IP(通过 ip ad 命令查看)
constexpr int NET_CARD_INDEX = 2;            // 网卡设备编号(通过 ip ad 命令查看)

int main() {
    // 1. 创建UDP套接字
    int recv_fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (recv_fd == -1) {
        perror("socket 创建失败");
        return -1;
    }
    std::cout << "UDP套接字创建成功,文件描述符: " << recv_fd << std::endl;

    // 2. 设置套接字加入组播组
    struct ip_mreqn multicast_req;
    // 填充组播地址(网络字节序)
    multicast_req.imr_multiaddr.s_addr = inet_addr(MULTICAST_IP.c_str());
    // 填充本地网卡IP(网络字节序)
    multicast_req.imr_address.s_addr = inet_addr(LOCAL_IP.c_str());
    // 填充网卡编号
    multicast_req.imr_ifindex = NET_CARD_INDEX;

    // 调用setsockopt加入组播组(层级:IPPROTO_IP,属性:IP_ADD_MEMBERSHIP)
    if (setsockopt(recv_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, 
                   &multicast_req, sizeof(multicast_req)) == -1) {
        perror("加入组播组失败");
        close(recv_fd);
        return -1;
    }
    std::cout << "成功加入组播组 " << MULTICAST_IP << ":" << MULTICAST_PORT << std::endl;

    // 3. 绑定组播地址和端口(必须绑定,否则无法接收组播数据)
    struct sockaddr_in multicast_addr;
    memset(&multicast_addr, 0, sizeof(multicast_addr));
    multicast_addr.sin_family = AF_INET;                  // IPv4协议族
    multicast_addr.sin_port = htons(MULTICAST_PORT);       // 端口号(网络字节序)
    multicast_addr.sin_addr.s_addr = inet_addr(MULTICAST_IP.c_str()); // 组播IP(网络字节序)

    if (bind(recv_fd, (struct sockaddr*)&multicast_addr, sizeof(multicast_addr)) == -1) {
        perror("绑定组播地址失败");
        close(recv_fd);
        return -1;
    }
    std::cout << "已绑定组播地址,等待接收数据..." << std::endl;

    // 4. 循环接收组播数据
    char recv_buf[1024] = {0};
    while (true) {
        memset(recv_buf, 0, sizeof(recv_buf)); // 清空接收缓冲区

        // 接收组播数据(无需关心发送端地址,填NULL即可)
        ssize_t recv_len = recvfrom(recv_fd, recv_buf, sizeof(recv_buf) - 1, 
                                   0, NULL, NULL);
        if (recv_len == -1) {
            perror("接收组播数据失败");
            continue; // 忽略错误,继续接收
        }

        // 输出接收结果
        std::cout << "收到组播消息: " << recv_buf << "(字节数: " << recv_len << ")" << std::endl;
    }

    // 5. 关闭套接字(实际运行中需手动终止,此处为逻辑闭环)
    close(recv_fd);
    return 0;
}
相关推荐
ulias21216 小时前
Linux系统中的权限问题
linux·运维·服务器
青花瓷17 小时前
Ubuntu下OpenClaw的安装(豆包火山API版)
运维·服务器·ubuntu
问简18 小时前
docker 镜像相关
运维·docker·容器
Dream of maid18 小时前
Linux(下)
linux·运维·服务器
齐鲁大虾18 小时前
统信系统UOS常用命令集
linux·运维·服务器
Benszen19 小时前
Docker容器化技术实战指南
运维·docker·容器
ZzzZZzzzZZZzzzz…19 小时前
Nginx 平滑升级:从 1.26.3 到 1.28.0,用户无感知
linux·运维·nginx·平滑升级·nginx1.26.3·nginx1.28.0
一叶知秋yyds20 小时前
Ubuntu 虚拟机安装 OpenClaw 完整流程
linux·运维·ubuntu·openclaw
专吃海绵宝宝菠萝屋的派大星21 小时前
使用Dify对接自己开发的mcp
java·服务器·前端
斯普信云原生组21 小时前
Prometheus 环境监控虚机 Redis 方案(生产实操版)
运维·docker·容器