套接字的默认状态是阻塞。阻塞的套接字调用可分为四种
(1),输入操作,包括read,readv,recv,recvfrom,recvmsg。
阻塞:
TCP:如果接收缓冲区没有数据读,则阻塞,直到数据到达。
UDP:如果接收缓冲区没有数据读,则阻塞,直到UDP数据报到达。
非阻塞:如果输入操作不能被满足(对于TCP套接字即至少有一个字节的数据可读,对于UDP套接字即有一个完整的数据报可读),相应调用将立即返回EWOULDBLOCK。
(2),输出操作,包括write,writev,send,sendto,sendmsg
阻塞:
TCP:如果发送缓冲区没有空间,则阻塞。有一些空间时,则返回不足计数
非阻塞:TCP:如果发送缓冲区没有空间,会立即返回一个EWOULDBLOCK错误。如
果有一些空间,返回值将是内核能够复制到该缓冲区中的字节数。这个
字节数也称为不足计数
输出操作对于UDP:没有发送缓冲区,不会因与TCP套接字一样的原因而阻塞,不过有可能会因其他原因而阻塞。
(3)接收外来连接,即用于accept函数
阻塞:阻塞在accept,直到有新的连接
非阻塞:没有新的连接来时,accept调用将立即返回一个EWOULDBLOCK
(4),发起外出连接,即用于TCP的connect函数。
阻塞:connect函数一直要等到客户收到对于自己的SYN的ACK为止才返回。所以TCP的每一个connect总会阻塞其调用的进程至少一个到服务器的RTT时间。
非阻塞:调用connect,并连接不能立即建立,那么连接的建立能照样发起,不过会返回一个EINPROGRESS错误。这个错误不同于上述三个情形,但客户端和服务端在同一主机,这些连接会立即建立。所以也要预备connect成功返回的情况。
STATUS connectWithTimeout(int sock, struct sockaddr*addrs, int adrsLen,struct timeval* tmOut)
{
int err = 0;
int len = sizeof(int);
int flag;
int ret;
fd_set set;
struct timeval mytm;
memset(&mytm, 0, sizeof(struct timeval));
if(tmOut!=NULL){
memcpy(&mytm, tmOut, sizeof(struct timeval));
}
flag = 1;
ioctl(sock,FIONBIO,&flag);
ret = connect(sock, addrs, adrsLen);//在非阻塞模式下 connect会返回1,下面的操作可以判断
connect是联通的,若可写则证明socket链接是成功的
if ( ret == -1 )
{
if ( EINPROGRESS == errno )
{
FD_ZERO(&set);
FD_SET(sock,&set);
if ( select(sock+1,NULL,&set,NULL,tmOut) > 0 )
{
getsockopt(sock,SOL_SOCKET,SO_ERROR,&err,(socklen_t*)&len);
if ( 0 == err )
ret = OK;
else
ret = ERROR;
}
else
{
ret = ERROR;
}
}
}
flag = 0;
ioctl(sock,FIONBIO,&flag);
if(tmOut!=NULL)
{
memcpy(tmOut, &mytm, sizeof(struct timeval));
}
return ret;
}