网络编程所有通信方式总结 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;
}
相关推荐
a***81391 小时前
【Go】Go语言基础学习(Go安装配置、基础语法)
服务器·学习·golang
IT 前端 张1 小时前
Uni-app 实现全局无操作监听:自动退出弹窗倒计时功能
运维·服务器·uni-app
tianyuanwo1 小时前
SSH连接底层原理与故障深度解析:从协议握手到安全运维
运维·安全·ssh
f***24111 小时前
不常用,总是忘记:nginx 重启指令
运维·windows·nginx
R***z1011 小时前
【Sql Server】sql server 2019设置远程访问,外网服务器需要设置好安全组入方向规则
运维·服务器·安全
梁正雄1 小时前
5、python 模块与包
linux·服务器·python
de之梦-御风1 小时前
【远程控制】开箱即用的 RustDesk 自建服务端完整 Docker Compose 模板
运维·docker·容器
axihaihai1 小时前
腾讯云镜像仓库访问问题
linux·服务器·腾讯云
宇钶宇夕1 小时前
魏德米勒 UR20-FBC-PN-IRT-V2 从站全解析:产品特性、模块详情、接线图与地址配置指南(地址修改部分)
运维·自动化