epoll范例

说明:在子线程中启动epoll进行监听,收到报文且检查无误后,通过传入的函数指针转发报文。通过设定EPOLLOUT事件发送回复给客户端。

#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <stdbool.h>
#include <sys/epoll.h>

#define BUF_SIZE 1024                 //默认缓冲区
#define EPOLL_RUN_TIMEOUT -1          //epoll的超时时间
#define EPOLL_SIZE 10000              //epoll监听的客户端的最大数目
#define PHDR_LEN 5
#define MAX_PACKLEN 1024*10

//两个有用的宏定义:检查和赋值并且检测
#define CHK(eval) if(eval < 0){printf("error1\n");perror("error1");return NULL;}
#define CHK2(res, eval) if((res = eval) < 0){printf("error2\n");perror("error2");return NULL;}

static int listener=-1;     //监听socket
static int epfd=-1;  //epoll描述符
static int run=0;

typedef void (*pfun)(char *,int,int);
struct ipp
{
     char *ip;
       int port;
     pfun fp;
};
struct evdata
{
        int fd;
     int len;
        char *buf;
};

struct ipp ptr;
static pthread_t pid;
int epoll_sendresp(char *buf,int len,int fd);
static int check_packet(char *buf,int len);

static struct epoll_event events[EPOLL_SIZE];

int setnonblocking(int sockfd)
{
     if(fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFD, 0)|O_NONBLOCK)<0)
          return -1;
         return 0;
}

void CloseAndDisable(int epfd, int sockid, struct epoll_event ee)
{
     epoll_ctl(epfd, EPOLL_CTL_DEL, sockid, &ee);
     shutdown(sockid,2);
     close(sockid);
}

void *epoll_service_start(void *arg)
{
     struct ipp *p=(struct ipp *)(arg);
     struct sockaddr_in addr,their_addr; 
     addr.sin_family = PF_INET;
     addr.sin_port = htons(p->port);
     addr.sin_addr.s_addr = inet_addr(p->ip);
     socklen_t socklen;
     socklen = sizeof(struct sockaddr_in);

     static struct epoll_event ev;
     ev.events = EPOLLIN | EPOLLET;        //对读感兴趣,边沿触发
     //char message[BUF_SIZE];

     int client, res, epoll_events_count,sockfd;

     CHK2(listener, socket(PF_INET, SOCK_STREAM, 0));               //初始化监听socket
     setnonblocking(listener);                                             //设置监听socket为不阻塞
     int on=1;
     setsockopt(listener,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
     CHK(bind(listener, (struct sockaddr *)&addr, sizeof(addr))); //绑定监听socket
     CHK(listen(listener, 10000));                                             //设置监听

     CHK2(epfd,epoll_create(EPOLL_SIZE));                              //创建一个epoll描述符,并将监听socket加入epoll
     ev.data.fd = listener;
     CHK(epoll_ctl(epfd, EPOLL_CTL_ADD, listener, &ev));

     while(1)
     {
          CHK2(epoll_events_count,epoll_wait(epfd, events, EPOLL_SIZE, EPOLL_RUN_TIMEOUT));//由于是在子线程中启动epoll监听,故超时时间不可设为0,否则会导致CPU使用率居高不下。
          int i;
          for(i = 0; i < epoll_events_count ; i++)
          {
               if(events[i].data.fd == listener)                         //新的连接到来,将连接添加到epoll中,并发送欢迎消息
               {
                    CHK2(client,accept(listener, (struct sockaddr *) &their_addr, &socklen));
                    setnonblocking(client);
                    ev.data.fd = client;
                    ev.events=EPOLLIN | EPOLLET;
                    CHK(epoll_ctl(epfd, EPOLL_CTL_ADD, client, &ev));//将客户端的fd加入epoll监听队列
               }
               else if(events[i].events&EPOLLIN)
               {
                    if ((sockfd = events[i].data.fd) < 0)
                         continue;

                    char *message=NULL;
                    char hdr[6]={0};                   
                    int recvNum = 0;
                    int recved = 0;
                    int packlen=0;
                    int recv_lef = 0;
                    bool bReadOk = false;

                    recvNum = recv(sockfd, hdr, PHDR_LEN, 0);
                    packlen = *(int *)(hdr+1);

                    if (PHDR_LEN < packlen &&packlen > MAX_PACKLEN)
                    {
                         CloseAndDisable(epfd,sockfd, events[i]);
                         continue;
                    }
                   
                    if(recvNum != PHDR_LEN && 0xff != hdr[0])
                    {
                         CloseAndDisable(epfd,sockfd, events[i]);
                         continue;
                    }
                    else
                    {
                        
                         message = (char *)malloc(packlen);
                         memset(message, 0, packlen);
                         memcpy(message, hdr, PHDR_LEN);
                         recved = recvNum;
                         recv_lef = packlen-recved;
                    }
                   
                    while(1)
                    {
                         // 确保sockfd是nonblocking的
                         recvNum = recv(sockfd, message+recved, recv_lef, 0);
                         if(recvNum < 0)
                         {
                              if(errno == EAGAIN)// 由于是非阻塞的模式,所以当errno为EAGAIN时,表示当前缓冲区已无数据可读 在这里就当作是该次事件已处理处.
                              {
                                   bReadOk = true;
                                   break;
                              }
                              else if (errno == ECONNRESET)// 对方发送了RST
                              {
                                   CloseAndDisable(epfd,sockfd, events[i]);
                                   break;
                              }
                              else if (errno == EINTR)// 被信号中断
                                   continue;
                              else
                              {
                                   CloseAndDisable(epfd,sockfd, events[i]);
                                   break;
                              }
                         }
                         else if( recvNum == 0)// 这里表示对端的socket已正常关闭.发送过FIN了。
                         {
                              CloseAndDisable(epfd,sockfd, events[i]);
                              break;
                         }

                         // recvNum > 0
                         recved += recvNum;
                         recv_lef -= recvNum;
                         if(recv_lef == 0)
                         {
                              bReadOk = true;
                              break; // 退出while(1),表示已经全部读完数据
                         }
                         else if(recv_lef < 0)
                         {
                              CloseAndDisable(epfd,sockfd, events[i]);
                              break;
                         }
                    }
                    if(bReadOk==true)
                    {
                                  
                         if(check_packet(message,recved)<0)
                         {
                              printf("error packet\n");
                              CloseAndDisable(epfd,sockfd, events[i]);
                         }
                         else
                              p->fp(message,recved,sockfd);
                         if(message!=NULL)
                         {
                              free(message);
                              message = NULL;
                         }
                    /*    
                         char *str="send resp to client";
                         epoll_sendresp(str,strlen(str),sockfd);
                    */    
                    }
               }
               else if(events[i].events&EPOLLOUT)//有数据待发送,写socket
               {
                    struct evdata *data=(struct evdata *)events[i].data.ptr;
                    sockfd = data->fd;
                    bool bWritten = false;
                    int writenLen = 0;
                    int sent = 0;
                    int send_lef = data->len;
                    while(1)
                    {
                         // 确保sockfd是非阻塞的
                         writenLen = send(sockfd, data->buf+sent, send_lef, 0);
                         if (writenLen == -1)
                         {
                              if (errno == EAGAIN)
                              {
                                   // 对于nonblocking 的socket而言,这里说明了已经全部发送成功了
                                   bWritten = true;
                                   break;
                              }
                              else if(errno == ECONNRESET)
                              {
                                   // 对端重置,对方发送了RST
                                   CloseAndDisable(epfd,sockfd, events[i]);
                                   break;
                              }
                              else if (errno == EINTR)// 被信号中断
                                   continue;
                         }
                         if (writenLen == 0)
                         {
                              // 这里表示对端的socket已正常关闭.
                              CloseAndDisable(epfd,sockfd, events[i]);
                              break;
                         }
                         // 以下的情况是writenLen > 0
                         sent += writenLen;
                         send_lef -= writenLen;
                         if(send_lef==0)
                         {
                              bWritten = true;
                              break;
                         }
                         else if(send_lef<0)
                         {
                              CloseAndDisable(epfd,sockfd, events[i]);
                              break;
                         }
                         break; // 退出while(1)
                    }
                    if (bWritten == true)
                    {
                         //printf("send suceess,send %d Byte\n",sent);
                         //设置用于读操作的文件描述符
                         ev.data.fd=sockfd;
                         //设置用于注测的读操作事件
                         ev.events=EPOLLIN | EPOLLET;
                         epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);//修改标识符,等待下一个循环时接收数据
                    }
                    else
                         printf("send error,do not set EPOLLIN\n");
                    free(data->buf);
                    free(data);
               }
          }
     }
    
     shutdown(listener,2);
     close(epfd);
     listener=epfd=-1;
     return ;
}

int epoll_stop()
{
     pthread_cancel(pid);
     if(listener>0)
          shutdown(listener,2);
     if(epfd>0)
          close(epfd);
     listener=epfd = -1;
     run = 0;
     return 0;
}
int epoll_start(char *ip,int port,pfun fp)
{
     if(run>0)
     {
          printf("epoll has been running\n");
          return -1;
     }
     memset(&ptr,0,sizeof(struct ipp));
     ptr.ip = ip;
     ptr.port = port;
     ptr.fp = fp;

     int rc = pthread_create(&pid, NULL, epoll_service_start, (void *)(&ptr));
     if(rc<0)
     {
          printf("create thread fail\n");
          return -1;
     }
     else
          run = 1;
     return 1;
}

int epoll_sendresp(char *msg,int len,int fd)
{
     struct epoll_event ev;
     if(epfd<0)
     {
          printf("epoll had stopped\n");
          return -1;
     }

     struct evdata *data = (struct evdata *)malloc(sizeof(struct evdata));
     memset(data,0,sizeof(struct evdata));
     data->fd  = fd;
     data->len = len;
     data->buf = (char *)malloc(len+1);
     memset(data->buf,0,len+1);
     memcpy(data->buf,msg,len);
     ev.data.ptr = (void *)data;//下一轮epoll监听会监测到这个EPOLLOUT事件,对应的epoll_event.data.ptr会指向data;
     //设置用于注测的写操作事件
     ev.events = EPOLLOUT | EPOLLET;
     //修改sockfd上要处理的事件为EPOLLOUT。
     epoll_ctl(epfd,EPOLL_CTL_MOD,fd,&ev);
     return 1;
}

static int check_packet(char *msg,int len)
{
     int packlen = *(int *)(msg+1);
     printf("buf len is %d,pg_len is %d,buf is %s\n",len,packlen,msg);
     if(len != packlen)
          return -1;
     if(msg[len-1] != 0x00)
          printf("error inf\n");
     return 1;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值