如何在C语言中实现高并发的TCP服务器?

解密C高并发TCP服务器实现方法

概述

在当今数字化时代,构建高效、可靠的服务器是网络通信领域的核心任务之一,C语言作为一种功能强大的编程语言,广泛应用于系统编程和网络编程中,本文将详细介绍如何使用C语言实现高并发的TCP服务器,包括多进程、多线程及IO多路复用等技术的应用,通过这些技术,可以显著提升服务器的性能和稳定性,满足大规模用户访问的需求。

「解密」C 高并发 TCP 服务器实现方法 (c 高并发tcp服务器)

多进程实现高并发TCP服务器

1、基本原理:多进程模型通过父进程监听客户端连接请求,每当有新的连接时,父进程会创建一个子进程来处理该连接,这种方式能够有效利用多核CPU的优势,提高系统的并发处理能力。

2、代码示例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <signal.h>
#include <wait.h>
#include <errno.h>
void recycleChild(int argc) {
    while (1) {
        int ret = waitpid(1, NULL, WNOHANG);
        if (ret > 0) {
            printf("Process %d recycled
", ret);
        } else if (ret == 0) {
            break;
        } else {
            break;
        }
    }
}
int main() {
    struct sigaction act;
    act.sa_handler = recycleChild;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
    sigaction(SIGCHLD, &act, NULL);
    int lfd = socket(AF_INET, SOCK_STREAM, 0);
    if (lfd == 1) {
        perror("socket");
        exit(1);
    }
    struct sockaddr_in serv_addr;
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(9999);
    serv_addr.sin_addr.s_addr = INADDR_ANY;
    int ret = bind(lfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
    if (ret == 1) {
        perror("bind");
        exit(1);
    }
    ret = listen(lfd, 128);
    if (ret == 1) {
        perror("listen");
        exit(1);
    }
    while (1) {
        struct sockaddr_in cli_addr;
        socklen_t len = sizeof(cli_addr);
        int cfd = accept(lfd, (struct sockaddr *)&cli_addr, &len);
        if (cfd == 1) {
            perror("accept");
            continue;
        }
        pid_t pid = fork();
        if (pid == 0) { // 子进程
            close(lfd); // 关闭不必要的文件描述符
            char buf[1024];
            while (recv(cfd, buf, sizeof(buf), 0) > 0) {
                // Echo back to the client
                send(cfd, buf, sizeof(buf), 0);
            }
            close(cfd);
            exit(0); // 结束子进程
        } else if (pid > 0) { // 父进程
            close(cfd); // 关闭不必要的文件描述符
        } else {
            perror("fork");
            exit(1);
        }
    }
    close(lfd);
    return 0;
}

3、注意事项:使用多进程模型时,需要注意资源的有效管理,如及时回收僵尸进程,避免资源泄漏,由于进程间内存不共享,需要通过IPC(InterProcess Communication)机制进行数据交换。

多线程实现高并发TCP服务器

1、基本原理:多线程模型通过在单个进程中创建多个线程来处理并发连接,每个线程负责一个客户端连接的处理,线程之间共享进程的内存空间,减少了进程间通信的开销。

「解密」C 高并发 TCP 服务器实现方法 (c 高并发tcp服务器)

2、代码示例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <unistd.h>
#include <errno.h>
typedef struct {
    int sockfd;
} thread_data;
void *handle_client(void *arg) {
    thread_data *data = (thread_data *)arg;
    char buf[1024];
    while (recv(data>sockfd, buf, sizeof(buf), 0) > 0) {
        send(data>sockfd, buf, sizeof(buf), 0);
    }
    close(data>sockfd);
    free(data);
    return NULL;
}
int main() {
    int lfd = socket(AF_INET, SOCK_STREAM, 0);
    if (lfd == 1) {
        perror("socket");
        exit(1);
    }
    struct sockaddr_in serv_addr;
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(9999);
    serv_addr.sin_addr.s_addr = INADDR_ANY;
    int ret = bind(lfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
    if (ret == 1) {
        perror("bind");
        exit(1);
    }
    ret = listen(lfd, 128);
    if (ret == 1) {
        perror("listen");
        exit(1);
    }
    while (1) {
        struct sockaddr_in cli_addr;
        socklen_t len = sizeof(cli_addr);
        int cfd = accept(lfd, (struct sockaddr *)&cli_addr, &len);
        if (cfd == 1) {
            perror("accept");
            continue;
        }
        thread_data *data = (thread_data *)malloc(sizeof(thread_data));
        data>sockfd = cfd;
        pthread_t tid;
        pthread_create(&tid, NULL, handle_client, data);
        pthread_detach(tid); // 设置为分离状态,线程结束时自动释放资源
    }
    close(lfd);
    return 0;
}

3、注意事项:使用多线程模型时,需要注意线程安全问题,尤其是在访问共享资源时需要加锁,以防止竞态条件的发生,合理设置线程的数目和优先级,以充分利用系统资源。

IO多路复用实现高并发TCP服务器

1、基本原理:IO多路复用技术(如select、poll、epoll)允许单个线程监视多个文件描述符,当某个文件描述符就绪时,线程对其进行处理,这种方式避免了为每个连接创建线程或进程的开销,提高了系统的并发处理能力。

2、代码示例(以epoll为例):

「解密」C 高并发 TCP 服务器实现方法 (c 高并发tcp服务器)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <unistd.h>
#include <errno.h>
#define MAX_EVENTS 1024
#define BUF_SIZE 1024
int setNonBlocking(int sockfd) {
    int flags = fcntl(sockfd, F_GETFL, 0);
    if (flags == 1) {
        perror("fcntl get");
        return 1;
    }
    flags |= O_NONBLOCK;
    if (fcntl(sockfd, F_SETFL, flags) == 1) {
        perror("fcntl set");
        return 1;
    }
    return 0;
}
int main() {
    int lfd = socket(AF_INET, SOCK_STREAM, 0);
    if (lfd == 1) {
        perror("socket");
        exit(1);
    }
    setNonBlocking(lfd); // 设置为非阻塞模式
    struct sockaddr_in serv_addr;
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(9999);
    serv_addr.sin_addr.s_addr = INADDR_ANY;
    int ret = bind(lfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
    if (ret == 1) {
        perror("bind");
        exit(1);
    }
    ret = listen(lfd, 128);
    if (ret == 1) {
        perror("listen");
        exit(1);
    }
    struct epoll_event ev, events[MAX_EVENTS];
    int epfd = epoll_create1(0); // 创建epoll实例
    if (epfd == 1) {
        perror("epoll create");
        exit(1);
    }
    ev.events = EPOLLIN; // 注册感兴趣的事件类型:有数据可读
    ev.data.fd = lfd; // 指定要监听的文件描述符
    if (epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &ev) == 1) { // 添加文件描述符到epoll实例中进行监听
        perror("epoll ctl");
        exit(1);
    }
    // 事件循环
    for (;;) {
        int nfds = epoll_wait(epfd, events, MAX_EVENTS, 1); // 等待事件发生,超时时间为1表示无限等待
        if (nfds == 1) {
            perror("epoll wait");
            exit(1);
        }
        for (int i = 0; i < nfds; ++i) { // 遍历所有就绪的事件
            if (events[i].data.fd == lfd) { // 如果就绪的文件描述符是监听套接字,说明有新的连接到来
                struct sockaddr_in cli_addr;
                socklen_t len = sizeof(cli_addr);
                int cfd = accept(lfd, (struct sockaddr*)&cli_addr, &len); // 接受新的连接
                setNonBlocking(cfd); // 设置为非阻塞模式
                epoll_event new_ev;
                new_ev.events = EPOLLIN | EPOLLET; // 注册感兴趣的事件类型:有数据可读或发生EOF(这里ET模式用于水平触发)
                new_ev.data.fd = cfd; // 指定要监听的文件描述符
                if (epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &new_ev) == 1) { // 添加新的文件描述符到epoll实例中进行监听
                    perror("epoll ctl");
                    exit(1);
                }
            } else { // 如果就绪的文件描述符是客户套接字,说明有数据传输或关闭事件发生
                int cfd = events[i].data.fd; // 获取就绪的客户套接字文件描述符
                char buf[BUF_SIZE]; // 读取数据缓冲区大小为BUF_SIZE字节
                int bytesRead = read(cfd, buf, BUF_SIZE); // 从套接字读取数据到缓冲区中,返回实际读取的字节数(可能小于BUF_SIZE)如果返回值为1,则表示对端关闭了连接或者出现了错误;否则表示成功读取了bytesRead个字节的数据,根据具体需求处理数据...(此处省略数据处理部分)... } } } } } return 0; }```3.注意事项:使用IO多路复用技术时,需要注意正确处理各种I/O事件,尤其是异常情况,合理设置文件描述符的数量和事件的类型,以平衡性能和资源消耗,不同的IO多路复用函数(如select、poll、epoll)有不同的特点和适用场景,需要根据实际情况选择合适的函数,表1:多进程、多线程与IO多路复用技术比较| 技术 | 优点 | 缺点 | 适用场景 |||||||多进程 | 实现简单,可以利用多核CPU | 进程间通信复杂,资源消耗大 | 适用于需要隔离不同任务的场景 ||多线程 | 线程间共享内存,减少通信开销 | 线程安全和同步问题 | 适用于I/O密集型任务 ||IO多路复用 | 高效的I/O处理能力,适合高并发 | 编程复杂,需要处理多种事件 | 适用于大规模并发连接的场景 |通过以上分析和比较,可以看出,不同的并发处理技术各有优缺点,适用于不同的场景,在实际应用中,可以根据具体需求选择合适的技术或组合使用多种技术,以实现高效、稳定的服务器性能。

各位小伙伴们,我刚刚为大家分享了有关“「解密」C 高并发 TCP 服务器实现方法 (c 高并发tcp服务器)”的知识,希望对你们有所帮助。如果您还有其他相关问题需要解决,欢迎随时提出哦!

原创文章,作者:未希,如若转载,请注明出处:https://www.kdun.com/ask/1224389.html

本网站发布或转载的文章及图片均来自网络,其原创性以及文中表达的观点和判断不代表本网站。如有问题,请联系客服处理。

(0)
未希新媒体运营
上一篇 2024-10-19 12:20
下一篇 2024-10-19 12:27

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

产品购买 QQ咨询 微信咨询 SEO优化
分享本页
返回顶部
云产品限时秒杀。精选云产品高防服务器,20M大带宽限量抢购 >>点击进入