• socket采用epoll编程demo


    epoll工作流程

    首先,需要调用epoll_create创建epoll;
    此后我们就可以进行socket/bind/listen;
    然后调用epoll_ctl进行注册;
    接下来,就可以通过一个while(1)循环调用epoll_wait来等待事件的发生;
    然后循环查看接收到的事件并进行处理;
    1)如果事件是sever的socketfd我们就要进行accept,并且把接收到client的socketfd加入到要监听的事件中;
    2)如果在监听过程中,需要修改操作方式(读/写),可以调用epoll_ctl来重新修改;
    3)如果监听到某一个客户端关闭,那么我就需要再次调用epoll_ctl把它从epoll监听事件中删除。

    epoll的结构体
    typedef union epoll_data {
         void        *ptr;
         int          fd;
         uint32_t     u32;
         uint64_t     u64;
     } epoll_data_t;
     struct epoll_event {
         uint32_t     events;      /* Epoll events */
         epoll_data_t data;        /* User data variable */
     };
    
    #include <stdio.h>
    #include <string.h>
    #include <sys/socket.h>
    #include <sys/epoll.h>
    #include <errno.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <sys/types.h>
    #include <unistd.h>
    #define SERV_PORT  8802
    int main()
    {
        int i,flag;
        int sockfd,clntfd,newfd;
        int epfd,nfds;
        ssize_t n;
        char buffer[1024];
        int s = sizeof(struct sockaddr);
    
        struct sockaddr_in serv_addr;
        struct sockaddr_in clnt_addr;
        //定义epoll数据结构
        struct epoll_event ev,events[20];
    
        epfd = epoll_create(256);
    
        //创建socket,并初始化事件ev
        sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (sockfd < 0) {
            perror("socket error!
    ");
            return -1;
        }
        ev.data.fd = sockfd;
        ev.events = EPOLLIN|EPOLLET;
    
        //注册epoll事件
        flag = epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &ev);
        if (flag < 0) {
            perror("epoll_ctl error!
    ");
            return -1;
        }
        bzero(&serv_addr, sizeof(serv_addr));
        serv_addr.sin_family = AF_INET;
        serv_addr.sin_port = htons(SERV_PORT);
        serv_addr.sin_addr.s_addr = htonl( INADDR_ANY );
    
        flag = bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(struct sockaddr));
        if (flag < 0) {
            perror("bind error!
    ");
            return -1;
        }
        printf("bind
    ");
    
        flag = listen(sockfd, 20);
        if (flag < 0) {
            perror("listen error!
    ");
            return -1;
        }
        printf("listen
    ");
    
        //开始循环
        while (1) {
            //等待事件发生,返回请求数目
            nfds = epoll_wait(epfd, events, 20, 500);
            //一次处理请求
            for (i = 0; i < nfds; ++i) {
                if (events[i].data.fd == sockfd){
                    clntfd = accept(sockfd, (struct sockaddr*)&clnt_addr,(unsigned int*)&s);
                    if (clntfd < 0) {
                        perror("accept error");
                        continue;
                    }
                    printf("accept
    ");
    
                    char *str = inet_ntoa(clnt_addr.sin_addr);
                    printf("accepnt the client ip : %s
    ",str);
    
                    //设置文件标识符,设置操作属性:写操作
                    ev.data.fd = clntfd;
                    ev.events = EPOLLOUT | EPOLLET;
                    //向创建的的epoll进行注册写操作
                    epoll_ctl(epfd, EPOLL_CTL_ADD, clntfd, &ev);
                } else if (events[i].events & EPOLLOUT) {
                    printf("EPOLLOUT
    ");
    
                    if ((newfd = events[i].data.fd) < 0)
                        continue;
                    bzero(buffer,sizeof(buffer));
                    strcpy(buffer,"welcome to myserver!
    ");
                    flag = send(newfd, buffer, 1024, 0);
                    if (flag < 0) {
                        perror("send error");
                        continue;
                    }
                    //修改操作为读操作
                    ev.data.fd = clntfd;
                    ev.events = EPOLLIN | EPOLLET;
                    epoll_ctl(epfd, EPOLL_CTL_MOD, newfd, &ev);
                } else if (events[i].events & EPOLLIN) {
                    printf("EPOLLIN
    ");
    
                    bzero(buffer,sizeof(buffer));
                    if ((newfd = events[i].data.fd) < 0)
                        continue;
                    if ((n = read(newfd, buffer, 1024)) < 0) {
                        if (errno == ECONNRESET){
                            close(newfd);
                            events[i].data.fd = -1;
                            printf("errno ECONRESET!
    ");
                        } else {
                            perror("readbuffer error!
    ");
                        }
                    } else if (n == 0) {//表示客户端已经关闭
                        close(newfd);
                        events[i].data.fd = -1;
                        printf("n为0
    ");
                    }
                    if (buffer[0] != '0')
                        printf("have read: %s
    ", buffer);
                }
            }
        }
        close(sockfd);
        return 0;
    }
    

      

    引自:https://www.bbsmax.com/A/l1dymR3Gde/
    优化
    #include <stdio.h>
    #include <string.h>
    #include <sys/socket.h>
    #include <sys/epoll.h>
    #include <errno.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <sys/types.h>
    #include <unistd.h>
    
    #define SERV_PORT   8802
    #define MAX_EVENTS  20
    
    
    int main()
    {
        int listen_fd = socket(AF_INET, SOCK_STREAM, 0);    //若成功则返回非负描述符,若失败则返回-1,第一个参数指明协议族(IPv4或IPv6等)
        if (listen_fd < 0) {                                //第二个参数指明套接字类型,字节流套接字(SOCK_STREAM)和数据报套接字(SOCK_DGRAM)
            perror("socket error!
    ");
            return -1;
        }
    
    
        int epfd = epoll_create(256);  //参数会被忽略,但是要大于0, 
                                       //若成功返回一个大于 0 的值,表示 epoll 实例;若返回 -1 表示出错
    
        //针对监听的sockfd,创建epollevent
        struct epoll_event event;
        event.data.fd = listen_fd;
        event.events = EPOLLIN | EPOLLET;
    
        //注册epoll事件
        int flag = epoll_ctl(epfd, EPOLL_CTL_ADD, listen_fd, &event);  //int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
        if (flag < 0) {                                                //成功返回0,出错返回-1
            perror("epoll_ctl error!
    ");
            return -1;
        }
    
        
        if (bindAndListenFd(listen_fd) < 0)
            return -1;
    
    
        //定义epoll数据结构
        struct epoll_event events[MAX_EVENTS];  //可以使用vector,参见muduo源码中的使用
    
        while (1) {
            //等待事件发生,返回请求数目
            int nfds = epoll_wait(epfd, events, MAX_EVENTS, 500);   //maxevents: 返回的events的最大个数,如果最大个数大于实际触发的个数,则下次epoll_wait的时候仍然可以返回
                                                                    //int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
                                                                    //成功返回的是一个大于 0 的数,表示事件的个数;返回 0 表示的是超时时间到;若出错返回 -1.
    
            for (int i = 0; i < nfds; ++i) {                  
                if (events[i].data.fd == listen_fd) {
                    struct sockaddr_in client_addr;
                    int client_fd = accept(listen_fd, (struct sockaddr*)&client_addr, sizeof(client_addr));  //若成功则为非负描述符,若出错则返回-1
                    if (client_fd < 0) {
                        perror("accept error");
                        continue;
                    }
    
                    char *str = inet_ntoa(client_addr.sin_addr);
                    printf("accept the client ip : %s
    ",str);
    
                    onRecvNewConnect(epfd, client_fd);
    
                } else if (events[i].events & EPOLLOUT) {
                    int sockfd = events[i].data.fd;
                    if (sockfd < 0)
                        continue;
    
                    onWriteFd(epfd, sockfd);
                    
                } else if (events[i].events & EPOLLIN) {
                    int sockfd = events[i].data.fd;
                    if (sockfd < 0)
                        continue;
    
                    if (onReadFd(epfd, sockfd) < 0) {
                        events[i].data.fd = -1;
                    }
                }
            }
        }
        close(sockfd);
        return 0;
    }
    
    int bindAndListenFd(int sockfd) {
        ::fcntl(sockfd, F_SETFL, O_NONBLOCK);                    //设置非阻塞模式
    
        struct sockaddr_in serv_addr;
    
        bzero(&serv_addr, sizeof(serv_addr));                    //void bzero(void *dest, size_t nbytes);
        serv_addr.sin_family = AF_INET;                          //类似void *memset(void *dest, int c, size_t len);
        serv_addr.sin_port = htons(SERV_PORT);                   //本地端口号转化为网络端口号 host to network short
        serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);           //INADDR_ANY代表本机所有的IP地址  host to network long
    
        int flag = bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));   //成功返回0,出错返回-1
        if (flag < 0) {
            perror("bind error!
    ");
            return -1;
        }
    
        flag = listen(sockfd, 20);                         //成功返回0,出错返回-1
        if (flag < 0) {
            perror("listen error!
    ");
            return -1;
        }
    
        return 0;
    }
    
    void onRecvNewConnect(int epfd, int clientfd) {
        ::fcntl(sockfd, F_SETFL, O_NONBLOCK);                //设置非阻塞模式
        //设置文件标识符,设置操作属性:写操作
        struct epoll_event ev_client;
        ev_client.data.fd = clintfd;
        ev_client.events = EPOLLOUT | EPOLLET;
        //向创建的的epoll进行注册写操作
        epoll_ctl(epfd, EPOLL_CTL_ADD, clientfd, &ev_client);
    }
    
    void onWriteFd(int epfd, int sockfd) {
        char buffer[1024];
        bzero(buffer, sizeof(buffer));
        strcpy(buffer, "welcome to myserver!
    ");
        int flag = send(sockfd, buffer, 1024, 0);
        if (flag < 0) {
            perror("send error");
            return;
        }
        //修改操作为读操作
        struct epoll_event ev_client;
        ev_client.data.fd = sockfd;
        ev_client.events = EPOLLIN | EPOLLET;
        epoll_ctl(epfd, EPOLL_CTL_MOD, sockfd, &ev_client);
    }
    
    
    int onReadFd(int epfd, int sockfd) {
        char buffer[1024];
        bzero(buffer, sizeof(buffer));
        int n = read(sockfd, buffer, 1024);
        if (n < 0) {
            if (errno == ECONNRESET) {
                close(sockfd);
                printf("errno ECONRESET!
    ");
                return -1;
            } else {
                perror("readbuffer error!
    ");
            }
        } else if (n == 0) {      //表示客户端已经关闭
            close(sockfd);
            printf("n为0
    ");
            return -1;
        }
        if (buffer[0] != '0')
            printf("have read: %s
    ", buffer);
        return 0;
    }
  • 相关阅读:
    python ddt 传多个参数值示例
    Appium 输入 & 符号,实际输入&-
    curl 调用jenkins的api
    Android WebView的Js对象注入漏洞解决方案
    Could not find com.android.tools.build:gradle:1.3.0.
    react-native疑难
    win上搭建react-native android环境
    gradle大体内容
    android studio This client is too old to work with the working copy at
    sharedPreference
  • 原文地址:https://www.cnblogs.com/yizhou35/p/12178347.html
Copyright © 2020-2023  润新知