北飞

非一般的感觉
随笔 - 9, 文章 - 3, 评论 - 0, 引用 - 0
数据加载中……

网络编程学习系列四 select模式

 

     在很长的时间内,相比于Windows平台众多的选择,select模式都是linux平台下网络通信的不多选择之一(在epoll未出生之前)。这种模式在面对大量短连接的时候,比OCOT有高的效率,可以避免频繁的上下文切换。

     select系统调用是用来让我们的程序监视多个文件描述符(file descriptor)的状态变化的。程序会停在select这里等待,直到遇到以下情况之一select会退出:

     (1)     监控的句柄至少有一个发生变化,返回值大于1。

     (2)     等待时间已到,返回值等于0。

     (3)     收到信号退出,返回值小于0。

     select操作的内部会在指定的时间内,不断的检测受控集合中的套接字是否可读,可写或有错误,在select返回时只保留发生状态变化的套接字。通过判断select的返回值,就可以了解该调用是否有文件描述符可读或是select调用退出等信息。

     要想让程序运行,光有一个select的调用不是不够的,我们还需要一系列的配套数据结构和算法来帮助将套接字归类,判断是否有指定的套接字等等,结构体fd_set和宏FD_ZERO,FD_CLR,FD_SET,FD_ISSET等就是我们所需要的。通过定义一个fd_set的对象,例如可读集合,有相同需求的套接字都可以放置到这个对象中,可以通过FD_SET这个宏来完成,当然在这之间需要利用FD_ZERO将这个对象初始化。等完成了以上操作后,便会调用select查询是否有套接字可读。等待select完成后,如果有套接字可读,就可以利用FD_ISSET判断是否为监听套接字,转而执行accept操作。如果一个套接字已经关闭,就可以利用FD_CLR将其从fd_set对象中剔除。

    服务器端源代码:
 

  1#include <iostream>
  2#include <sys/socket.h>
  3#include <sys/types.h>
  4#include <arpa/inet.h>
  5#include <sys/select.h>
  6#include <sys/ioctl.h>
  7using namespace std;
  8
  9int main()
 10{
 11
 12   int server_sock = socket(AF_INET,SOCK_STREAM,0);
 13   if(server_sock < 0)
 14   {
 15      cout << "create socket error" << endl;
 16      return -1;
 17   }

 18
 19   struct sockaddr_in server_addr;
 20   memset(&server_addr,0,sizeof(server_addr));
 21   server_addr.sin_family = AF_INET;
 22   server_addr.sin_port = htons(5555);
 23   server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
 24
 25   if(bind(server_sock,(struct sockaddr*)&server_addr,sizeof(server_addr)) < 0)
 26   {
 27      cout << "bind socket error" << endl;
 28      close(server_sock);
 29      return -1;
 30   }

 31
 32   listen(server_sock,10);
 33
 34   fd_set read_set;
 35   fd_set test_set;
 36   FD_ZERO(&read_set);
 37   FD_SET(server_sock,&read_set);
 38
 39   struct timeval tm;
 40   tm.tv_sec = 5;
 41   tm.tv_usec = 500;
 42
 43
 44   while(true)
 45   {
 46       int nread = 0;
 47       test_set = read_set;
 48       int ret = select(FD_SETSIZE,&test_set,NULL,NULL,&tm);
 49
 50       if(ret < 0)
 51       {
 52           cout << "select error" << endl;
 53           close(server_sock);
 54           return -1;
 55       }

 56       else if(ret == 0)
 57       {
 58           //cout << "waitout" << endl;
 59           continue;
 60       }

 61       else
 62       {
 63           for(int fd=0; fd<FD_SETSIZE; ++fd)
 64           {
 65               if(FD_ISSET(fd,&test_set))
 66               {
 67                   //如果有新的连接到达
 68                   if(fd == server_sock)
 69                   {
 70                       int sock = accept(server_sock,NULL,NULL);
 71                       FD_SET(sock,&read_set);
 72                   }

 73                   else
 74                   {
 75                       ioctl(fd,FIONREAD,&nread);
 76
 77                       if(nread == 0//客户端已经关闭
 78                       {
 79                          close(fd);
 80                          FD_CLR(fd,&read_set);
 81                          cout << "client has removed " << fd << endl;
 82                       }

 83                       else
 84                       {
 85                          char buf[128];
 86                          recv(fd,buf,128,0);
 87                          cout << buf << endl;
 88                          send(fd,buf,strlen(buf)+1,0);
 89                       }

 90                   }

 91
 92               }

 93
 94           }

 95
 96       }

 97
 98   }

 99}

100

posted on 2010-08-06 17:57 北飞 阅读(1094) 评论(0)  编辑 收藏 引用


只有注册用户登录后才能发表评论。
【推荐】超50万行VC++源码: 大型组态工控、电力仿真CAD与GIS源码库
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理