温馨提示×

Linux C++网络编程中如何处理并发连接

小樊
38
2025-02-28 22:09:19
栏目: 编程语言
C++开发者专用服务器限时活动,0元免费领,库存有限,领完即止! 点击查看>>

在Linux环境下使用C++进行网络编程时,处理并发连接是一个常见的需求。以下是几种常用的方法来处理并发连接:

1. 多线程

多线程是最直观的并发处理方式。每个连接由一个单独的线程处理。

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

void handleClient(int clientSocket) {
    char buffer[1024];
    while (true) {
        int bytesReceived = recv(clientSocket, buffer, sizeof(buffer), 0);
        if (bytesReceived <= 0) {
            break;
        }
        send(clientSocket, buffer, bytesReceived, 0);
    }
    close(clientSocket);
}

int main() {
    int serverSocket, clientSocket;
    struct sockaddr_in serverAddr, clientAddr;
    socklen_t clientAddrLen = sizeof(clientAddr);

    serverSocket = socket(AF_INET, SOCK_STREAM, 0);
    if (serverSocket < 0) {
        perror("socket");
        return 1;
    }

    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.s_addr = INADDR_ANY;
    serverAddr.sin_port = htons(8080);

    if (bind(serverSocket, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) < 0) {
        perror("bind");
        return 1;
    }

    if (listen(serverSocket, 5) < 0) {
        perror("listen");
        return 1;
    }

    while (true) {
        clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddr, &clientAddrLen);
        if (clientSocket < 0) {
            perror("accept");
            continue;
        }
        std::thread(handleClient, clientSocket).detach();
    }

    close(serverSocket);
    return 0;
}

2. 多进程

多进程模型中,每个连接由一个单独的进程处理。

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

void handleClient(int clientSocket) {
    char buffer[1024];
    while (true) {
        int bytesReceived = recv(clientSocket, buffer, sizeof(buffer), 0);
        if (bytesReceived <= 0) {
            break;
        }
        send(clientSocket, buffer, bytesReceived, 0);
    }
    close(clientSocket);
}

int main() {
    int serverSocket, clientSocket;
    struct sockaddr_in serverAddr, clientAddr;
    socklen_t clientAddrLen = sizeof(clientAddr);

    serverSocket = socket(AF_INET, SOCK_STREAM, 0);
    if (serverSocket < 0) {
        perror("socket");
        return 1;
    }

    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.s_addr = INADDR_ANY;
    serverAddr.sin_port = htons(8080);

    if (bind(serverSocket, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) < 0) {
        perror("bind");
        return 1;
    }

    if (listen(serverSocket, 5) < 0) {
        perror("listen");
        return 1;
    }

    while (true) {
        clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddr, &clientAddrLen);
        if (clientSocket < 0) {
            perror("accept");
            continue;
        }
        pid_t pid = fork();
        if (pid == 0) {
            close(serverSocket);
            handleClient(clientSocket);
            exit(0);
        } else if (pid > 0) {
            close(clientSocket);
        } else {
            perror("fork");
        }
    }

    close(serverSocket);
    return 0;
}

3. I/O多路复用

I/O多路复用模型允许单个进程/线程处理多个连接。常用的系统调用有selectpollepoll

使用select

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

int main() {
    int serverSocket, clientSocket;
    struct sockaddr_in serverAddr;
    fd_set readfds;
    int maxClients = 10;
    int clientSockets[maxClients] = {0};

    serverSocket = socket(AF_INET, SOCK_STREAM, 0);
    if (serverSocket < 0) {
        perror("socket");
        return 1;
    }

    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.s_addr = INADDR_ANY;
    serverAddr.sin_port = htons(8080);

    if (bind(serverSocket, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) < 0) {
        perror("bind");
        return 1;
    }

    if (listen(serverSocket, 5) < 0) {
        perror("listen");
        return 1;
    }

    while (true) {
        FD_ZERO(&readfds);
        FD_SET(serverSocket, &readfds);
        int maxFd = serverSocket;

        for (int i = 0; i < maxClients; i++) {
            clientSocket = clientSockets[i];
            if (clientSocket > 0) {
                FD_SET(clientSocket, &readfds);
            }
            if (clientSocket > maxFd) {
                maxFd = clientSocket;
            }
        }

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

        if (FD_ISSET(serverSocket, &readfds)) {
            clientSocket = accept(serverSocket, NULL, NULL);
            if (clientSocket < 0) {
                perror("accept");
                continue;
            }
            for (int i = 0; i < maxClients; i++) {
                if (clientSockets[i] == 0) {
                    clientSockets[i] = clientSocket;
                    break;
                }
            }
        }

        for (int i = 0; i < maxClients; i++) {
            clientSocket = clientSockets[i];
            if (FD_ISSET(clientSocket, &readfds)) {
                char buffer[1024];
                int bytesReceived = recv(clientSocket, buffer, sizeof(buffer), 0);
                if (bytesReceived <= 0) {
                    close(clientSocket);
                    clientSockets[i] = 0;
                } else {
                    send(clientSocket, buffer, bytesReceived, 0);
                }
            }
        }
    }

    close(serverSocket);
    return 0;
}

使用epoll

#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <sys/epoll.h>

int main() {
    int serverSocket, clientSocket, epollFd;
    struct sockaddr_in serverAddr;
    struct epoll_event event, events[10];
    int maxEvents = 10;

    serverSocket = socket(AF_INET, SOCK_STREAM, 0);
    if (serverSocket < 0) {
        perror("socket");
        return 1;
    }

    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.s_addr = INADDR_ANY;
    serverAddr.sin_port = htons(8080);

    if (bind(serverSocket, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) < 0) {
        perror("bind");
        return 1;
    }

    if (listen(serverSocket, 5) < 0) {
        perror("listen");
        return 1;
    }

    epollFd = epoll_create1(0);
    if (epollFd < 0) {
        perror("epoll_create1");
        return 1;
    }

    event.events = EPOLLIN;
    event.data.fd = serverSocket;
    if (epoll_ctl(epollFd, EPOLL_CTL_ADD, serverSocket, &event) < 0) {
        perror("epoll_ctl: serverSocket");
        return 1;
    }

    while (true) {
        int numEvents = epoll_wait(epollFd, events, maxEvents, -1);
        if (numEvents < 0) {
            perror("epoll_wait");
            continue;
        }

        for (int i = 0; i < numEvents; i++) {
            if (events[i].data.fd == serverSocket) {
                clientSocket = accept(serverSocket, NULL, NULL);
                if (clientSocket < 0) {
                    perror("accept");
                    continue;
                }
                event.events = EPOLLIN | EPOLLET;
                event.data.fd = clientSocket;
                if (epoll_ctl(epollFd, EPOLL_CTL_ADD, clientSocket, &event) < 0) {
                    perror("epoll_ctl: clientSocket");
                    close(clientSocket);
                }
            } else {
                clientSocket = events[i].data.fd;
                char buffer[1024];
                int bytesReceived = recv(clientSocket, buffer, sizeof(buffer), 0);
                if (bytesReceived <= 0) {
                    close(clientSocket);
                    epoll_ctl(epollFd, EPOLL_CTL_DEL, clientSocket, NULL);
                } else {
                    send(clientSocket, buffer, bytesReceived, 0);
                }
            }
        }
    }

    close(serverSocket);
    close(epollFd);
    return 0;
}

总结

  • 多线程:简单直观,但线程切换开销较大。
  • 多进程:每个连接一个进程,资源隔离性好,但进程切换开销大。
  • I/O多路复用:单个进程/线程处理多个连接,效率高,适用于高并发场景。

选择哪种方法取决于具体应用场景和性能需求。

亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>

推荐阅读:C++ Linux网络编程难点在哪

0