套接字的默认行为为:“一直到阻塞到请求动作完成为止”。比如recv函数或者accept函数在收到数据之前会默认一直处于阻塞,含有阻塞函数的进程会在阻塞期间被操作系统挂起。
有三种方法实现非阻塞套接字、异步I/O和超时。
非阻塞套接字
实现非阻塞套接字的方法就是更改套接字的行为,使得所有的调用都是非阻塞的。用fcntl函数就能更改套接字行为
int fcntl
(
int socket
,
int command
, ...
)
;
顺便说一句:fcntl是file control的缩写。
对于设置为非阻塞的套接字,每次调用的时候,要么立即完成IO操作,要么立刻返回失败。返回失败的时候系统会把errno的值设置为EWOULDBLOCK,connect函数除外,它会将errno设置为EINPROGRESS。
由于在网络编程中,我们并不知道何时调用会成功,只能周期性地调用recv或者read函数,一次又一次得去实验它,我们把这种方式称为“轮询”。
异步I/O
异步I/O的原理就是当套接字上发生摸个与IO相关的事件之后把SIGIO信号发送给该进程。实例代码如下:
...
int sock ;
int main ( int argc , char *argv [ ] )
{
...
signal (SIGIO , sig_handler ) ;
...
}
void sig_handler ( int signalType )
{
...
recv (sock , buffer , length , flags ) ;
...
}
int sock ;
int main ( int argc , char *argv [ ] )
{
...
signal (SIGIO , sig_handler ) ;
...
}
void sig_handler ( int signalType )
{
...
recv (sock , buffer , length , flags ) ;
...
}
设置超时
unsigned int alarm(unsigned int secs);
Linux中的alarm()函数会设置一个计时器,经过secs秒之后,进程会收到SIGALRM信号。调用alarm()也会返回之前设置的报警剩余时间。
...
int main ( int argc , char *argv [ ] )
{
signal (SIGALRM , func ) ;
...
alarm (TIME_OUT_VALUE ) ;
while (times < MAX_TIMES )
{
sock = recv (... ) ;
...
}
alarm ( 0 ) ; //取消报警计时器
...
}
void func ( int signalType )
{
times += 1 ;
}
int main ( int argc , char *argv [ ] )
{
signal (SIGALRM , func ) ;
...
alarm (TIME_OUT_VALUE ) ;
while (times < MAX_TIMES )
{
sock = recv (... ) ;
...
}
alarm ( 0 ) ; //取消报警计时器
...
}
void func ( int signalType )
{
times += 1 ;
}
记得把sock设置为非阻塞
如果有什么问题,请在下面留言。
原文链接:http://www.yucoat.com/internet/linux_unblock.html