溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

select---基于TCP客戶/服務(wù)端編程

發(fā)布時間:2020-07-13 12:50:12 來源:網(wǎng)絡(luò) 閱讀:1140 作者:小楊楊雪松 欄目:編程語言

我們先來說說最重要的函數(shù)select這個函數(shù),它的原型如下:

       int select(int nfds,fd_set *readfds,fd_set *writefds,fd_set *exceptfds,struct timeval *timeout);

       select函數(shù)可以執(zhí)行I/O多路轉(zhuǎn)接。傳給select的參數(shù)告訴內(nèi)核:

                1.我們所關(guān)心的描述符

                2.對于每個描述符我們所關(guān)心的條件(是否想從一個給定的描述符讀,寫,還是異常條件)

                3.愿意等待的時間(永遠等待,等待一個給定的時間或者根本不等待)

       從select返回時,內(nèi)核告訴我們:

                1.已準(zhǔn)備好的描述符的總數(shù)量

                2.對于讀,寫,異常這3個條件中的每一個,哪些描述符已經(jīng)準(zhǔn)備好

       有了上面的返回信息,我們就可以調(diào)用相應(yīng)的I/O函數(shù)(read或者write),并且確知該函數(shù)不會被阻塞。


我們再來說說select這個函數(shù)的返回值和相應(yīng)的參數(shù):

       正常返回準(zhǔn)備就緒的描述符數(shù)目,若超時,返回0;出錯時返回-1.

       參數(shù):

                我們先來說最后一個參數(shù),它是一個指向結(jié)構(gòu)體的指針,這個結(jié)構(gòu)體如下:

                struct timeval

                {

                    long tv_sec;

                    long  tv_usec;

                }

                這個參數(shù)指明了愿意等待的時間長度,單位為秒和微妙。當(dāng)它的值為NULL,說明要永遠等待。如果捕捉到一個信號則中斷無限等待。當(dāng)所指定的描述符的一個已經(jīng)準(zhǔn)備好或捕捉到一個信號則返回。當(dāng)這個結(jié)構(gòu)體的兩個成員的值均不為0時,則是我們設(shè)定的要指定的秒數(shù)和微秒數(shù)。當(dāng)指定的一個描述符之一已經(jīng)準(zhǔn)備好,或者當(dāng)指定的時間已經(jīng)超過時立即返回。在超時后還沒有一個描述符準(zhǔn)備好,返回0。

                中間的 readfds,writefds,exceptfds這三個參數(shù)是指向描述符集的指針。這3個描述符集說明了我們關(guān)心的可讀,可寫或處于異常條件的描述符集合。每個描述符集存儲在一個fd_set數(shù)據(jù)類型中。這個數(shù)據(jù)類型為每一個描述符保持一位。在這里我們簡單的認(rèn)為是一個很大的字節(jié)數(shù)組。

                第一個參數(shù) nfds 的意思是“最大文件描述符編號值加1”。因為描述符從0 開始,所以要在最大描述符編號上加1.意思是我們我們在所有描述符集當(dāng)中找出最大的一個,然后加1就行了。這里我們也可以將第一個參數(shù)設(shè)置為FD_SETSIZE,它是一個常量值,它指定最大描述符數(shù)(一般是1024),一般而言,此值有點大。通過我們指定最大的描述符數(shù),內(nèi)核就只需在此范圍內(nèi)尋找打開的位,大大節(jié)省了時間。  


我們再來說說fd_set這個數(shù)據(jù)類型:

       這種數(shù)據(jù)類型的變量經(jīng)常用到以下函數(shù):

             int FD_ISSET(int fd,fd_set *fdset);

             void FD_CLR(int fd,fd_set *fdset);

             void FD_SET(int fd,fd_set *fdset);

             void FD_ZERO(fd_set* fdset);

          這些接口可實現(xiàn)為宏或者函數(shù)。調(diào)用FD_ZERO將一個fd_set變量的所有位設(shè)置為0.調(diào)用FD_SET設(shè)置描述符集中的一位。調(diào)用FD_CLR可以清除一位。我們可以調(diào)用FD_ISSET測試描述符集中的一個指定位是否已經(jīng)打開。我們要注意,在我們聲明了一個描述符集之后,必須調(diào)用FD_ZERO函數(shù)將這個描述符集置為0,然后再在其中設(shè)置我們關(guān)心的各個描述符的位。     


上面說了那么多,我們來用用這幾個函數(shù)吧,在下面的程序中,我們用剛說的那幾個函數(shù)寫了一個簡單的客戶/服務(wù)器間通信模型,客戶向服務(wù)端發(fā)送數(shù)據(jù),服務(wù)端收到后再回顯給客戶端,我們來看看具體代碼:

  

server端:

   #include <stdio.h>
   #include <stdlib.h>
   #include <sys/types.h>
   #include <assert.h>
   #include <unistd.h>
   #include <netinet/in.h>
   #include <arpa/inet.h>
   #include <sys/socket.h>
   
  #define _BACKLOG_ 5
  int fds[64];
  
  static void usage(const char* arg)
  {
      printf("usage:%s [ip][poort]",arg);
  }
  
  static int startup(char *ip,int port)                                                                                                                               
  {
      assert(ip);
      int sock=socket(AF_INET,SOCK_STREAM,0);
      if(sock<0)
      {
          perror("socket");
          exit(1);
      }
  
      struct sockaddr_in local;
      local.sin_family=AF_INET;
      local.sin_port=htons(port);
      local.sin_addr.s_addr=inet_addr(ip);
  
      if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0)
      {
          perror("bind");
          exit(2);
      }
  
      if(listen(sock,_BACKLOG_)<0)
      {
          perror("listen");
          exit(3);
      }
      return sock;                                                                                                                                                    
  }
  
  int main(int argc,char* argv[])
  {
      if(argc!=3)
      {
          usage(argv[0]);
          exit(1);
      }
      int port=atoi(argv[2]);
      char *ip=argv[1];
  
      int listen_sock=startup(ip,port);
      int done=0;
      int new_sock=-1;
      struct sockaddr_in client;
      socklen_t len=sizeof(client);
  
      int max_fd;
      fd_set reads;
      fd_set writs;
  
      int i=0;
      int fds_num=sizeof(fds)/sizeof(fds[0]);
      for( ;i<fds_num;++i)
      {
          fds[i]=-1;
      }
      fds[0]=listen_sock;
      max_fd=fds[0];
      while(!done)
      {
          FD_ZERO(&reads);
          FD_ZERO(&writs);                                                                                                                                            
          FD_SET(listen_sock,&reads);
          struct timeval timeout={5,0};
          for(i=1;i<fds_num;++i)
          {
              if(fds[i]>0)
              {
                  FD_SET(fds[i],&reads);
                  if(fds[i]>max_fd)
                  {
                      max_fd=fds[i];
                  }
              }
          }
          switch(select(max_fd+1,&reads,&writs,NULL,&timeout))
          {
              case 0://超時
                    printf("select timeout\n");
                    break;
              case -1:
                    perror("select");
                    break;
             default:
                   {
                       i=0;
                       for(;i<fds_num;++i)
                       {
                           if(fds[i]==listen_sock&&FD_ISSET(fds[i],&reads))
                           {//監(jiān)聽套接字就緒
                               new_sock=accept(listen_sock,(struct sockaddr*)&client,&len);
                               if(new_sock<0)
                               {
                                   perror("accept");
                                   continue;                                                                                                                         
                               }
                               printf("get a new connect...%d\n",new_sock);
                               for(i=0;i<fds_num;++i)
                               {
                                   if(fds[i]==-1)
                                   {
                                       fds[i]=new_sock;
                                       break;
                                   }
                               }
                               if(i==fds_num)
                               {
                                   close(new_sock);
                               }
                           }//listen sock
                           else if(fds[i]>0&&FD_ISSET(fds[i],&reads))
                           {
                               char buf[1024];
                               ssize_t _s=read(fds[i],buf,sizeof(buf)-1);
                               if(_s>0)
                               {
                                   buf[_s]='\0';
                                   printf("client : %s",buf);
                                   write(fds[i],buf,_s);
                               }
                               else if(_s==0)
                               {
                                   printf("client quit..\n");
                                   close(fds[i]);
                                   fds[i]=-1;
                               }
                               else{
                               }
                           }//nomal socket
                           else{                                                                                                                                     
 
                           }
                       }
                   }
                   break;
         }
     }
     return 0;
 }

   下面是client端:

  

#include <stdio.h>
   #include <errno.h>
   #include <stdlib.h>
   #include <string.h>
   #include <netinet/in.h>
   #include <arpa/inet.h>
   #include <sys/types.h>
   #include <sys/socket.h>
   
  void usage(const char* arg)
  {
      printf("%s [remote_ip][remote_port\n",arg);
  }
  
  int main(int argc,char *argv[])
  {
      if(argc != 3)
      {
          usage(argv[0]);
          exit(0);
      }
                                                                                                                                                                      
      int port=atoi(argv[2]);
      char *ip=argv[1];
  
      int sock=socket(AF_INET,SOCK_STREAM,0);
      if(sock<0)
      {
          perror("socket");
          exit(1);
      }
  
      struct sockaddr_in remote;
      remote.sin_family=AF_INET;
      remote.sin_port=htons(port);
      remote.sin_addr.s_addr=inet_addr(ip);
  
      int ret=connect(sock,(struct sockaddr*)&remote,sizeof(remote));
  
      char buf[1024];                                                                                                                                                 
      while(1)
      {
          printf("please enter: ");
          fflush(stdout);
          ssize_t _s=read(0,buf,sizeof(buf)-1);
          buf[_s]='\0';
          write(sock,buf,sizeof(buf)-1);
  
          _s=read(sock,buf,sizeof(buf)-1);  //回顯
          if(_s>0)
          {
              buf[_s]='\0';
              printf("server-->client:%s",buf);
          }
      }
      return 0;
 }

上面的結(jié)果如下:

select---基于TCP客戶/服務(wù)端編程

    從上面的結(jié)果我我們可以看到,從client端發(fā)送的數(shù)據(jù),最后被server端hui顯給client端了,達到了我們的目的。

                                                                                                                                                                                                                                                                                                                                      


   














向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI