select---基于TCP客户/服务端编程-创新互联

我们先来说说最重要的函数select这个函数,它的原型如下:

成都创新互联从2013年开始,是专业互联网技术服务公司,拥有项目成都网站设计、成都网站建设网站策划,项目实施与项目整合能力。我们以让每一个梦想脱颖而出为使命,1280元朝阳做网站,已为上家服务,为朝阳各地企业和个人服务,联系电话:18982081108

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

      select函数可以执行I/O多路转接。传给select的参数告诉内核:

               1.我们所关心的描述符

               2.对于每个描述符我们所关心的条件(是否想从一个给定的描述符读,写,还是异常条件)

               3.愿意等待的时间(永远等待,等待一个给定的时间或者根本不等待)

       从select返回时,内核告诉我们:

                1.已准备好的描述符的总数量

               2.对于读,写,异常这3个条件中的每一个,哪些描述符已经准备好

       有了上面的返回信息,我们就可以调用相应的I/O函数(read或者write),并且确知该函数不会被阻塞。

我们再来说说select这个函数的返回值和相应的参数:

      正常返回准备就绪的描述符数目,若超时,返回0;出错时返回-1.

       参数:

                我们先来说最后一个参数,它是一个指向结构体的指针,这个结构体如下:

                struct timeval

               {

                    long tv_sec;

                   long tv_usec;

               }

               这个参数指明了愿意等待的时间长度,单位为秒和微妙。当它的值为NULL,说明要永远等待。如果捕捉到一个信号则中断无限等待。当所指定的描述符的一个已经准备好或捕捉到一个信号则返回。当这个结构体的两个成员的值均不为0时,则是我们设定的要指定的秒数和微秒数。当指定的一个描述符之一已经准备好,或者当指定的时间已经超过时立即返回。在超时后还没有一个描述符准备好,返回0。

                中间的 readfds,writefds,exceptfds这三个参数是指向描述符集的指针。这3个描述符集说明了我们关心的可读,可写或处于异常条件的描述符集合。每个描述符集存储在一个fd_set数据类型中。这个数据类型为每一个描述符保持一位。在这里我们简单的认为是一个很大的字节数组。

                第一个参数 nfds 的意思是“大文件描述符编号值加1”。因为描述符从0 开始,所以要在大描述符编号上加1.意思是我们我们在所有描述符集当中找出大的一个,然后加1就行了。这里我们也可以将第一个参数设置为FD_SETSIZE,它是一个常量值,它指定大描述符数(一般是1024),一般而言,此值有点大。通过我们指定大的描述符数,内核就只需在此范围内寻找打开的位,大大节省了时间。

我们再来说说fd_set这个数据类型:

      这种数据类型的变量经常用到以下函数:

             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);

         这些接口可实现为宏或者函数。调用FD_ZERO将一个fd_set变量的所有位设置为0.调用FD_SET设置描述符集中的一位。调用FD_CLR可以清除一位。我们可以调用FD_ISSET测试描述符集中的一个指定位是否已经打开。我们要注意,在我们声明了一个描述符集之后,必须调用FD_ZERO函数将这个描述符集置为0,然后再在其中设置我们关心的各个描述符的位。

上面说了那么多,我们来用用这几个函数吧,在下面的程序中,我们用刚说的那几个函数写了一个简单的客户/服务器间通信模型,客户向服务端发送数据,服务端收到后再回显给客户端,我们来看看具体代码:

server端:

   #include 
   #include 
   #include 
   #include 
   #include 
   #include 
   #include 
   #include 
   
  #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( ;i0)
              {
                  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(;i0&&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 
   #include 
   #include 
   #include 
   #include 
   #include 
   #include 
   #include 
   
  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;
 }

上面的结果如下:

select---基于TCP客户/服务端编程

    从上面的结果我我们可以看到,从client端发送的数据,最后被server端hui显给client端了,达到了我们的目的。

另外有需要云服务器可以了解下创新互联scvps.cn,海内外云服务器15元起步,三天无理由+7*72小时售后在线,公司持有idc许可证,提供“云服务器、裸金属服务器、高防服务器、香港服务器、美国服务器、虚拟主机、免备案服务器”等云主机租用服务以及企业上云的综合解决方案,具有“安全稳定、简单易用、服务可用性高、性价比高”等特点与优势,专为企业上云打造定制,能够满足用户丰富、多元化的应用场景需求。


本文名称:select---基于TCP客户/服务端编程-创新互联
分享路径:http://myzitong.com/article/hdjdg.html