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

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

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

      網絡io與select

      我們知道網絡IO模型一共有5種,這里我們主要討論同步IO和select多路復用的情況。
      我們先從一個簡單的TCP服務器的代碼出發,來討論一下這個是怎么實現的。

      一個十分簡單的TCP服務器

      一個簡單的TCP的服務器的建立流程是這樣

      • 建立SOCKET
      • 綁定端口
      • 監聽
      • 接受連接
      • 接受消息
      • 發送消息
      • 關閉連接
      #include<stdio.h>
      #include<sys/socket.h>
      #include<sys/types.h>
      #include<netinet/in.h>
      #include<fcntl.h>
      #include <unistd.h>
      
      
      
      #define BUFFER_LENGTH 128
      
      
      int main()
      {
          //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
          struct sockaddr_in client;
          //客戶端結構體的長度
          socklen_t len=sizeof(client);
          //等待接受連接
          //第一個參數服務器的套接字,第二個接收到的客戶端的socket,第三個函數就是結構體的長度
          int clientfd=accept(listenfd,(struct sockaddr*)&client,&len);
          //接受的緩沖區大小
          unsigned char buffer[BUFFER_LENGTH]={0};
          //收函數
          //第一個參數客戶端的套接字,第二個參數,接受的緩沖區,第三個參數緩沖區的大小,第4個參數接收到的字節數
          int ret = recv(clientfd,buffer,BUFFER_LENGTH,0);
          if (ret==0)
          {
             close(clientfd);
      
          }
          printf("buffer: %s , ret : %d\n",buffer,ret);
          //發函數
          //第一個參數客戶端的套接字,第二個參數,發送的緩沖區,第三個參數發送的字節數,第4個參數實際發送的字節數
          ret = send(clientfd,buffer,ret,0);
      
      
      }
      

      上面的代碼已經把每個函數的參數的作用,還有 參數的意義都已經注釋上了,
      運行一下上面的代碼,并使用我們的網絡調試助手,發現我們的客戶端已經可以收發數據了。
      現在就遇到了一個問題,如果我們想一直接受和發送數據,我們需要做什么處理那?
      可能大多數人都可以想到,我們添加 一個while循環的就可以一直接受數據了,因此我們改變一下我們的代碼。
      讓它可以一直收發數據,知道我們的客戶端退出為止。

      #include<stdio.h>
      #include<sys/socket.h>
      #include<sys/types.h>
      #include<netinet/in.h>
      #include<fcntl.h>
      #include <unistd.h>
      
      
      
      #define BUFFER_LENGTH 128
      
      
      int main()
      {
          //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
          struct sockaddr_in client;
          //客戶端結構體的長度
          socklen_t len=sizeof(client);
          //等待接受連接
          //第一個參數服務器的套接字,第二個接收到的客戶端的socket,第三個函數就是結構體的長度
          int clientfd=accept(listenfd,(struct sockaddr*)&client,&len);
          //接受的緩沖區大小
          unsigned char buffer[BUFFER_LENGTH]={0};
          while (1)
          {
             //收函數
              //第一個參數客戶端的套接字,第二個參數,接受的緩沖區,第三個參數緩沖區的大小,第4個參數接收到的字節數
              int ret = recv(clientfd,buffer,BUFFER_LENGTH,0);
              if (ret<=0)
              {
                  close(clientfd);
                  break;
              }
              printf("buffer: %s , ret : %d\n",buffer,ret);
              //發函數
              //第一個參數客戶端的套接字,第二個參數,發送的緩沖區,第三個參數發送的字節數,第4個參數實際發送的字節數
              ret = send(clientfd,buffer,ret,0);
          }
          
          
          return 0;
      
      }
      

      這樣我們就達到了我們的要求,那么還有新的問題,就是我創建一個TCP server不能只使用一個。
      下面就是如果我這個TCPserver想要連接多個客戶端我應該怎么去做?
      有兩個可行的方案 ,供我們使用:

      1. 多線程、多進程的方式
      2. select,poll,epoll多路復用的方式。
        我們首先看一下第一種的方式
        多進程的方式,創建一個服務器,實現一個TCP 服務器可以連接多個TCP 客戶端
      #include<stdio.h>
      #include<sys/socket.h>
      #include<sys/types.h>
      #include<netinet/in.h>
      #include<fcntl.h>
      #include <unistd.h>
      
      
      
      #define BUFFER_LENGTH 128
      
      
      int main()
      {
      
           unsigned char buffer[BUFFER_LENGTH]={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
          ;
          while (1)
          {
              struct sockaddr_in client;
              //客戶端結構體的長度
              socklen_t len=sizeof(client);
              //等待接受連接
              //第一個參數服務器的套接字,第二個接收到的客戶端的socket,第三個函數就是結構體的長度
              int clientfd=accept(listenfd,(struct sockaddr*)&client,&len);
              if (clientfd<0)
              {
                  close(listenfd);
                  return -3;
              }
              
              pid_t id=fork();
              if (id<0)
              {
                  perror("fork");
              }else if (id==0)
              {
                 close(listenfd);
                 pid_t idd= fork();
                 if (idd<0)
                 {
                   perror("second fork");
                   _exit(5);
                 }
                 else if (idd==0)
                 {
                      while (1)
                  {
                      //收函數
                      //第一個參數客戶端的套接字,第二個參數,接受的緩沖區,第三個參數緩沖區的大小,第4個參數接收到的字節數
                       int ret = recv(clientfd,buffer,BUFFER_LENGTH,0);
                      if (ret<=0)
                      {
                          close(clientfd);
                          break;
                      }
                       printf("buffer: %s , ret : %d\n",buffer,ret);
              //發函數
              //第一個參數客戶端的套接字,第二個參數,發送的緩沖區,第三個參數發送的字節數,第4個參數實際發送的字節數
                       ret = send(clientfd,buffer,ret,0);
              }
                 }else{
                   _exit(6);
                 }
                 
      
                 
              }
              
      
      #if 0
              //接受的緩沖區大小
              unsigned char buffer[BUFFER_LENGTH]={0};
              while (1)
              {
                 //收函數
                  //第一個參數客戶端的套接字,第二個參數,接受的緩沖區,第三個參數緩沖區的大小,第4個參數接收到的字節數
                  int ret = recv(clientfd,buffer,BUFFER_LENGTH,0);
                  if (ret<=0)
                  {
                   close(clientfd);
                      break;
                  }
                  printf("buffer: %s , ret : %d\n",buffer,ret);
              //發函數
              //第一個參數客戶端的套接字,第二個參數,發送的緩沖區,第三個參數發送的字節數,第4個參數實際發送的字節數
                  ret = send(clientfd,buffer,ret,0);
              }
      #endif
          }
         
          
          
          return 0;
      
      }
      

      然后我們在看看通過第一種方式的多線程的方式來完成我們的TCP服務器

      #include<stdio.h>
      #include<sys/socket.h>
      #include<sys/types.h>
      #include<netinet/in.h>
      #include<fcntl.h>
      #include <unistd.h>
      #include<thread>
      
      
      #define BUFFER_LENGTH 128
      
      void routine(void *arg) {
      
      	int clientfd = *(int *)arg;
      
      	while (1) {
      		
      		unsigned char buffer[BUFFER_LENGTH] = {0};
      		int ret = recv(clientfd, buffer, BUFFER_LENGTH, 0);
      		if (ret == 0) {
      			close(clientfd);
      			break;
      			
      		}
      		printf("buffer : %s, ret: %d\n", buffer, ret);
      
      		ret = send(clientfd, buffer, ret, 0); // 
      
      	}
      
      }
      
      int main()
      {
      
           unsigned char buffer[BUFFER_LENGTH]={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
          ;
          while (1)
          {
              struct sockaddr_in client;
              //客戶端結構體的長度
              socklen_t len=sizeof(client);
              //等待接受連接
              //第一個參數服務器的套接字,第二個接收到的客戶端的socket,第三個函數就是結構體的長度
              int clientfd=accept(listenfd,(struct sockaddr*)&client,&len);
              if (clientfd<0)
              {
                  close(listenfd);
                  return -3;
              }
              
              std::thread t{&routine,&clientfd};
              t.detach();
      
                 
          }
              
      
      #if 0
              //接受的緩沖區大小
              unsigned char buffer[BUFFER_LENGTH]={0};
              while (1)
              {
                 //收函數
                  //第一個參數客戶端的套接字,第二個參數,接受的緩沖區,第三個參數緩沖區的大小,第4個參數接收到的字節數
                  int ret = recv(clientfd,buffer,BUFFER_LENGTH,0);
                  if (ret<=0)
                  {
                   close(clientfd);
                      break;
                  }
                  printf("buffer: %s , ret : %d\n",buffer,ret);
              //發函數
              //第一個參數客戶端的套接字,第二個參數,發送的緩沖區,第三個參數發送的字節數,第4個參數實際發送的字節數
                  ret = send(clientfd,buffer,ret,0);
              }
      #endif   
          
          return 0;
      
      }
      

      這樣我們的就完成了我們TCP 單進程多線程服務器的創建,這里會有一個問題:如果我們就想要通過單線程完成我們的可以接受多個客戶端的連接,我們應該怎么去實現那?
      在這里,我大概講一下我自己的理解,可能理解的不對,也是剛開始學,我是這樣去理解,當我們使用單線程只能接受一個客戶端的TCP服務器的時候,當我們連接第2個客戶端的時候,ACCEPT是可以接收的,但是沒有辦法進行消息的收發,因此我們就有這樣的一個思路去完成我們的需求,就是通過類似哈希表一樣的結構,有一個新的連接連入我們的時候,我們就插入這個表,然后我們開始輪詢遍歷這個表中的連接,如果有讀寫的事件產生,我們就調用recv和send函數進行調用,完成我們的需求。

      類似圖片中的輪詢,下面我們看一下使用select函數的代碼是怎么樣的。

      #include<stdio.h>
      #include<sys/socket.h>
      #include<sys/types.h>
      #include<netinet/in.h>
      #include<fcntl.h>
      #include <unistd.h>
      
      
      #define BUFFER_LENGTH 128
      
      
      
      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;
          
          while (1)
          {
              //開始進行序列的賦值,
              rset=rfds;
              wset=wfds;
      
              //select開始多路服用
              //第一個參數是所有文件描述符的范圍,第二個參數監控讀的文件描述符的序列,第三個參數監控寫的文件描述符的序列, 第4個參數監控異常的序列,第5個參數等待的時間,0是指無限等待
              int nready=select(maxfd+1,&rset,&wset,NULL,NULL);
              //判斷listenfd服務器socket是否被設置,設置代表有效。
              if (FD_ISSET(listenfd,&rset))
              {
                  printf("listen --> \n");
                  struct sockaddr_in client;
      			socklen_t len = sizeof(client);
                  	//開始接受客戶端得連接
      			int clientfd = accept(listenfd, (struct sockaddr*)&client, &len);
                  //添加可讀列表中
      			FD_SET(clientfd, &rfds);
                  //如果客戶端的文件描述符的序號大于最大的文件描述符的編號
      			if (clientfd > maxfd) maxfd = clientfd;
              }
              int i = 0;
              //開始輪詢監聽的已經連接的客戶端的socket
      		for (i = listenfd+1; i <= maxfd;i ++) {
                  //如果有可讀的事件
      			if (FD_ISSET(i, &rset)) { //
                      //開始接受信息
      				ret = recv(i, buffer, BUFFER_LENGTH, 0);
      				if (ret <= 0) {
      					close(i);
      					FD_CLR(i, &rfds);
      					
      				} else if (ret > 0) {
                          //打印接受到信息
      					printf("buffer : %s, ret: %d\n", buffer, ret);
                          //設置可寫的事件
      					FD_SET(i, &wfds);
      				}
      				//如果有可寫的事件
      			} else if (FD_ISSET(i, &wset)) {
      				//開發發送消息
      				ret = send(i, buffer, ret, 0); // 
      				//清空可寫的事件
      				FD_CLR(i, &wfds); 
                      //設置可讀事件,因為并沒有斷開連接
      				FD_SET(i, &rfds);
      				
      
      			}
      
      		}
      		
          }
              
          return 0;
      
      }
      
      

      這樣就完成了我們的select選擇模型的代碼。今天我們就介紹到這里。

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

      posted @ 2022-10-08 21:04  飄雨的河  閱讀(46)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 94人妻少妇偷人精品| 中文字幕第55页一区| 亚洲午夜爱爱香蕉片| 成人无码一区二区三区网站| 日韩免费无码视频一区二区三区| 国产一区二区三区av在线无码观看 | 无码人妻一区二区三区精品视频 | 免费视频爱爱太爽了| 无码AV无码免费一区二区| 国产亚洲精品久久久久秋霞| 亚洲这里只有久热精品伊人| 免费大片黄国产在线观看| 亚洲精品宾馆在线精品酒店| 98精品全国免费观看视频| 国产精品亚洲二区在线播放| 国产精品白丝久久av网站| 亚洲欧美日韩综合久久久| 昆山市| 国产成人久久精品二区三| 樱桃视频影院在线播放| 东京热大乱系列无码| 丁香婷婷色综合激情五月| 亚洲av成人一区在线| 国内综合精品午夜久久资源| 国产色精品久久人妻| 国产麻豆放荡av激情演绎| 67194熟妇在线直接进入| 国产99视频精品免费视频36| 亚洲天堂男人的天堂在线| 欧美18videosex性欧美黑吊| 国产精品白浆在线观看免费| 国产精品国产三级国AV| 亚洲人成电影网站 久久影视| 国内精品自产拍在线播放| 欧美老熟妇喷水| jk白丝喷浆| 一级毛片网| 国产精品播放一区二区三区| 亚洲欧美偷国产日韩| 四虎国产精品永久入口| 亚洲精品一区二区三天美|