2014-09-03 36 views
0

我写了一个服务器/客户端程序。并使用select检查套接字。但是当客户端关闭套接字(服务器中的tcp状态将获得close_wait)时,请选择始终返回1,并且errno为0.当tcp在close_wait时,选择总是返回1

为什么select返回1? Tcp socket现在没有什么可读的!

服务器:

int sock = socket(AF_INET, SOCK_STREAM, 0); 

struct sockaddr_in addr; 
addr.sin_family = AF_INET; 
addr.sin_addr.s_addr = htonl(INADDR_ANY); 
addr.sin_port = htons(6999); 
socklen_t socklen = sizeof(struct sockaddr_in); 
bind(sock, (struct sockaddr *)&addr, socklen); 
listen(sock, 0); 

int clisock; 
clisock = accept(sock, NULL, NULL); 

fd_set backset, rcvset; 
struct timeval timeout; 
timeout.tv_sec = 3; 

int maxfd = clisock+1; 
FD_SET(clisock, &rcvset); 
backset = rcvset; 

int ret; 
while(1) { 
    rcvset = backset; 
    timeout.tv_sec = 3; 
    ret = select(maxfd, &rcvset, NULL, NULL, &timeout); 
    if(ret <= 0) 
     continue; 

    sleep(1); 
    printf("ret:%d, %s\n", 
     ret, strerror(errno)); 
} 

客户端:

int sock = socket(AF_INET, SOCK_STREAM, 0); 

struct sockaddr_in addr; 
addr.sin_family = AF_INET; 
addr.sin_addr.s_addr = inet_addr("127.0.0.1"); 
addr.sin_port = htons(6999); 

socklen_t socklen = sizeof(struct sockaddr_in); 
connect(sock, (struct sockaddr *)&addr, socklen); 

sleep(3); 
close(sock); 
sleep(100); 

输出:

./server 
ret:1, Success 
ret:1, Success 
ret:1, Success 
+2

当远程端点关闭其连接时,本地端点将变为可读,但是'read'返回'0'。这表示远程终点已经关闭,'read'返回'0'。 – 2014-09-03 08:21:14

+1

关于你使用'errno',不要检查它,除非实际上*是一个错误,并且在错误的函数调用之后总是直接检查它(即如果'select'失败,你必须直接检查'errno'' select')。想一想,如果“睡眠”失败会发生什么?然后'errno'将包含'sleep'函数的错误。 – 2014-09-03 08:23:25

+0

循环速度如此之快,所以我添加了一个睡眠。 errno总是0.谢谢! – 2014-09-03 08:26:41

回答

1

选择退货,因为有它监视的插口内的事件。该文档使用术语“可读”。在这个CAS中,由于另一端的套接字是关闭的,并且没有字节可以读取,所以它有点误导。文档之所以这样表述是因为select可以处理任何类型的文件描述符。 “文件”可以是套接字,管道或普通文件。他们不希望被不同类型的文件描述符的细节纠缠在一起。

另一端的套接字关闭是正常的,所以在这种情况下选择不应该返回一个错误。当您尝试从套接字中实际读取数据时,如果连接已经在另一端关闭,您将在读取所有可用数据后收到错误消息。

由于select可以一次监控多个文件描述符,并且每个文件描述符都使用一位,因此无法区分“数据已到达”和“另一端的套接字已关闭”。这两个事件都会将套接字标记为“可读”。

监视写入时也是如此。如果另一端关闭它的端点,就选择而言,套接字将被标记为“可写”。直到您真正尝试写入套接字为止,您将不会收到错误。

+2

插座未关闭。连接被对等关闭:套接字仍然打开。否则select()会立即返回一个错误。您正在使用连接混合套接字。当你阅读到流结束时,你不会收到错误。流结束指示:recv()返回零。 – EJP 2014-09-03 11:45:09

+0

不,你没有。它仍然表示“套接字已关闭”,并且“一旦读取了所有可用数据,就会出现错误...”。这些陈述都不是真的。第三段中的推理也是似是而非的。真正的原因是EOS被视为阅读事件而不是它自己的事件。你最后的段落是不正确的。当且仅当发送缓冲区中有空间时,套接字被标记为可写:与远程无关的任何关闭。 – EJP 2014-09-04 22:37:10

+2

我只能看到,你还没有解决八小时前我指出的任何错误。我的最后一点确实是正确的,并且确实如此,'select()'不会将已经接收远程关闭的连接标记为'writeable'。这样做是错误的。它只知道它已经收到了一个FIN,这可能只是写入关机:对方仍然可以读取。 – EJP 2014-09-05 07:06:43

2

套接字是可读的,因为对方已经关闭它,当你读取它时,你会得到一个流的结束。而不是没有。

CLOSE_WAIT表示TCP正在等待关闭该套接字。所以关闭它。