UNIX TCP回射服务器/客户端(3):使用select模型的服务器

来源:互联网 发布:单片机与嵌入式的关系 编辑:程序博客网 时间:2024/05/17 02:16

    《Unix网络编程》这本书附带了许多短小精美的小程序,我在阅读此书的时候,将书上的代码按照自己的理解重写了一遍(大部分是抄书上的),加深一下自己的理解(纯看书太困了,呵呵)。此例子在Ubuntu10.04上测试通过。

    PS:程序里使用了包裹函数(首字母是大写的函数)和常量(所有字母都是大写的常量)的声明在my_unp.h文件中,定义在unp_base.c和unp_thread.c中,地址:http://blog.csdn.net/aaa20090987/article/details/8096701

    程序简介:这是一个运用select函数进行IO复用的服务器模型。它的原理是:当一个客户端连接上服务器时,服务器就将其连接的fd加入fd_set集合,等到这个连接准备好读或写的时候,就通知程序进行IO操作,与客户端进行数据通信。它是一个经典的IO复用模型,但当同时连接的客户端数量变大时,fd_set集合就变得很大,而每次要时行IO操作时,都要遍历一次这个集合,导致效率降低,所以它现在基本上被EPOLL模型代替了。


上代码:

#include "my_unp.h"    int main(void)  {      int i, maxi, maxfd, listenfd, connfd, sockfd;      pid_t childpid;        int nready, client[FD_SETSIZE];      ssize_t n;      fd_set rset, allset;      char buf[MAXLINE];      socklen_t clilen;      struct sockaddr_in cliaddr, servaddr;        //创建用于TCP协议的套接字        listenfd = Socket(AF_INET, SOCK_STREAM, 0);      memset(&servaddr, 0, sizeof(servaddr));      servaddr.sin_family = AF_INET;      servaddr.sin_addr.s_addr = htonl(INADDR_ANY);      servaddr.sin_port = htons(SERV_PORT);        //把socket和socket地址结构联系起来       Bind(listenfd, (SA*)&servaddr, sizeof(servaddr));        //开始监听LISTENQ端口       Listen(listenfd, LISTENQ);        maxfd = listenfd;      maxi = -1;      //初始化结构fd_set      for(i=0; i<FD_SETSIZE; i++)          client[i] = -1;      FD_ZERO(&allset);      FD_SET(listenfd, &allset);        while(1)      {          rset = allset;          //阻塞程序,等待某个事件的发生(见注解1)          //这里没有对Select函数设置超时,会导致程序有安全隐患          nready = Select(maxfd+1, &rset, NULL, NULL, NULL);            //如果监听的套接字变得可读,就建立一个新链接          if( FD_ISSET(listenfd, &rset) )          {              clilen = sizeof(cliaddr);                //调用accept函数并更新相关的数据结构              connfd = Accept(listenfd, (SA*)&cliaddr, &clilen);              //输出客户端的IP地址与端口号,还有处理(子)进程的PID                printf("connection from %s, port %d. process with clild %d\n",                      Inet_ntop(AF_INET, (void*)&cliaddr.sin_addr, buff, sizeof(buff)),                      ntohs(cliaddr.sin_port), childpid);                 for(i=0; i<FD_SETSIZE; i++)              {                  if( client[i] < 0 )                  {                      client[i] = connfd;                      break;                  }              }              //链接数已经达到上限了              if( FD_SETSIZE == i )                  error_quit("too many clients");                //增加新的描述符到集合中              FD_SET(connfd, &allset);              if( connfd > maxfd )                  maxfd = connfd;              if( i > maxi )                  maxi = i;                            //利用select的返回值来检查末就绪的描述符              if( --nready <= 0 )                  continue;          }            //扫描每个现在客户链接          for(i=0; i<=maxi; i++)          {              sockfd = client[i];              if( sockfd < 0 )                  continue;              if( FD_ISSET(sockfd, &rset) )              {                  n = Read(sockfd, buf, MAXLINE);                  //如果客户关闭了链接,就更新数据结构                  if( 0 == n )                  {                      Close(sockfd);                      FD_CLR(sockfd, &allset);                      client[i] = -1;                  }                  //否则,就回射一段字符给客户端                  else                      Writen(sockfd, buf, n);              }                if( --nready <= 0 )                  break;          }      }      return 0;  }  
与其配套的客户端在这里:http://blog.csdn.net/aaa20090987/article/details/8462262