epoll初步使用

最近准备学习一下epoll,就写了一个小程序做测试。把中间遇到的问题记录下来,请大家多多指正。

代码很简单:

一个服务端,接收请求,将接收到的内容再返回给客户端。

一个客户端,多线程的发送请求再接收。


server.cc

#include <stdio.h>
#include <strings.h>
#include <string.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>

#define BUFSIZE 1024
#define MAXEVENTS 1024

typedef struct cusdata{
        char buf[BUFSIZE];
        int size;
}cusdata;


cusdata bufdata[MAXEVENTS];

void setnonblocking(int sock)
{
    int opts;
    opts=fcntl(sock,F_GETFL);
    if(opts<0)
    {
        perror("fcntl(sock,GETFL)");
        exit(1);
    }
    opts = opts|O_NONBLOCK;
    if(fcntl(sock,F_SETFL,opts)<0)
    {
        perror("fcntl(sock,SETFL,opts)");
        exit(1);
    }
}

void setreuse(int sock){
        int flag=1;
        if( setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &flag,sizeof(int))==-1)
        {
                perror("setsockopt");
                exit(0);
        } 

}
void test(){
        int accepted = 0;
        struct sockaddr_in clientaddr;
        struct sockaddr_in serveraddr;

        int listenfd = socket(AF_INET, SOCK_STREAM, 0);
        if(listenfd == -1){
                perror("listen_fd");
                exit(0);
        }
        setnonblocking(listenfd);
        setreuse(listenfd);

        bzero(&serveraddr, sizeof(serveraddr));
        serveraddr.sin_family = AF_INET;
        char *local_addr="127.0.0.1";
        int port=9988;
        inet_aton(local_addr,&(serveraddr.sin_addr));
        serveraddr.sin_port=htons(port);
        bind(listenfd,(sockaddr *)&serveraddr, sizeof(serveraddr));
        listen(listenfd, 1000);

        int epollfd = epoll_create(1024);
        if(epollfd == -1){
                perror("epoll_create");
                exit(0);
        }
        struct epoll_event ev, events[MAXEVENTS];
        ev.events = EPOLLIN;
        ev.data.fd = listenfd;

        int ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &ev);
        if(ret == -1){
                perror("epoll_ctl");
                exit(0);
        }


        while(1){
//                printf("to wait event\n");
                int nfds = epoll_wait(epollfd, events, MAXEVENTS, 100);
                if(nfds == -1){
                        perror(NULL);
                }
                for(int i=0; i <nfds; i++){
                        if(events[i].data.fd == listenfd){
                                int addrlen = sizeof(clientaddr); 
                                int newfd = accept(listenfd, (sockaddr*)&clientaddr, (socklen_t*)&addrlen);
                                if(newfd==-1){
                                        perror("accept");
                                        exit(0);
                                }
                                char *addrstr = inet_ntoa(clientaddr.sin_addr);
//                                printf("accept client from addrstr:%s newfd:%d\n", addrstr, newfd);
                                accepted++;
                                printf("accepted:%d\n", accepted);
                                ev.data.fd = newfd;
                                ev.events = EPOLLIN|EPOLLET;

                                epoll_ctl(epollfd, EPOLL_CTL_ADD, newfd, &ev);
                        }
                        else if(events[i].events & EPOLLIN){
                                int curfd = events[i].data.fd;
//                                printf("---------EPOLLIN------------\n");
                                memset(bufdata[curfd].buf, 0, BUFSIZE);
                                int n = read(curfd, bufdata[curfd].buf, BUFSIZE);
                                if(n<=0){
//                                        printf("close %d\n", curfd);
                                        close(curfd);
                                }
                                else{
//                                        printf("recv fd:%d %s\n",curfd, bufdata[curfd].buf);
                                        bufdata[curfd].size = n;
                                        ev.data.fd = curfd;
                                        ev.events = EPOLLOUT | EPOLLET;
                                        epoll_ctl(epollfd, EPOLL_CTL_MOD, curfd, &ev);
                                }
                        }
                        else if(events[i].events & EPOLLOUT){
 //                               printf("---------EPOLLOUT------------\n");
                                int curfd = events[i].data.fd;
                                write(curfd, bufdata[curfd].buf, bufdata[curfd].size);
 //                               printf("send fd:%d %s\n",curfd, bufdata[curfd].buf);
                                ev.data.fd = curfd;
                                ev.events = EPOLLIN | EPOLLET;
                                epoll_ctl(epollfd, EPOLL_CTL_MOD, curfd, &ev);

                        }
                }
        }

}

int main(){
        test();
}
                

server.cc里遇到的陷阱:

epoll_data结构如下:

typedef unionepoll_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 */

};

仔细看epoll_data为一个union,不能给ptr和fd同时赋值,否则会覆盖。



client.cc

#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <string.h>
#include <pthread.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>

#define BUFSIZE 10
#define SENDTIME 100000
#define THREAD_NUM 100

pthread_attr_t attr;

void* worker(void* args){
        int* ptid = (int*)args;
        int tid = *ptid;
        char *local_addr="127.0.0.1";
        int port=9988;
        for(int i=0; i<SENDTIME; i++){

                struct sockaddr_in serveraddr;
                int fd = socket(AF_INET, SOCK_STREAM, 0);
                if(fd == -1){
                        perror("socket");
                        exit(0);
                }
                bzero(&serveraddr, sizeof(serveraddr));
                serveraddr.sin_family = AF_INET;
                inet_aton(local_addr,&(serveraddr.sin_addr));
                serveraddr.sin_port=htons(port);

                int ret = connect(fd, (sockaddr*)&serveraddr, sizeof(serveraddr));
                if(ret<0){
                        perror("connect");
                        exit(0);
                }

                char buf[BUFSIZE];
                char msg[BUFSIZE];
                memset(msg, 0, BUFSIZE);
                int len = snprintf(buf, BUFSIZE, "%d-%d", tid, i);
                ret = write(fd, buf, len);
                printf("send %s\n", buf);
                if(ret != len){
                        printf("write error tid:%d time:%d write_len:%d expect_len:%d\n", tid, i, ret, len);
                        perror(NULL);
                        exit(0);
                }
                ret = read(fd, msg, BUFSIZE);
                printf("recv %s\n", msg);
                if(ret != len){
                        printf("write error tid:%d time:%d read_len:%d expect_len:%d\n", tid, i, ret, len);
                        perror(NULL);
                        exit(0);
                }
                close(fd);
        }

}
 
void test(){
        pthread_t pids[THREAD_NUM];
        int args[THREAD_NUM];
        for(int i = 0; i<THREAD_NUM; i++){
                args[i]=i;
                ret=pthread_create(&(pids[i]), NULL, worker, (void*)&(args[i]));
                if(ret!=0){
                        perror("pthread_create");
                        exit(0);
                }
        }

        for(int i=0; i <THREAD_NUM; i++){
                pthread_join(pids[i], NULL);
        }

}

               
int main(){
        test();
}
                


client.cc遇到问题:

1 当连接次数达到28063时出现Cannot assign requested addressgoogle了一下发现是连接次数太多导致端口用光,需要将TIME_WAIT状态的端口重复利用才行。所以将listenfd设置为SO_REUSEADDR,但是发现无用,连接次数到了20000多,仍然会出现这个问题。还想请懂的人指导一下。


2当线程创建到300多个时,会出现pthread_create: Cannot allocate memory。google一下得知因为每个线程的栈在10M,在32位系统上,用户进程内存为3G,导致内存不足。解决方法:

1)在子线程里调用pthread_detach(),这样已经完成的子线程会自动释放内存,不用等到join的时候再释放。

  遇到问题:主线程里不用再join等待子线程,但是主线程创建完子线程直接退出。导致部分子线程还没执行完,整个进程就退出。不知有没有既可以将子线程设置为detach(为了及时释放内存),又可以在主线程等待子线程完成的方法。

 

2)修改子线程的栈大小,这样就可以创建更多的子线程。


后来client采用第二种做法,修改了子线程栈大小,如下:

client.cc

#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <string.h>
#include <pthread.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>

#define BUFSIZE 10
#define SENDTIME 1
#define THREAD_NUM 500

pthread_attr_t attr;

void* worker(void* args){
//        pthread_detach(pthread_self());
//        size_t size=0;
//        pthread_attr_getstacksize(&attr, &size);
//        printf("stack size:%d\n", size);
        int* ptid = (int*)args;
        int tid = *ptid;
        char *local_addr="127.0.0.1";
        int port=9988;
        for(int i=0; i<SENDTIME; i++){

                struct sockaddr_in serveraddr;
                int fd = socket(AF_INET, SOCK_STREAM, 0);
                if(fd == -1){
                        perror("socket");
                        exit(0);
                }
                bzero(&serveraddr, sizeof(serveraddr));
                serveraddr.sin_family = AF_INET;
                inet_aton(local_addr,&(serveraddr.sin_addr));
                serveraddr.sin_port=htons(port);

                int ret = connect(fd, (sockaddr*)&serveraddr, sizeof(serveraddr));
                if(ret<0){
                        perror("connect");
                        exit(0);
                }

                char buf[BUFSIZE];
                char msg[BUFSIZE];
                memset(msg, 0, BUFSIZE);
                int len = snprintf(buf, BUFSIZE, "%d-%d", tid, i);
                ret = write(fd, buf, len);
                printf("send %s\n", buf);
                if(ret != len){
                        printf("write error tid:%d time:%d write_len:%d expect_len:%d\n", tid, i, ret, len);
                        perror(NULL);
                        exit(0);
                }
                ret = read(fd, msg, BUFSIZE);
                printf("recv %s\n", msg);
                if(ret != len){
                        printf("write error tid:%d time:%d read_len:%d expect_len:%d\n", tid, i, ret, len);
                        perror(NULL);
                        exit(0);
                }
                close(fd);
        }

}
 
void test(){

        int ret=pthread_attr_init(&attr);
        if(ret!=0){
                perror("pthread_attr_init");
                exit(0);
        }
        ret = pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN+1024);
        if(ret!=0){
                perror("pthread_attr_setstacksize");
                exit(0);
        }
 
        pthread_t pids[THREAD_NUM];
        int args[THREAD_NUM];
        for(int i = 0; i<THREAD_NUM; i++){
                args[i]=i;
                ret=pthread_create(&(pids[i]), NULL, worker, (void*)&(args[i]));
                if(ret!=0){
                        perror("pthread_create");
                        exit(0);
                }
        }

        for(int i=0; i <THREAD_NUM; i++){
                pthread_join(pids[i], NULL);
        }

}

               
int main(){
        test();
}
                


将子线程是栈设置小后,就可以创建更多线程。


总经一下不明白的问题,还请了解的人指导:

1 当连接次数达到28063时出现Cannot assign requested addressgoogle了一下发现是连接次数太多导致端口用光,需要将TIME_WAIT状态的端口重复利用才行。所以将listenfd设置为SO_REUSEADDR,但是发现无用,连接次数到了20000多,仍然会出现这个问题。


2 子线程里调用pthread_detach(),主线程里不用再join等待子线程,但是主线程创建完子线程直接退出。导致部分子线程还没执行完,整个进程就退出。不知有没有既可以将子线程设置为detach(为了及时释放内存),又可以在主线程等待子线程完成的方法。


发现最后要解决的问题都是与系统紧密相关的,关于epoll的反而少了,准备下一步再对epoll做更多学习,到时候再分享给大家。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值