Linux c++(socket网络通信 & epoll的三种模式)

  • 默认:水平触发模式 - 根据读来解释
    • 只要fd对应的缓冲区有数据
    • epoll_wait返回
    • 返回的次数与发送数据的次数没有关系
    • epoll默认的工作模式
  • ET: 边沿触发模式
    • 客户端给server发数据
      • 发一次数据server的epoll——wait返回一次
      • 不在乎诗句是否读完
                  // 将新的到的cfd挂到树上
                  struct epoll_event temp;
                  // 设置边沿触发
                  temp.events = EPOLLIN | EPOLLET;
                  temp.data.fd = cfd;
                  epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&temp);
  • 边沿非阻塞触发
    • 效率最高
    • 如何设置非阻塞
      • open()
        • 设置flags
        • 必须O_WDRW|O_NONBLOCK
        • 终端文件: /dev/tty
      • fcntl
        • int flag = fcntl(fd,F_GETFL);
        • flag |=O_NONBLOCK;
        • fcntl(fd,F_SETFL,flag);
    • 将缓冲区的全部数据读出
      while(recv() > 0)
      {
        printf();
      }

边沿非阻塞代码示例

 #include <stdio.h>                                                                       
  #include <sys/types.h>
  #include <arpa/inet.h>
  #include <string.h>
  #include <stdlib.h>
  #include <unistd.h>
  #include <sys/epoll.h>
  #include <fcntl.h>
  #include <errno.h>
  int main(int argc,char *argv[])
  {
      if(argc< 2)
      {    
          printf("eg: ./app port");
          exit(1);
      }
      int port = atoi(argv[1]);
      struct sockaddr_in serv_addr;
      socklen_t serv_len = sizeof(serv_addr);
      //创建套接字
      int lfd = socket(AF_INET,SOCK_STREAM,0);
      //初始化服务器
      memset(&serv_addr,0,serv_len);
      serv_addr.sin_family = AF_INET;
      serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
      serv_addr.sin_port = htons(port);
      //绑定IP和端口
      bind(lfd,(struct sockaddr*)&serv_addr,serv_len);
      //设置同时监听的最大个数
      listen(lfd,36);
      printf("Start accept......\n");
      struct sockaddr_in client_addr;
      socklen_t cli_len = sizeof(client_addr);
      // 创建epoll树根节点
      int epfd = epoll_create(2000);
      // 初始化树
      struct epoll_event ev;
      ev.events = EPOLLIN;
    ev.data.fd = lfd;
      epoll_ctl(epfd,EPOLL_CTL_ADD,lfd,&ev);
  
      struct epoll_event all[2000];
      while(1)
      {
          // 使用epoll通知内核fd 文件IO检测 
          int ret = epoll_wait(epfd,all,sizeof(all)/sizeof(all[0]),-1);
          // 便利all数组中的前ret个元素
          for(int i = 0;i<ret;++i)
          {
              int fd = all[i].data.fd;
              // 判断是否有新链接
              if(fd == lfd)
              {
                  int cfd = accept(lfd,(struct sockaddr*)&client_addr,&cli_len);
                  if (cfd == -1) {
                      perror("accept error");
                      exit(1);
                  }
                  // 设置文件cfd为非阻塞模式
                  int flag = fcntl(cfd,F_GETFL);
                  flag |= O_NONBLOCK;
                  fcntl(cfd,F_SETFL);
                  // 将新的到的cfd挂到树上
                  struct epoll_event temp;
                  temp.events = EPOLLIN | EPOLLET;
                  temp.data.fd = cfd;
                  epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&temp);
              }else{
                  // 处理已经链接的客户端发送过来的数据 
                 if(!all[i].events & EPOLLIN)
                      continue;
                  // 读数据
                  char buf[1024] = {0};
                  int len;
                  while((len =recv(fd,buf,sizeof(buf),0))> 0)   
         {
                      // 数据打印到终端
                      write(STDOUT_FILENO,buf,len);
                      // 发送给客户端
                      send(fd,buf,len,0);
                  }
                  if(len == 0)
                  {
                      printf("client disconnected ......\n");
                      // 将fd从树上栽下
                      ret = epoll_ctl(epfd,EPOLL_CTL_DEL,fd,NULL);
                      if (ret == -1) {
                          perror("epoll_ctl - del error");
                          exit(1);
                      }
                      close(fd);
                  }else if(len < 0)
                  {
                      if(errno == EAGAIN)
                      {
                          printf("缓冲区数据已经读完\n");
                      }else{
                          perror("recv error");
                          exit(1);
                      }
                  }
 #if 0
                  if(len < 0)
                  {
                  }else if(len == 0)
                  {
                  }else{
                      printf("recv %s\n",buf);
                      send(fd,buf,len,0);
                  }
  #endif
              }
          }
      }
      close(lfd);
      return 0;
  }           

posted on 2021-05-10 11:40  lodger47  阅读(499)  评论(0)    收藏  举报

导航