1. 程式人生 > >Linux關於IO複用(select使用)

Linux關於IO複用(select使用)

I/O複用網路應用場合

當客戶處理多個描述字 一個客戶同時處理多個套介面 如果一個tcp伺服器既要處理監聽套介面,又要處理連線套介面 如果一個伺服器既要處理TCP,又要處理UDP

select函式作用

這個函式允許程序指示核心等待多個事件中的任一個發生,並僅在一個或多個事件發生或經過某指定的時間後才喚醒程序

select函式什麼情況下返回

作為一個例子,我們可以呼叫函式select並通知核心僅在下列情況發生時才返回: 集合{1,4,5}中的任何描述子準備好讀 或 集合{2,7}中的任何描述字準備好寫或 集合{1,4}中的任何描述字有異常條件待處理或 已經過了10.2秒 也就是說,通知核心我們對哪些描述字感興趣 (讀、寫或異常條件)以及等待多長時間。

select函式

包含標頭檔案<sys/select.h><sys/socket.h> 功能:提供了即時響應多個套接的讀寫事件 原型:     int select(int maxfdp1,fd_set *readset,fd_set *writeset,fd_set *except,const struct timeval *timeout); 引數 maxfdp1:等待最大套接字值加1,(等待套接字的數量) readset:要檢查讀事件的容器 writeset:要檢查寫事件的容器 timeout:超時時間 返回值:返回觸發套件接字的個數

select函式使用

我們還是模擬CS架構,來使用select

下面是伺服器程式碼:

#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <sys/types.h>  
#include <sys/wait.h>
#include <sys/socket.h>  
#include <netinet/in.h> /* for struct sockaddr_in*/  
#include <sys/errno.h>  
#include <signal.h> 
#include <sys/select.h>


//關於IO複用伺服器的socket
void error_exit(char *name)
{
   perror(name);
   exit(-1);
}

int main(int argc,char *argv[])
{
   int sockfd=socket(AF_INET,SOCK_STREAM,0);
   if(sockfd<0)
   {
        error_exit("create error");
   }
   //繫結地址(ip和埠號)
   struct sockaddr_in svraddr;
   memset(&svraddr,0,sizeof(svraddr));
   svraddr.sin_family=AF_INET;
   svraddr.sin_addr.s_addr=INADDR_ANY;
   //svraddr.sin_addr.s_addr=inet_addr("127.0.0.1");第二種寫法
   svraddr.sin_port=htons(5555);
   int ret=bind(sockfd,(struct sockaddr*)&svraddr,sizeof(svraddr));
   if(ret<0)
   {
      error_exit("bind error");
   }
   //設定監聽引數back login 半連線數最大
   ret=listen(sockfd,1024);
   if(ret<0)
   {
      error_exit("listen error");
   }
   //新增service到fd
   fd_set listen_set,temp_set;
   FD_ZERO(&temp_set);
   FD_ZERO(&listen_set);
   FD_SET(sockfd,&listen_set);

   int maxfd=sockfd; 
   struct sockaddr removeaddr;
   int addr_len = sizeof(removeaddr);
   //定義一個屬於去儲存accept fd,FD_SETSIZE函式能夠監聽檔案描述符最大值
   int fds[FD_SETSIZE]={0};
   int i=0;
   for(;i<FD_SETSIZE;i++)
   {
       fds[i]=-1;
   }
   char buf[1024]={0};
   while(1)
   {
        temp_set=listen_set;
        int nevent=select(maxfd+1,&temp_set,NULL,NULL,NULL);
        if(nevent==0)
        {
             printf("timeout\n");
             continue;
        }
        else if(nevent<0)
        {
            error_exit("select error");
        }
        if(FD_ISSET(sockfd,&temp_set))   
        {
             int fd=accept(sockfd,(struct sockaddr *)&removeaddr,&addr_len);
              if(fd<0)
              {
                 error_exit("accept error");
              }
              printf("new connection  %d…\n",fd);
              //把accept新增到監聽集
              FD_SET(fd,&listen_set);
              //數量改變
              maxfd=maxfd>fd?maxfd:fd;

              for(i=0;i<FD_SETSIZE;i++)
              {
                   if(fds[i]==-1)
                   {
                        fds[i]=fd;
                        break;
                   }
              }
        }
        for(i=0;i<FD_SETSIZE;i++)
        {
               if(fds[i]==-1)
               { 
                   continue;
               }
               if(FD_ISSET(fds[i],&temp_set))
               {
                  memset(buf,0,1024);
                  int rdsize=read(fds[i],buf,1024);
                  if(rdsize<=0)
                  {
                       printf("close %d\n",fds[i]);
                       //對方socket關閉,或者出錯,把accept,fd移出監聽集合
                       close(fds[i]);
                       FD_CLR(fds[i],&listen_set); 
                       fds[i]=-1;
                  }
                  else
                  {
                      printf("read buf =%s,%d\n",buf,fds[i]);
                  }
               }
        }
   }
   return 0;
}

下面是客戶端程式碼,寫入資料hello

#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <sys/types.h>  
#include <sys/wait.h>
#include <sys/socket.h>  
#include <netinet/in.h> /* for struct sockaddr_in*/  
#include <sys/errno.h>  
#include <signal.h> 

//關於客戶端的socket
void error_exit(char *name)
{
   perror(name);
   exit(-1);
}
int main(int argc,char *argv[])
{
   if(argc<3)
   {
        printf("run program+ip+port\n");
        return-1;
   }
   int sockfd=socket(AF_INET,SOCK_STREAM,0);
   if(sockfd<0)
   {
        error_exit("create error");
   }
   //連線伺服器,設定伺服器的地址(ip和埠)
   struct sockaddr_in svraddr;
   memset(&svraddr,0,sizeof(svraddr));
   svraddr.sin_family=AF_INET;
   svraddr.sin_addr.s_addr= inet_addr(argv[1]);
   svraddr.sin_port=htons(atoi(argv[2]));
   int ret =connect(sockfd,(struct sockaddr *)&svraddr,sizeof(svraddr));
   if(ret<0)
   {
      error_exit("connect error");
   }

   write(sockfd,"hello",strlen("hello"));
   sleep(5); 
   close(sockfd);
   return 0;
}

先執行伺服器程式碼:

等待中......

執行客戶端程式碼:

 

檢視服務端結果如下:

總結:成功讀出來資料,證明我們輸入成功,並且把fd讀出來,解釋一下為何從fd為何從4開始,我們知道系統中有0,1,2分別代表標準輸入、標準輸出和標準錯誤資訊輸出,總共3個,所以我們建立的fd從4開始計算