欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  网络运营

7.10 第九章I/O复用高级函数 select poll epoll(lt et)

程序员文章站 2022-03-12 20:06:46
//所有内容均来自linux高性能服务器编程这本书.利用epoll中的EPOLLONESHOT实现一段时间内只能有一个线程在处理一个SOKCET 防止该线程刚读取完数据准备处理 又来了一个数据 启动另外一个线程来处理 ONESHOT只会触发一次 所以当该工作进程处理完当前的事件之后,应该重置EPOLL_CTL_MOD 该FD状态 以便于下一次能够再次触发#include #include #include

//所有内容均来自linux高性能服务器编程这本书.

利用epoll中的EPOLLONESHOT实现一段时间内只能有一个线程在处理一个SOKCET 防止该线程刚读取完数据准备处理 又来了一个数据 启动另外一个线程来处理 ONESHOT只会触发一次 所以当该工作进程处理完当前的事件之后,应该重置EPOLL_CTL_MOD 该FD状态 以便于下一次能够再次触发

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <pthread.h>
#include <stdbool.h>
// linux下使用结构体一定要在前面加入struct这个字 不然是没有效果的
#define MAX_EVENT_NUMBER 1024
#define BUFFER_SIZE 1024
// 由于之前没有什么敲linux下代码的经验  
// 这次debug调式了很久的时间  需要注意的点就是如果是应用一个结构体 那么就需要在前面加入struct关键字  对于指针的传递一定要写&地址符号 (在实际编程中很少这样写 导致频繁出错) !!
typedef struct  // 定义一个结构体 
{
    int epollfd;
    int socket;
}fds;
int setnoblock(int fd)
{
    int old_option = fcntl(fd,F_GETFL);
    int new_option = old_option | O_NONBLOCK;
    fcntl(fd,F_SETFL,new_option);
    return old_option;
}
void addfd(int epollfd,int fd,bool oneshot)
{
    struct epoll_event event;
    event.data.fd = fd;
    event.events = EPOLLIN | EPOLLET;
    if(oneshot)
    {
        event.events |= EPOLLONESHOT;
    }
    epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&event);
    setnoblock(fd);
}

void reset_oneshot(int epollfd,int fd)
{
    struct epoll_event event;
    event.data.fd = fd;
    event.events = EPOLLIN | EPOLLET | EPOLLONESHOT;
    epoll_ctl(epollfd,EPOLL_CTL_MOD,fd,&event);
}
void* work(void* arg) // 主线程已经帮我连接了  为只是负责处理这个事件
{
    int sockfd = (( fds* )arg)->socket;
    int epollfd = (( fds* )arg)->epollfd;
    printf("start new thread to receive msg on %d\n",socket);

    char buf[BUFFER_SIZE];

    memset(buf,0, BUFFER_SIZE);
    while(1)
    {
        int ret = recv(sockfd,buf,BUFFER_SIZE - 1,0);
        if(ret == 0)
        {
            close(socket);
            printf("foreiner close the connect\n");
            break;
        }
        else if(ret < 0)
        {
            if(errno == EAGAIN)
            {
                reset_oneshot(epollfd,socket);
                printf("all revc and reset socket");
                break;
            }
        }
        else
        {
            printf("the msg is %s\n",buf);
            sleep(5); // 模拟处理过程
        }
        printf("end thread receiving data on fd: %d\n",socket);
    }
}
int main()
{
    const char* ip = "";
    int port = 80;
    struct sockaddr_in address;
    bzero(&address,sizeof address);
    address.sin_family = AF_INET;
    address.sin_port = htons(port);
    inet_pton(AF_INET,ip,&address.sin_addr);

    int listenfd = socket(PF_INET,SOCK_STREAM,0);
    assert(listenfd >= 0);

    int ret = bind(listenfd,(struct sockaddr *)&address,sizeof address);
    assert(ret != -1);

    ret = listen(listenfd,5);
    assert(ret != -1);
    struct epoll_event events[MAX_EVENT_NUMBER];
    int epollfd = epoll_create(5);

    addfd(epollfd,listenfd,false);
    while(1)
    {
        int ret = epoll_wait(epollfd,events,MAX_EVENT_NUMBER,-1);

        if(ret < 0)
        {
            printf("epoll failed");break;
        }
        for(int i = 0; i < ret ; i++)
        {
            int sockfd = events[i].data.fd;
            if(sockfd == listenfd)
            {
                struct sockaddr_in client;
                socklen_t len = sizeof(client);
                int t = accept(sockfd,(struct sockaddr*)&client,&len);
                addfd(epollfd,t,true);
            }
            else if(events[i].events & EPOLLIN)
            {
                pthread_t thread;
                fds fds_for_new_work;
                fds_for_new_work.epollfd = epollfd;
                fds_for_new_work.socket = events[i].data.fd;
                pthread_create(&thread,NULL,work,(void *)&fds_for_new_work); // 传递给工作线程的函数
            }
            else
            {
                printf("someting else happen\n");
            }
            
        }
    }
    close(listenfd);
    return 0;
}

利用I/O复用 可以实现的高级功能有:
1.非阻塞的connect
同时发出多个链接 。用select监听有无读写数据
2.聊天室程序
多个用户同时在线进行聊天 poll I/O复用实现多用户在线聊天
客户端程序:


3.同时处理TCP和UDP服务

本文地址:https://blog.csdn.net/qq_41468712/article/details/107254109