分类: LINUX
2011-05-12 20:32:35
Scorpio 2011-5-12 19:24:01
套接字的默认状态是阻塞的. 这意味着当发出一个不能立即完成的套接字调用后, 则进入睡眠, 等待相应操作完成. 可能阻塞的套接字调用有以下几种
1.输入操作, 包括read, readv, recv, recvfrom和recvmsg. 如果某个进程对一个阻塞的TCP套接字调用这些输入函数, 而该套接字的接收缓冲中没有数据可读, 该进程将进入睡眠, 直到有数据到达, 哪怕只有一个字节到达. 如果你想等到有一定长度的数据到达后才返回, 可以调用readn, 或者指定MSG_WAITALL标记. 当然对于UDP套接字而言需要有一个完整的数据报可读, 才会返回. 在阻塞情况下, 如果不满足读的要求, 则会返回EWOULDBLOCK/ EAGAIN /EINPROCESS, 这三个错误标记的宏, 在WIN32和LINUX下的定义可能不同, 具体的定义也不太一样, 可以参照
2.输出操作, 包括write, writev, send, sendto和sendmsg. 在调用这些函数的时候, 内核会从用户空间将数据拷贝到套接字的发送缓冲区, 如果套接口的发送空间没有空间了则会阻塞(默认是阻塞的情况), 如果是空间不足的情况, 则会拷贝部分数据, 返回已拷贝的数据长度. 这是TCP套接口的情况. 如果是UDP套接口, 虽然UDP套接口不存在真正意义上的套接口发送缓冲, 它会冠以UDP首部和IP首部沿着协议栈向下发传送. 但可能会因为别的情况而阻塞. 具体man一下sendto的错误标记的解释.
3.接受连接, 即accept. 如果调用了accept, 但尚未有新的连接到来, 那么会立即返回EWOULDBLOCK/ EAGAIN/ EINPROCESS错误.
4.发起连接, 即connect. 一般用于TCP的connect函数. 虽然UDP也可以使用connect但UDP并没有真正与peer(对端)建立连接, 只是使内核保存对端的IP和PORT. TCP建立连接时, 会有三路握手的过程, 具体过程如下:
上面的示意图清晰表明了三路握手的情况, 也就是说connect函数直到收到对于自己的SYN和ACK才会返回. 这意味着TCP每个connect总会阻塞其调用进程一个到peer的RTT时间. 当然如果客户端和服务器在同一主机上, 连接就会立即建立而返回.
以上4种情况说明了非阻塞IO发生的具体情况.
对于一个不能立即满足的非阻塞IO操作, SystemV会返回EAGAIN错误. 而Berkeley的实现则返回EWOULDBLOCK或EINPROCESS, 这些值的定义不同的系统可能不同, 具体查看一下
int ret = connect(sfd, &sAddr, addrLen);
if (ret < 0 && (errno != EAGAIN || errno != EWOULDBLOCK || errno != EINPROCESS))
{
return -1;
}
Scorpio 2011-5-12 20:28:33