Chinaunix首页 | 论坛 | 博客
  • 博客访问: 17584
  • 博文数量: 9
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 57
  • 用 户 组: 普通用户
  • 注册时间: 2014-02-20 09:56
文章分类

全部博文(9)

文章存档

2014年(9)

我的朋友

分类: LINUX

2014-02-27 15:45:43

    select()的机制中提供一fd_set的数据结构,实际上是一long类型的数组, 每一个数组元素都能与一打开的文件句柄(不管是Socket句柄,还是其他 文件或命名管道或设备句柄)建立联系,建立联系的工作由程序员完成, 当调用select()时,由内核根据IO状态修改fd_set的内容,由此来通知执 行了select()的进程哪一Socket或文件可读,下面具体解释:
点击(此处)折叠或打开

  1. int select(nfds, readfds, writefds, exceptfds, timeout)
  2. int nfds;
  3. fd_set *readfds, *writefds, *exceptfds;
  4. struct timeval *timeout;
    ndfs:select监视的文件句柄数,视进程中打开的文件数而定,一般设为呢要监视各文件 中的最大文件号加一。 
    readfds:select监视的可读文件句柄集合。
    writefds: select监视的可写文件句柄集合。
    exceptfds:select监视的异常文件句柄集合。
    timeout:本次select()的超时结束时间。(见/usr/sys/select.h, 可精确至百万分之一秒!)
    struct timeval结构体在time.h中的定义为:

点击(此处)折叠或打开

  1. struct timeval
  2.     {
  3.     __time_t tv_sec; /* Seconds. */
  4.     __suseconds_t tv_usec; /* Microseconds. */
  5.     };
其中,tv_sec为Epoch到创建struct timeval时的秒数,tv_usec为微秒数,即秒后面的零头。    
当readfds或writefds中映象的文件可读或可写或超时,本次select() 就结束返回。程序员利用一组系统提供的宏在select()结束时便可判 断哪一文件可读或可写。对Socket编程特别有用的就是readfds。 几只相关的宏解释如下:

FD_ZERO(fd_set *fdset):清空fdset与所有文件句柄的联系。
FD_SET(int fd, fd_set *fdset):建立文件句柄fd与fdset的联系。
FD_CLR(int fd, fd_set *fdset)清除文件句柄fd与fdset的联系。
FD_ISSET(int fd, fdset *fdset):检查fdset联系的文件句柄fd是否
可读写,>0表示可读写。
(关于fd_set及相关宏的定义见/usr/include/sys/types.h)
这样,你的socket只需在有东东读的时候才读入,大致如下:

点击(此处)折叠或打开

  1. ...
  2. int sockfd;
  3. fd_set fdR;
  4. struct timeval timeout = ..;
  5. ...
  6. for(;;) {
  7. FD_ZERO(&fdR);
  8. FD_SET(sockfd, &fdR);
  9. switch (select(sockfd + 1, &fdR, NULL, &timeout)) {
  10. case -1:
  11. error handled by u;
  12. case 0:
  13. timeout hanled by u;
  14. default:
  15. if (FD_ISSET(sockfd)) {
  16. now u read or recv something;
  17. /* if sockfd is father and
  18. server socket, u can now
  19. accept() */
  20. }
  21. }
  22. }

    所以一个FD_ISSET(sockfd)就相当通知了sockfd可读。至于struct timeval在此的功能,请man select。不同的timeval设置 使使select()表现出超时结束、无超时阻塞和轮询三种特性。由于 timeval可精确至百万分之一秒,所以Windows的SetTimer()根本不算 什么。你可以用select()做一个超级时钟。

FD_ACCEPT的实现?依然如上,因为客户方socket请求连接时,会发送连接请求报文,此时select()当然会结束,FD_ISSET(sockfd)当然大于零,因为有报文可读嘛!至于这方面的应用,主要在于服务方的父Socket,你若不喜欢主动accept(),可改为如上机制来accept()。


充关于select在异步(非阻塞)connect中的应用,刚开始搞socket编程的时候,我一直都用阻塞式的connect,非阻塞connect的问题是由于当时搞proxy scan 而提出的, 通过在网上与网友们的交流及查找相关FAQ,总算知道了怎么解决这一问题.同样 用select可以很好地解决这一问题.大致过程是这样的:

1.将打开的socket设为非阻塞的,可以用fcntl(socket, F_SETFL, O_NDELAY)完成(有的系统用FNEDLAY也可).

2.发connect调用,这时返回-1,但是errno被设为EINPROGRESS,意即connect仍旧在进行还没有完成.

3.将打开的socket设进被监视的可写(注意不是可读)文件集合用select进行监视, 如果可写,用getsockopt(socket, SOL_SOCKET, SO_ERROR, &error, sizeof(int));
来得到error的值,如果为零,则connect成功.

Linux下用select查询串口数据

Linux下直接用read读串口可能会造成堵塞,或数据读出错误。然而用select先查询com口,再用read去读就可以避免,并且当com口延时时,程序可以退出,这样就不至于由于com口堵塞,程序就死了。我的代码如下:


点击(此处)折叠或打开

  1. bool ReadDevice( int hComm, unsigned long uLen, char* pData )

  2. {

  3. int nread = 0;

  4. char inbuf[uLen];

  5. char buff[uLen];

  6. memset( inbuff, '\0', uLen );

  7. memset( buff, '\0', uLen );

  8. fd_set readset;
  9. struct timeval tv;
  10. int MaxFd = 0;

  11. int c = 0;
  12. int z;
  13. do
  14. {
  15. FD_ZERO( &readset );
  16. if( hComm >= 0 )
  17. FD_SET( hComm, &readset );
  18. MaxFd = hComm + 1;
  19. tv.tv_sec = 0;
  20. tv.tv_usec = 500000;
  21. do
  22. {
  23. z = select( MaxFd, &readset, 0, 0, &tv);
  24. }while( z==-1 && errno==EINTR );
  25. if( z == -1 )
  26. printf("select(2)\n");
  27. if( z == 0 )
  28. {
  29. hComm = -1;
  30. }

  31. if( hComm>=0 && FD_ISSET(hComm, &readset) )
  32. {
  33. z = read( hComm, buff, uLen - c );
  34. c += z;
  35. if( z == -1 )
  36. {
  37. hComm = -1;
  38. }
  39. if( z > 0 )
  40. {

  41. buff[ z + 1 ] = '\0';
  42. strcat( inbuff, buff );
  43. memset( buff, 0x00, uLen );
  44. }
  45. else
  46. {
  47. hComm = -1;
  48. }
  49. }
  50. }while( hComm >= 0 );

  51. memcpy( pData, inbuff, c );

  52. return true;

  53. }


阅读(188) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~