高并发服务器开发与配置( 六 )


epoll_wait范围之后应该是一个循环 , 遍历所有的事件:
while(true){  nfds = epoll_wait(epfd,events,20,500);     for(n=0;n<nfds;++n)  {   if(events[n].data.fd==listener)   { //如果是主socket的事件的话 , 则表示     //有新连接进入了 , 进行新连接的处理 。client=accept(listener,(structsockaddr*)&local,&addrlen);     if(client<0)   //在此最好将client设置为非阻塞    { perror("accept"); continue; }     setnonblocking(client);//将新连接置于非阻塞模式  ev.events=EPOLLIN|EPOLLET; //并且将新连接也加入EPOLL的监听队列 。注意: 并没有设置对写socket的监听    ev.data.fd=client;     if(epoll_ctl(kdpfd,EPOLL_CTL_ADD,client,&ev)<0)       { //设置好event之后 , 将这个新的event通过epoll_ctl加入到epoll的监听队列里面 ,  这里用EPOLL_CTL_ADD来加一个新的epoll事件 , 通过EPOLL_CTL_DEL来减少一个 ,epoll事件 , 通过EPOLL_CTL_MOD来改变一个事件的监听方式 。         fprintf(stderr,"epollsetinsertionerror:fd=%d0,client); return-1;       }    }    elseif(event[n].events&EPOLLIN)    { //如果是已经连接的用户 , 并且收到数据 ,  那么进行读入      int sockfd_r;      if((sockfd_r=event[n].data.fd)<0)    continue;      read(sockfd_r,buffer,MAXSIZE);      //修改该sockfd_r上要处理的事件为EPOLLOUT , 这样可以监听写缓存是否可写 , 直到可写时才写入数据      ev.data.fd=sockfd_r;        ev.events=EPOLLOUT|EPOLLET;      epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd_r,&ev) );//修改标识符 , 等待下一个循环时发送数据 , 异步处理的精髓    }     elseif(event[n].events&EPOLLOUT) //如果有数据发送            {      intsockfd_w=events[n].data.fd;      write(sockfd_w,buffer,sizeof(buffer));      //修改sockfd_w上要处理的事件为EPOLLIN      ev.data.fd=sockfd_w;      ev.events=EPOLLIN|EPOLLET;      epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd_w,&ev) //修改标识符 , 等待下一个循环时接收数据            }     do_use_fd(events[n].data.fd);  } }epoll实例:#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <errno.h>#include <sys/socket.h>#include <netdb.h>#include <fcntl.h>#include <sys/epoll.h>#include <string.h>#define MAXEVENTS 64//函数://功能:创建和绑定一个TCP socket//参数:端口//返回值:创建的socketstatic int create_and_bind (char *port){  struct addrinfo hints;  struct addrinfo *result, *rp;  int s, sfd;  memset (&hints, 0, sizeof (struct addrinfo));  hints.ai_family = AF_UNSPEC;     /* Return IPv4 and IPv6 choices */  hints.ai_socktype = SOCK_STREAM; /* We want a TCP socket */  hints.ai_flags = AI_PASSIVE;     /* All interfaces */  s = getaddrinfo (NULL, port, &hints, &result);//getaddrinfo解决了把主机名和服务名转换成套接口地址结构的问题 。  if (s != 0)    {      fprintf (stderr, "getaddrinfo: %sn", gai_strerror (s));      return -1;    }  for (rp = result; rp != NULL; rp = rp->ai_next)    {      sfd = socket (rp->ai_family, rp->ai_socktype, rp->ai_protocol);      if (sfd == -1)        continue;      s = bind (sfd, rp->ai_addr, rp->ai_addrlen);      if (s == 0)        {          /* We managed to bind successfully! */          break;        }      close (sfd);    }  if (rp == NULL)    {      fprintf (stderr, "Could not bindn");      return -1;    }  freeaddrinfo (result);  return sfd;}//函数//功能:设置socket为非阻塞的static intmake_socket_non_blocking (int sfd){  int flags, s;  //得到文件状态标志  flags = fcntl (sfd, F_GETFL, 0);  if (flags == -1)    {      perror ("fcntl");      return -1;    }  //设置文件状态标志  flags |= O_NONBLOCK;  s = fcntl (sfd, F_SETFL, flags);  if (s == -1)    {      perror ("fcntl");      return -1;    }  return 0;}//端口由参数argv[1]指定int main (int argc, char *argv[]){  int sfd, s;  int efd;  struct epoll_event event;  struct epoll_event *events;  if (argc != 2)    {      fprintf (stderr, "Usage: %s [port]n", argv[0]);      exit (EXIT_FAILURE);    }  sfd = create_and_bind (argv[1]);  if (sfd == -1)    abort ();  s = make_socket_non_blocking (sfd);  if (s == -1)    abort ();  s = listen (sfd, SOMAXCONN);  if (s == -1)    {      perror ("listen");      abort ();    }  //除了参数size被忽略外,此函数和epoll_create完全相同  efd = epoll_create1 (0);  if (efd == -1)    {      perror ("epoll_create");      abort ();    }  event.data.fd = sfd;  event.events = EPOLLIN | EPOLLET;//读入,边缘触发方式  s = epoll_ctl (efd, EPOLL_CTL_ADD, sfd, &event);  if (s == -1)    {      perror ("epoll_ctl");      abort ();    }  /* Buffer where events are returned */  events = calloc (MAXEVENTS, sizeof event);  /* The event loop */  while (1)    {      int n, i;      n = epoll_wait (efd, events, MAXEVENTS, -1);      for (i = 0; i < n; i++)        {          if ((events[i].events & EPOLLERR) ||              (events[i].events & EPOLLHUP) ||              (!(events[i].events & EPOLLIN)))            {              /* An error has occured on this fd, or the socket is not                 ready for reading (why were we notified then?) */              fprintf (stderr, "epoll errorn");              close (events[i].data.fd);              continue;            }          else if (sfd == events[i].data.fd)            {              /* We have a notification on the listening socket, which                 means one or more incoming connections. */              while (1)                {                  struct sockaddr in_addr;                  socklen_t in_len;                  int infd;                  char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];                  in_len = sizeof in_addr;                  infd = accept (sfd, &in_addr, &in_len);                  if (infd == -1)                    {                      if ((errno == EAGAIN) ||                          (errno == EWOULDBLOCK))                        {                          /* We have processed all incoming                             connections. */                          break;                        }                      else                        {                          perror ("accept");                         break;                        }                    }                                  //将地址转化为主机名或者服务名                  s = getnameinfo (&in_addr, in_len,                                   hbuf, sizeof hbuf,                                   sbuf, sizeof sbuf,                                   NI_NUMERICHOST | NI_NUMERICSERV);//flag参数:以数字名返回                                  //主机地址和服务地址                  if (s == 0)                    {                      printf("Accepted connection on descriptor %d "                             "(host=%s, port=%s)n", infd, hbuf, sbuf);                    }                  /* Make the incoming socket non-blocking and add it to the                     list of fds to monitor. */                  s = make_socket_non_blocking (infd);                  if (s == -1)                    abort ();                  event.data.fd = infd;                  event.events = EPOLLIN | EPOLLET;                  s = epoll_ctl (efd, EPOLL_CTL_ADD, infd, &event);                  if (s == -1)                    {                      perror ("epoll_ctl");                      abort ();                    }                }              continue;            }          else            {              /* We have data on the fd waiting to be read. Read and                 display it. We must read whatever data is available                 completely, as we are running in edge-triggered mode                 and won't get a notification again for the same                 data. */              int done = 0; while (1)                {                  ssize_t count;                  char buf[512];count = read (events[i].data.fd, buf, sizeof(buf));                  if (count == -1)                    {                      /* If errno == EAGAIN, that means we have read all                         data. So go back to the main loop. */                      if (errno != EAGAIN)                        {                          perror ("read");                          done = 1;                        }                      break;                    }                  else if (count == 0)                    {                      /* End of file. The remote has closed the                         connection. */                      done = 1;                      break;                    }                  /* Write the buffer to standard output */                  s = write (1, buf, count);                  if (s == -1)                    {                      perror ("write");                      abort ();                    }                }              if (done)                {                  printf ("Closed connection on descriptor %dn",                          events[i].data.fd);                  /* Closing the descriptor will make epoll remove it                     from the set of descriptors which are monitored. */                  close (events[i].data.fd);                }            }        }    }  free (events);  close (sfd);  return EXIT_SUCCESS;}


推荐阅读