阻塞读 真有硬伤?
本帖最后由 shouso888 于 2013-10-30 10:47:40 编辑
              
            捣捣stevens的《Unix 网络编程》后,留下的疑问:
客户端 阻塞于 read; 服务器主机崩溃,来不及做close 发出 FIN。
问:阻塞read 会一直阻塞?而且完全没有任何可能让它返回?因为对端主机崩溃时,只有写操作才能引起
ETIMEOUT,如果是read就会一直阻塞,TCP也不会知道对端已经崩溃。那阻塞read就是个Bug啊,不知道对端崩
溃还一直傻傻的等,用它就是埋下隐患?
------解决方案-------------------- 可以设置keepalive;也可以自己加心跳包;
------解决方案-------------------- 就像你把网线拔了, socket并不知道这个事情的发生, 如果你什么动作都没做过的话.
------解决方案-------------------- 网络不通,第一次可以写,第二次写立马弹出一个错误,而且如果拔网线,有时候会被发送信号,程序不处理,有可能被杀死。TCP对于长时间无影响的连接,我认为是会进入CLOSE_WAIT状态,很久很久以后就断开。
------解决方案-------------------- read是会阻塞,但是不会一直阻塞的!协议里会有一个超时的!
------解决方案-------------------- 用阻塞的应用一般都会考虑超时的,例如用SO_RCVTIMEO or alarm等
灵活性强了不是bug
------解决方案-------------------- 引用: Quote: 引用:  
阻塞读可以自己设置timeout, 
struct timeval timeout = {20, 0 }; 
int result = setsockopt(connfd, SOL_SOCKET, SO_RCVTIMEO, 
				(char *) &timeout, sizeof(timeout)); 
设置20秒超时,楼主可以去试试 
这个我知道,谢谢提醒。 
我现在最想知道:阻塞read 不设置超时, 是不是会 一直阻塞。 
man手册是这样说的
If  no messages are available at the socket, the receive calls wait for a message to arrive, unless the socket is non-blocking (see  fcntl(2)),
楼主不要纠结read是否一直阻塞的问题,阻塞的系统调用大都会被EINTR信号打断,当你阻塞的recv被意外返回时,你可以perror打出来看看
------解决方案-------------------- 引用: Quote: 引用:  
如果不设置超时,也没有设置SO_KEEPALIVE,会一直阻塞。 
 
我也是这么想的,但其他的版主怎么说“read是会阻塞,但是不会一直阻塞的!协议里会有一个超时的!” 
哎,不就结了。我就笃定它是永远阻塞吧。 
 
另外其实你说的SO_KEEPALIVE原理也是间歇性的与对端有交互数据,所以保持了链接(因为太久没动作的链 
接会被防火墙什么的关闭)。相当于向对端写东西,如果一直收不到对方的TCP确认就置为自己机器的SO_ERROR, 
进而使我的阻塞读返回 -1。
如果有防火墙或者是通过NAT连接出去的,我就不能确定了。
但是如果两台机器是直接连接的,并且也没有设置SO_KEEPALIVE的话,read永远不会返回的。
以下的测试代码可以说明这一点
 
/** 
 * @file    demo.c 
 * @brief    
 */ 
 
 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <arpa/inet.h> 
#include <netinet/in.h> 
#include <netdb.h> 
 
#include <unistd.h> 
#include <signal.h> 
 
#include <errno.h> 
#include <limits.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <time.h> 
 
int  init(void) 
{ 
    FILE *fp; 
    char path[PATH_MAX]; 
 
    daemon(0, 0); 
 
    signal(SIGCHLD, SIG_IGN); 
 
    close(STDOUT_FILENO); 
    close(STDERR_FILENO); 
    sprintf(path, "%s/log/demo.log", getenv("HOME")); 
    fp = fopen(path, "a+b"); 
    dup2(STDOUT_FILENO, STDERR_FILENO); 
 
    return 0; 
} 
 
int main(int argc, char *argv[]) 
{