您当前的位置: 首页 >  服务器

phymat.nico

暂无认证

  • 5浏览

    0关注

    1967博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

基于select模型的TCP服务器

phymat.nico 发布时间:2018-01-15 20:27:40 ,浏览量:5

之前的一篇博文是基于TCP的服务器和客户机程序,今天在这我要实现一个基于select模型的TCP服务器(仅实现了服务器)。

socket套接字编程提供了很多模型来使服务器高效的接受客户端的请求,select就是其中之一。

了解select模型我们先来看一下的代码:

int iResult = recv(s, buffer,1024); 这 是用来接收数据的,在默认的阻塞模式下的套接字里,recv会阻塞在那里,直到套接字连接上有数据可读,把数据读到buffer里后recv函数才会返 回,不然就会一直阻塞在那里。在单线程的程序里出现这种情况会导致主线程(单线程程序里只有一个默认的主线程)被阻塞,这样整个程序被锁死在这里,如果永 远没数据发送过来,那么程序就会被永远锁死。这个问题可以用多线程解决,但是在有多个套接字连接的情况下,这不是一个好的选择,扩展性很差。Select 模型就是为了解决这个问题而出现的。

select函数:

[cpp] view plain copy
  1. int select(int nfds, fd_set  *readfds, fd_set  *writefds, fd_set *exceptfds, const struct timeval *timeout );  

参数nfds是需要监视的最⼤的⽂件描述符值+1; rdset,wrset,exset分别对应于需要检测的可读⽂件描述符的集合,可写⽂件描述符的集 合及异 常⽂件描述符的集合。 struct timeval结构⽤于描述⼀段时间长度,如果在这个时间内,需要监视的描述符没有事件

发⽣则函数返回,返回值为0。

[cpp] view plain copy
  1. timeval结构体:  
  2. struct  timeval {  
  3.         long    tv_sec;        //秒  
  4.         long    tv_usec;     //毫秒  
  5. };  

select返回fd_set中可用的套接字个数。

下⾯的宏提供了处理这三种描述词组的⽅式: FD_CLR(inr fd,fd_set* set);⽤来清除描述词组set中相关fd 的位 FD_ISSET(int fd,fd_set *set);⽤来测试描述词组set中相关fd 的位是否为真 FD_SET(int fd,fd_set*set);⽤来设置描述词组set中相关fd的位 FD_ZERO(fd_set *set);⽤来清除描述词组set的全部位

函数返回值: 执⾏成功则返回⽂件描述词状态已改变的个数。 如果返回0代表在描述词状态改变前已超过timeout时间,没有返回; 当有错误发⽣时则返回-1, 错误原因存于errno,此时参数readfds,writefds,exceptfds和timeout的值变成不可预测。错误值可能为: EBADF ⽂件描述词为⽆效的或该⽂件已关闭。 EINTR 此调⽤被信号所中断。 EINVAL 参数n 为负值。 ENOMEM 核⼼内存不⾜。

根据以上的只是前提,我们可以得到select的特点:

select模型的特点:

  (1)可监控的⽂件描述符个数取决与sizeof(fd_set)的值。我这边服务 器上sizeof(fd_set)=

512,每bit表⽰⼀个⽂件描述符,则我服务器上⽀持的最⼤⽂件描述符是512*8=4096。据说

可调,另有说虽 然可调,但调整上限受于编译内核时的变量值。本⼈对调整fd_set的⼤⼩不

http://www.cppblog.com /CppExplore/archive/2008/03/21/45061.html 太感兴趣,参考中的模

型2(1)可以有效突破select可监控的⽂件描述符上 限。

  (2)将fd加⼊select监控集的同时,还要再使⽤⼀个数据结构array保存放到select监控集

中的fd,⼀是⽤于再select 返回后,array作为源数据和fd_set进⾏FD_ISSET判断。⼆是select

返回后会把以前加⼊的但并⽆事件发⽣的fd清空,则每次开始 select前都要重新从array取得fd

逐⼀加⼊(FD_ZERO最先),扫描array的同时取得fd最⼤值maxfd,⽤于select的第⼀个 参

数。

  (3)可见select模型必须在select前循环array(加fd,取maxfd),select返回后循环array

(FD_ISSET判断是否有时间发⽣)。

以下是select模型的工作过程:

1:用FD_ZERO宏来初始化我们感兴趣的fd_set。

也就是select函数的第二三四个参数。

2:用FD_SET宏来将套接字句柄分配给相应的fd_set。

如果想要检查一个套接字是否有数据需要接收,可以用FD_SET宏把套接接字句柄加入可读性检查队列中

3:调用select函数。

如果该套接字没有数据需要接收,select函数会把该套接字从可读性检查队列中删除掉,

4:用FD_ISSET对套接字句柄进行检查。

如果我们所关注的那个套接字句柄仍然在开始分配的那个fd_set里,那么说明马上可以进行相应的IO操 作。比如一个分配给select第一个参数的套接字句柄在select返回后仍然在select第一个参数的fd_set里,那么说明当前数据已经来了, 马上可以读取成功而不会被阻塞。

基于select模型的TCP服务器的实现:

[cpp] view plain copy
  1. #include  
  2. #include  
  3. #include  
  4. #include  
  5. #include  
  6. #include  
  7. #include  
  8.   
  9. #define _MAX_SIZE_ 10  
  10. int fd_arr[_MAX_SIZE_];  
  11. int max_fd=0;  
  12. static void Useage(const char* proc)  
  13. {  
  14.     printf("Useage:%s,[ip][port]");  
  15.     exit(1);  
  16. }  
  17. static int add_fd_arr(int fd)  
  18. {  
  19.     //fd add to fd_arr  
  20.     int i=0;  
  21.     for(;i
关注
打赏
1659628745
查看更多评论
立即登录/注册

微信扫码登录

0.1195s