阻塞读 真有硬伤?
本帖最后由 shouso888 于 2013-10-30 10:47:40 编辑 捣捣stevens的《Unix 网络编程》后,留下的疑问:
客户端 阻塞于 read; 服务器主机崩溃,来不及做close 发出 FIN。
问:阻塞read 会一直阻塞?而且完全没有任何可能让它返回?因为对端主机崩溃时,只有写操作才能引起
ETIMEOUT,如果是read就会一直阻塞,TCP也不会知道对端已经崩溃。那阻塞read就是个Bug啊,不知道对端崩
溃还一直傻傻的等,用它就是埋下隐患?
网络编程 unix tcp Linux
[解决办法]
可以设置keepalive;也可以自己加心跳包;
[解决办法]
就像你把网线拔了, socket并不知道这个事情的发生, 如果你什么动作都没做过的话.
[解决办法]
网络不通,第一次可以写,第二次写立马弹出一个错误,而且如果拔网线,有时候会被发送信号,程序不处理,有可能被杀死。TCP对于长时间无影响的连接,我认为是会进入CLOSE_WAIT状态,很久很久以后就断开。
[解决办法]
read是会阻塞,但是不会一直阻塞的!协议里会有一个超时的!
[解决办法]
用阻塞的应用一般都会考虑超时的,例如用SO_RCVTIMEO or alarm等
灵活性强了不是bug
[解决办法]
如果不设置超时,也没有设置SO_KEEPALIVE,会一直阻塞。
我也是这么想的,但其他的版主怎么说“read是会阻塞,但是不会一直阻塞的!协议里会有一个超时的!”
哎,不就结了。我就笃定它是永远阻塞吧。
另外其实你说的SO_KEEPALIVE原理也是间歇性的与对端有交互数据,所以保持了链接(因为太久没动作的链
接会被防火墙什么的关闭)。相当于向对端写东西,如果一直收不到对方的TCP确认就置为自己机器的SO_ERROR,
进而使我的阻塞读返回 -1。
/**
* @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[])
{
int servfd, clntfd;
struct sockaddr_in servaddr, clntaddr;
socklen_t servlen, clntlen;
int port;
int keepalive;
int reuseaddr;
socklen_t optlen;
int ret;
pid_t pid;
ssize_t len;
char buf[1024];
time_t tick;
keepalive = 0;
port = 8888;
if (argc > 1) {
keepalive = atoi(argv[1]);
if (keepalive)
port++;
}
init();
servfd = socket(PF_INET, SOCK_STREAM, 0);
optlen = sizeof(reuseaddr);
ret = setsockopt(servfd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, optlen);
if (ret < 0) {
perror("setsockopt SO_REUSEADDR");
exit(1);
}
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(port);
servlen = sizeof(servaddr);
ret = bind(servfd, (struct sockaddr *)&servaddr, servlen);
if (ret < 0) {
printf("%04d: ERRO %-25.25s:%4d : %s", port, "bind", errno, ctime(&tick));
exit(1);
}
listen(servfd, 5);
for (;;) {
clntlen = sizeof(clntaddr);
clntfd = accept(servfd, (struct sockaddr *)&clntaddr, &clntlen);
if (clntfd >= 0) {
pid = fork();
if (pid == 0) {
tick = time(NULL);
printf("%04d: CONN %-30.30s : %s", port, inet_ntoa(clntaddr.sin_addr), ctime(&tick));
fflush(stdout);
optlen = sizeof(keepalive);
ret = setsockopt(clntfd, SOL_SOCKET, SO_KEEPALIVE, &keepalive, optlen);
if (ret < 0) {
tick = time(NULL);
printf("%04d: ERRO %-25.25s %4d : %s", port, "setsockopt SO_KEEPALIVE", errno, ctime(&tick));
exit(1);
}
memset(buf, 0, sizeof(buf));
len = read(clntfd, buf, sizeof(buf));
tick = time(NULL);
printf("%04d: RECV %-30d : %s", port, (int)len, ctime(&tick));
exit(0);
} else if (pid > 0) {
close(clntfd);
continue;
} else {
close(clntfd);
tick = time(NULL);
printf("%04d: ERRO %-25.25s:%4d : %s", port, "fork", errno, ctime(&tick));
continue;
}
} else if (errno == EINTR) {
continue;
} else {
tick = time(NULL);
printf("%04d: ERRO %-25.25s:%4d : %s", port, "accept", errno, ctime(&tick));
exit(1);
}
}
return 0;
}