kenlistian

厚积薄发. 勤为桨,思为帆

   :: 首页 :: 新随笔 ::  :: 聚合  :: 管理 ::
  73 随笔 :: 4 文章 :: 22 评论 :: 0 Trackbacks

   在网络通讯中,socket处于阻塞模式运行时,其存在着超时处理。以下总结下在那些阻塞函数的处理方法。

这里摘抄一段描述阻塞函数的描述,非常到位。

所谓阻塞函数,是指其完成指定的任务之前不允许程序调用另一个函数,在Windows下还会阻塞本线程消息的发送。

所谓非阻塞函数,是指操作启动之后,如果可以立即得到结果就返回结果,否则返回表示结果需要等待的错误信息,不等待任务完成函数就返回。

首先,异步函数是非阻塞函数;

其次,获取远地信息的数据库函数是阻塞函数(因此,WinSock提供了其异步版本);

在Berkeley socket函数部分中,不涉及网络I/O、本地端工作的函数是非阻塞函数;

在Berkeley socket函数部分中,网络I/O的函数是可阻塞函数,也就是它们可以阻塞执行,也可以不阻塞执行。这些函数都使用了一个socket,如果它们使用的socket是阻塞的,则这些函数是阻塞函数;如果它们使用的socket是非阻塞的,则这些函数是非阻塞函数

    其实说明阻塞还是非阻塞也是我们可以设置相关。这里主要讲解下处于阻塞模式下的超时处理。

1.在我们直接调用socket创建时,如果不进行特意声明的话,创建的socket都是阻塞的。这样当我们调用accept,recv时,将有可能“block”,如果想设置为非阻塞,则方法有调用fcntl,select,WSAAsynSelect 来改变socket的阻塞

hsocket = socket(AF_INET, SOCK_STREAM, 0)

fcntl(hsocket, F_SETFL, 0_NONBLOCK);

 注意:

    其中fcntl是Unix系统环境中使用的,使用ioctl()函数和fcntl()函数实现对套接字的控制,而在Windows系统中则应使用ioctlsocket()函数。

 Ioctl和fcntl的区别是:

  ioctl   -   control   device  
  ioctl()   performs   a   variety   of   control   functions     on     devices  
            and   STREAMS.     For   non-STREAMS   files,   the   functions   performed  
            by   this   call   are   device-specific   control   functions.     request  
            and     an   optional   third   argument   with   varying   type   are   passed  
            to   the   file   designated   by   fildes   and   are   interpreted   by     the  
            device   driver.  
    The   fcntl()   function   provides   control   of   open   file   descriptors.   It   is   similar   to   ioctl().

   ( 这些带着unix的体味的函数,看着就头大。这个我是摘自某个文档,设置超时应该采用ioctlsocket。)

1.调用MFC的CAsyncSocket和CSocket类

  MFC提供了两个类CAsyncSocket和CSocket来封装WinSock API,

  CAsyncSocket在较低层次上封装了WinSock API,缺省情况下,使用该类创建的socket是非阻塞的socket,所有操作都会立即返回,如果没有得到结果,返回WSAEWOULDBLOCK,表示是一个阻塞操作。

  CSocket建立在CAsyncSocket的基础上,是CAsyncSocket的派生类。也就是缺省情况下使用该类创建的socket是非阻塞的socket,但是CSocket的网络I/O是阻塞的,它在完成任务之后才返回。CSocket的阻塞不是建立在“阻塞”socket的基础上,而是在“非阻塞”socket上实现的阻塞操作,在阻塞期间,CSocket实现了本线程的消息循环,因此,虽然是阻塞操作,但是并不影响消息循环,即用户仍然可以和程序交互。(即程序不会freeze)。

  其中设置超时函数如下:

CAsyncSocket::SetSockOpt(   int   nOptionName,   const   void*   lpOptionValue,   int   nOptionLen,   int   nLevel   =   SOL_SOCKET   );  
  nOptionName用SO_SNDTIMEO   or   SO_RCVTIMEO。  

2.采用select处理超时

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

其中在windows中,其nfds可以可以设置为0,没有实际意义。

fd_set  fdR;
struct  timeval timeout = ..; //设置超时时间
...
for(;;) {
        FD_ZERO(&fdR);
        FD_SET(sockfd, &fdR);
        switch (select(sockfd + 1, &fdR, NULL, &timeout)) {
                case -1:
                    //错误,需要关闭端口。。。
                case 0:

                   //  timeout 处理

                default:
                        if (FD_ISSET(sockfd)) { 

                            //或读写操作或Accept()操作,按你设置处理。
                        }
        }
}

注意的是:

     由于Windows Sockets 某些函数在接口上虽然与Unix Sockets一致,但是它们的内部实现却不一样,例如,在函数select()的参数中,Unix Sockets实现套接字集合使用的是位掩码,但在Windows Sockets中却是使用一个SOCKET的数组。虽然套接字的集合仍由fd_set类型表示,但在Unix Sockets 源文件中直接修改fd_set结构的代码在Windows Sockets环境下将不能正常工作。故fd_set在微软中则采用FD_XXX宏处理。

3.采用WSAsyncSelect() 

   这个是消息事件模式来处理的,通过其中参数句柄返回一个消息,在自定义消息中处理。

 

4.采用setsockopt设置超时

int PASCAL FAR setsockopt( SOCKET s, int level, int optname,
const char FAR* optval, int optlen);

s:标识一个套接口的描述字。
level:选项定义的层次;目前仅支持SOL_SOCKET和IPPROTO_TCP层次。
optname:需设置的选项。
optval:指针,指向存放选项值的缓冲区。
optlen:optval缓冲区的长度。

     setsockopt()函数用于任意类型、任意状态套接口的设置选项值。尽管在不同协议层上存在选项,但本函数仅定义了最高的“套接口”层次上的选项。选项影响套接口的操作,诸如加急数据是否在普通数据流中接收,广播数据是否可以从套接口发送等等。
  有两种套接口的选项:

      一种是布尔型选项,允许或禁止一种特性;

      一种是整形或结构选项。允许一个布尔型选项,则将optval指向非零整形数;禁止一个选项optval指向一个等于零的整形数。

        对于布尔型选项,optlen应等于sizeof(int);对其他选项,optval指向包含所需选项的整形数或结构,而optlen则为整形数或结构的长度。

      SO_LINGER选项用于控制下述情况的行动:套接口上有排队的待发送数据,且closesocket()调用已执行。参见closesocket()函数中关于SO_LINGER选项对closesocket()语义的影响。应用程序通过创建一个linger结构来设置相应的操作特性。

      这个setsockopt的水也太深了,对于一般运用大致运用就行,主要是其中参数太多,只需要了解几个就可以了,至于想了解更多,则读msdn吧,以及相关的文档吧,不过感觉这些东西纯粹就是个技术指标细节,不需要动脑筋却需要了解的一个方面,说白了是这些都是技术活而不是脑力活。

常见的命令:

//确定套接字自动读入的数据量

#define FIONREAD _IOR(''''f'''', 127, u_long) /* get # bytes to read */

//允许或禁止套接字的非阻塞模式,允许为非0,禁止为0

#define FIONBIO _IOW(''''f'''', 126, u_long) /* set/clear non-blocking i/o */

//确定是否所有带外数据都已被读入

#define SIOCATMARK _IOR(''''s'''', 7, u_long) /* at oob mark? */

设置接受超时

posted on 2007-12-27 18:04 kenlistian 阅读(7572) 评论(0)  编辑 收藏 引用

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理