原文:
将一个socket 设置成阻塞模式和非阻塞模式,使用fcntl方法,即:
设置成非阻塞模式: 先用fcntl的F_GETFL获取flags,用F_SETFL设置flags|O_NONBLOCK; 即:
flags = fcntl(sockfd, F_GETFL, 0); //获取文件的flags值。 fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); //设置成非阻塞模式; 同时在接收和发送数据时,需要使用MSG_DONTWAIT标志 即: 在recv,recvfrom和send,sendto数据时,将flag设置为MSG_DONTWAIT。 设置成阻塞模式:
先用fcntl的F_GETFL获取flags,用F_SETFL设置flags&~O_NONBLOCK; 即: flags = fcntl(sockfd,F_GETFL,0); //获取文件的flags值。 fcntl(sockfd,F_SETFL,flags&~O_NONBLOCK); //设置成阻塞模式; 同时在接收和发送数据时,需要使用阻塞标志 即: 在recv,recvfrom和send,sendto数据时,将flag设置为0,默认是阻塞。
在将socket设置成非阻塞模式后,每次的对于sockfd 的操作都是非阻塞的; 非阻塞模式下: connect =0 当返回0时,表示立即创建了socket链接, <0 当返回-1时,需要判断errno是否是EINPROGRESS(表示当前进程正在处理),否则失败。 例如:下面会有select或epoll监听fd是否建立链接, select监听connect是否成功的例子,注意getsockopt验证,因为三次握手的第三个ACK有可能会丢失,但是客户端认为链接已经建立: int ret = ::connect(_socket_fd, add.addr(), add.length());
int retval = select(_socket_fd + 1, NULL, &set, NULL, &timeo); //事件监听 //将检测到_socket_fd读事件或写时间,并不能说明connect成功 注意:这里主要是想强调当epoll或select监听到sockfd上有EPOLL_IN或EPOLL_OUT时,即读写事件时,并不能说明链接已经建立,如上面的代码。 /* int error = 0; //说明链接建立失败,close(fd); else { //说明链接建立成功。即可以向fd上写数据。 } */ recv 和 recvfrom =0 当返回值为0时,表示对端已经关闭了这个链接,我们应该自己关闭这个链接,即close(sockfd)。另外因为异步操作会用select或epoll做事件触发,所以: 1、如果使用select,应该使用FD_CLR(sockfd,fd_set)将sockfd清除掉,不再监听。 2、如果使用epoll,系统会自己将sockfd清除掉,不再进行监听。 >0 当返回值大于0 且 小于sizeof(buffer)时,表示数据肯定读完。(如果等于sizeof(buffer),可能有数据还没读,应该继续读,不可能有大于) <0 当返回值小于0,即等于-1时,分情况判断: 1、如果 errno 为 EAGAINE 或 EWOULDBLOCK 表示暂时无数据可读,可以继续读,或者等待epoll或select的后续通知。(EAGAINE,EWOULDBLOCK产生的 原因:可能是多进程读同一个sockfd,可能一个进程读到数据,其他进程就读取不到数据(类似惊群效应),当然 单个进程也可能出现这种情况。对于这种错误,不需用close(sockfd)。可以等待select或epoll的下一次触发, 继续读。) 2、如果 errno 为 EINTR 表示被中断了,可以继续读,或者等待epoll或select后续的通知。 否则,真的是读取数据失败。(此时应该close(sockfd))
send和sendto 返回值是实际发送的字符数,因为我们知道要发送的总长度,所以,如果没有发送完,我们可以继续发送。 <0 当返回值为 -1 时, 我们需要判断 errno: 1、如果errno为 EAGAINE 或 EWOULDBLOCK ,表示当前缓冲区写满,可以继续写, 或者等待epoll或select的后续通知,一旦有缓冲区,就会触发写操作,这个也是经常利用的一个特性。 2、如果errno为EINTR ,表示被中断了,可以继续写,或者等待epoll或select的后续通知。 否则真的出错了,即errno不为EAGAINE或EWOULDBLOCK或EINTR,此时应该close(sockfd) >=0 >=0且不等于要求发送的长度,应该继续send,如果等于要求发送的长度,发送完毕。 |
|