【问题标题】:when tcp in close_wait, select always return 1当 tcp 在 close_wait 时,select 总是返回 1
【发布时间】:2014-09-05 18:54:39
【问题描述】:

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

为什么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

【问题讨论】:

  • 当远程端点关闭其连接时,本地端点将变得可读,但read 返回0。那表示远程端点已经关闭,read 返回0
  • 关于你对errno的使用,除非确实有错误,否则不要检查它,并且总是在错误的函数调用之后直接检查它(即如果@987654332 @失败你必须在select之后直接检查errno)。想想如果sleep 失败会发生什么?然后errno 将包含来自sleep 函数的错误。
  • 你已经告诉 select() 当你可以从套接字读取时你想要一个通知。您应该从套接字读取,然后您可以检查从套接字读取是否在该套接字上给您一个错误。 (select() 可以监视许多套接字,当您监视套接字的读取事件时,select() 不会指示某个特定套接字上的错误)。如果您在 select() 告诉您可以读取时选择不从套接字读取,则 select() 会告诉您在下一次迭代中您仍然可以从它读取。
  • 最后,记住select 修改集合,所以如果你在传递给select的集合中有多个描述符,你必须清除集合并再次添加所有描述符。
  • errno的值如果没有错误是未定义的,除非有错误,否则不要检查。

标签: c linux sockets tcp


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

    Select 返回是因为它监视的其中一个套接字上有一个事件。该文档使用术语“可读”。在这种情况下,它有点误导,因为另一端的套接字已关闭并且没有字节可以从中读取。文档之所以这样表述,是因为 select 适用于任何类型的文件描述符。 “文件”可以是套接字、管道或普通文件。他们不想被不同类型文件描述符的细节所纠缠。

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

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

    监视写入时也是如此。如果另一方关闭其端点,则就 select 而言,套接字将被标记为“可写”。在您真正尝试写入套接字之前,您不会收到错误消息。

    【讨论】:

    • 套接字没有关闭。连接被对等方关闭:套接字仍然打开。否则 select() 将立即返回错误。您正在将套接字与连接混合在一起。当您阅读到流的末尾时,您不会收到错误消息。你得到一个流结束指示:recv() 返回零。
    • 不,你没有。它仍然说“套接字已关闭”和“一旦您读取了所有可用数据,您将收到一个错误......”。这些陈述都不是真的。您第三段中的推理也是似是而非的。真正的原因是 EOS 被视为读取事件而不是其自身的事件。您的最后一段完全不正确。当且仅当发送缓冲区中有空间时,套接字才被标记为可写:与远程关闭完全无关。
    • 我只能看到,您仍然没有修复我八小时前指出的任何错误。我的最后一点确实是正确的,select() 确实没有将已收到远程关闭的连接标记为writeable。这样做是错误的。它只知道它收到了一个 FIN,这可能只是写关闭:对等方可能仍在读取。
    • 第二、三、四段还是有同样的问题。
    猜你喜欢
    • 1970-01-01
    • 2016-11-21
    • 2019-10-10
    • 2014-04-13
    • 2017-03-20
    • 2013-11-04
    • 2011-12-09
    • 2012-07-08
    • 1970-01-01
    相关资源
    最近更新 更多