<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      epoll和ractor的粗淺理解

      我們繼續上篇的文章繼續更新我們的代碼。
      首先就是介紹一下epoll的三個函數。

      • epoll_create
      • epoll_ctl
      • epoll_wait
        如何去理解這3個函數,我是這樣去理解這個函數,
        就像我們去取快遞一樣,之前的Select模型,是通過輪詢的方式一直去循環遍歷客戶端FD的列表,而EPOLL就相當于專門了一個快遞柜,會將有讀寫事件的FD放到快遞柜里面,而快遞員只需要去快遞柜進行取件和放件就可以了。
        epoll_create函數就相當于我們添加了一個快遞柜在樓下,
        epoll_ctl就相當于我們添加快遞或者取快遞在快遞柜中,
        epoll_wait就相當于快遞員什么時候進行取件,什么時候取送件。

      這就會有一些優點什么優點那?

      1. 不需要循環遍歷所有fd
      2. 每一次取就緒集合,在固定位置;
      3. 異步解耦

      那么我們在下面看一下EPOLL實現的服務器的代碼,我會把對應的注釋標記到代碼上

      #include<stdio.h>
      #include<sys/socket.h>
      #include<sys/types.h>
      #include<netinet/in.h>
      #include<fcntl.h>
      #include <unistd.h>
      #include<sys/epoll.h>
      #include <string.h>
      
      #define BUFFER_LENGTH 128
      #define EVENTS_LENGTH 128
      
      char rbuffer[BUFFER_LENGTH] = {0};
      char wbuffer[BUFFER_LENGTH] = {0};
      
      int main()
      {
      
           unsigned char buffer[BUFFER_LENGTH]={0};
           int ret=0;
          //socket有兩個參數,第一個參數指定我們要使用IPV4,還是IPV6,第二個參數表明我們要使用套接字類型,這里我們使用的是流格式的套接字,第三個參數就是我們需要使用傳輸協議
          //這里使用0,表示讓系統自動推導我們需要使用的傳輸協議。
          int listenfd= socket(AF_INET,SOCK_STREAM,0);
          //如果返回值為-1,說明我們創建SOCKET失敗,直接返回。
          if (listenfd==-1)
          {
              return -1;
          }
          //我們需要綁定的信息
          struct sockaddr_in serveraddr;
          //使用IPV4
          serveraddr.sin_family=AF_INET;
          //我們需要綁定的IP地址,INADDR_ANY 就是0.0.0.0 ,就是所有網卡的所有IP段都可以連接到我們的創建的TCP服務器上。
          serveraddr.sin_addr.s_addr=htonl(INADDR_ANY);
          //我們需要綁定的端口,這里我們綁定的端口為9999
          serveraddr.sin_port=htons(9999);
          //第一個參數我們創建的套接字,第二個是我們填寫的綁定信息,最后是我們的綁定信息結構體的大小。    
          if (-1==bind(listenfd,(const  sockaddr*)&serveraddr,sizeof(serveraddr)))
          {
              return -2;        
          }
          //監聽我們創建的套接字,請求的隊列數量,這里我們填寫為10個
          listen(listenfd,10);
          //定義客戶端的socket
         
          //定義可讀序列和可寫序列
          fd_set rfds,wfds,rset,wset;
          //清空序列
          FD_ZERO(&rfds);
          //設置讀的序列
          FD_SET(listenfd,&rfds);
          //清空可寫的序列
          FD_ZERO(&wfds);
      
          int maxfd=listenfd;
          
          //開始進行EPOLL的創建
          int epfd = epoll_create(1);
          struct epoll_event ev,events[EVENTS_LENGTH];
          ev.events=EPOLLIN;
          ev.data.fd=listenfd;
          //添加我們的服務器通信的listenfd到EPFD中,
          epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev);
          //接下來開始接受 我們的客戶端的連接請求
          while (1)
          {
              //我們需要詳細講解一下這個函數的里面的各個參數的意義 ,以及它什么時候是阻塞的,什么時候是非阻塞的,
              //第一個參數我們的EPFD的文件描述符,第二個我們的接收事件的緩沖器,第三個是我們事件數量的多少,最后一個參數就是我們等待的時長了。
              //當是-1的時候就是一直等待連接的意思,沒有連接就會 一直被阻塞住,
              //當是0的時候就是一直有連接直接返回的意思,
              //當是大于0的數的時候,就是在輪詢查看是否有事件的時長,單位是MS。
              int nready = epoll_wait(epfd,events,EVENTS_LENGTH,-1);
              printf("----------%d\n",nready);
      
      
              //開始遍歷我們的事件
              int i =0;
              for (int i = 0; i < nready; i++)
              {
                  int clientfd=events[i].data.fd;
                  if (listenfd==clientfd)
                  {
                     //如果是我們的監聽的FD,說明是有客戶端連入的事件
                     struct sockaddr_in client;
                     socklen_t len=sizeof(client);
                     //接受客戶端的請求,
                     int connfd=accept(listenfd,(struct sockaddr*)&client,&len);
                     if (connfd==-1)
                     {
                          break;
                     }
                     printf("accept:%d\n",connfd);
                     //增加到我們的快遞柜中
                     ev.events=EPOLLIN;
                     ev.data.fd=connfd;
                     epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,&ev);   
                     //如果是讀的請求            
                  }
                  else if (events[i].events & EPOLLIN)
                  {
                      //如果客戶端在線可以接受到消息
                      int n=recv(clientfd,rbuffer,BUFFER_LENGTH,0);
                      if (n>0)
                      {
                         rbuffer[n]='\0';
                         printf("clientfd :%d recv: %s ,n:%d\n",clientfd,rbuffer,n);
                         memcpy(wbuffer,rbuffer,BUFFER_LENGTH);
      
                         ev.events=EPOLLOUT;
                         ev.data.fd=clientfd;
      
                         epoll_ctl(epfd,EPOLL_CTL_MOD,clientfd,&ev);
                         //客戶端退出的時候會觸發
                      }
                      else
                      {
                       
                         ev.data.fd=clientfd;
                         epoll_ctl(epfd,EPOLL_CTL_DEL,clientfd,&ev);
                      }
                  }
                  else if(events[i].events & EPOLLOUT)
                  {
                          int sent = send(clientfd, wbuffer, BUFFER_LENGTH, 0); //
      				    printf("sent: %d\n", sent);
      
      				    ev.events = EPOLLIN;
      				    ev.data.fd = clientfd;
      
      				    epoll_ctl(epfd, EPOLL_CTL_MOD, clientfd, &ev);
      				
                  }
                             
                  
              }
              
          }
          
          return 0;
      
      }
      

      上面我已經把對應的注釋以及注意的點已經寫在了代碼的上面,
      這里我們還要說一個問題,就是EPOLLLT和EPOLLET的問題
      LT模式
      對于讀事件 EPOLLIN,只要socket上有未讀完的數據,EPOLLIN 就會一直觸發,直到我們的數據接收完畢后才會停止;對于寫事件 EPOLLOUT,只要socket可寫,EPOLLOUT 就會一直觸發。

      在這種模式下,大家會認為讀數據會簡單一些,因為即使數據沒有讀完,那么下次調用epoll_wait()時,它還會通知你在上沒讀完的文件描述符上繼續讀,也就是人們常說的這種模式不用擔心會丟失數據。

      而寫數據時,因為使用 LT 模式會一直觸發 EPOLLOUT 事件,那么如果代碼實現依賴于可寫事件觸發去發送數據,一定要在數據發送完之后移除檢測可寫事件,避免沒有數據發送時無意義的觸發。

      ET模式
      對于讀事件 EPOLLIN,只有socket上的數據從無到有,EPOLLIN 才會觸發;對于寫事件 EPOLLOUT,只有在socket寫緩沖區從不可寫變為可寫,EPOLLOUT 才會觸發(剛剛添加事件完成調用epoll_wait時或者緩沖區從滿到不滿)

      這種模式聽起來清爽了很多,只有狀態變化時才會通知,通知的次數少了自然也會引發一些問題,比如觸發讀事件后必須把數據收取干凈,因為你不一定有下一次機會再收取數據了,即使不采用一次讀取干凈的方式,也要把這個激活狀態記下來,后續接著處理,否則如果數據殘留到下一次消息來到時就會造成延遲現象。

      這種模式下寫事件觸發后,后續就不會再觸發了,如果還需要下一次的寫事件觸發來驅動發送數據,就需要再次注冊一次檢測可寫事件。

      其次我們代碼還有一個問題,就是公用同一個緩沖區的問題,這個問題,我們后面的文章再去解決。

      推薦一個零聲學院免費教程,個人覺得老師講得不錯,
      分享給大家:[Linux,Nginx,ZeroMQ,MySQL,Redis,
      fastdfs,MongoDB,ZK,流媒體,CDN,P2P,K8S,Docker,
      TCP/IP,協程,DPDK等技術內容,點擊立即學習:
      服務器
      音視頻
      dpdk
      Linux內核

      posted @ 2022-10-10 11:15  飄雨的河  閱讀(115)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 久章草这里只有精品| 视频网站在线观看不卡| 花莲县| 亚洲国产一区二区三区| 色噜噜一区二区三区| 方正县| 内射干少妇亚洲69XXX| 国产福利社区一区二区| 四虎国产精品永久在线| 国产成人AV男人的天堂| 亚洲精品乱码久久久久久蜜桃不卡| 日韩V欧美V中文在线| 思思热在线视频精品| 国产对白老熟女正在播放| 久久久久久久无码高潮| 2020国产成人精品视频| 粉嫩国产av一区二区三区| 极品美女aⅴ在线观看| 国产精品免费看久久久| 午夜福利理论片高清在线| 国产精品电影久久久久电影网| 亚洲中文字幕精品久久| 国产成熟女人性满足视频| 国产精品午夜福利片国产| 国产成人精品亚洲午夜| 欧美国产精品啪啪| 自拍第一区视频在线观看| 大香伊蕉在人线国产免费| 久热这里只有精品12| 亚洲成在人线AⅤ中文字幕| 2020国产欧洲精品网站| 亚洲国产长腿丝袜av天堂| 成人欧美一区在线视频| 亚洲综合在线日韩av| 精品无码人妻一区二区三区| 日本高清视频网站www| 亚洲sm另类一区二区三区| 国产黄色三级三级看三级| 亚洲综合精品一区二区三区| 377P欧洲日本亚洲大胆| 豆国产97在线 | 亚洲|