基于多反应堆的高并发服务器【C/C++/Reactor】(中)

2023-12-22 12:53:37

在这篇文章中虽然实现了能够和多客户端建立连接,并且同时和多个客户端进行通信。

基于多反应堆的高并发服务器【C/C++/Reactor】(上)-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/weixin_41987016/article/details/135141316?spm=1001.2014.3001.5501但是有一个问题(O_O)?:这个程序它是单线程的。如果我们想要程序的效率更高一些,就需要使用多线程。

研究一下:若使用多线程,需要在什么地方把子线程创建出来

在服务器端有两类文件描述符:一类是用于通信的,一类是用于监听的

关于监听的文件描述符,在服务器端有且仅有一个。所以我们把它在主线程里边创建出来之后,就不需要做其他的监听的文件描述符的创建了。通过这一个唯一的监听文件描述符,服务器就能接收到客户端的连接请求,并且和多个客户端建立连接

int epollRun(int lfd) {
    ...
    while(1) {
        int num = epoll_wait(epfd,evs,size,-1);
        if(num == -1) {
            perror("epoll_wait");
            return -1;
        }
        for(int i=0;i<num;++i) {
            int fd = evs[i].data.fd;
            if(fd == lfd) {
                // 建立新连接 accept
                acceptClient(lfd,epfd);
            }else{
                // 主要是接收对端的数据
                recvHttpRequest(fd,epfd);
            }

        }
    }
    return 0;
}

epollRun函数中,在while循环里边,需要判断文件描述符的类型,如果是监听的文件描述符,它的读事件被触发了。那么我们就和客户端建立新连接。还有一种情况是通信的文件描述符,我们就需要和客户端进行通信。因此不管是建立连接还是和客户端通信,都可以把它放到一个子线程里面去做。也就是说我们需要在这两个函数调用的位置分别创建子线程。把acceptClient函数或者是recvHttpRequest函数传递给子线程,让子线程去执行这个处理动作。

int epollRun(int lfd) {
    // 1.创建epoll实例
    int epfd = epoll_create(1);
    if(epfd == -1) {
        perror("epoll_create");
        return -1;
    }
    // 2.添加监听fd lfd上树 对于监听的描述符来说只需要看一下有没有新的客户端连接
    struct epoll_event ev;
    ev.data.fd = lfd;
    ev.events = EPOLLIN;// 委托epoll(内核)帮我们检测lfd的读事件
    int ret = epoll_ctl(epfd,EPOLL_CTL_ADD,lfd,&ev);
    if(ret == -1) {
        perror("epoll_ctl");
        return -1;
    }
    // 3.检测
    struct epoll_event evs[1024];
    // int size = sizeof(evs)/sizeof(epoll_event);
    int size = sizeof(evs)/sizeof(evs[0]);
    while(1) {
        int num = epoll_wait(epfd,evs,size,-1);
        if(num == -1) {
            perror("epoll_wait");
            return -1;
        }
        for(int i=0;i<num;++i) {
            struct FdInfo* info = (struct FdInfo*)malloc(sizeof(struct FdInfo));
            int fd = evs[i].data.fd;
            info->epfd = epfd;
            info->fd = fd;
            if(fd == lfd) {
                // 建立新连接 accept
                // acceptClient(lfd,epfd);// 两个参数
                // 两个参数->只能够对数据进行封装
                pthread_create(&info->tid,NULL,acceptClient,info);
            }else{
                // 主要是接收对端的数据
                // recvHttpRequest(fd,epfd);
                // 子线程被创建出来后,它对应的处理动作是 recvHttpRequest,给这个函数传递的实参是
                // info这个结构体里边的数据
                pthread_create(&info->tid,NULL,recvHttpRequest,info);
            }

        }
    }
    return 0;
}

接下来要做的事情就是把acceptClient函数和recvHttpRequest函数原型修改一下。因为对于一个子线程来说,它的回调函数对应的是一个函数指针,函数指针的返回值是void*类型。它的参数也是void*类型。先切换到头文件,修改头文件。

Server.h

修改前:
// 和客户端建立连接
int acceptClient(int lfd,int epfd);
// 主要是接收对端的数据
int recvHttpRequest(int cfd,int epfd);

修改后:
// 和客户端建立连接
void* acceptClientThread(void* arg);
// 主要是接收对端的数据
void* recvHttpRequest(void* arg);

未完待续~

文章来源:https://blog.csdn.net/weixin_41987016/article/details/135147682
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。