【问题标题】:Writing to closed TCP/IP connection hangs写入关闭的 TCP/IP 连接挂起
【发布时间】:2012-11-09 08:53:34
【问题描述】:

我的 TCP/IP 客户端在写入套接字时挂起。即使服务器通过close()(或shutdown())调用正确关闭了已接受的连接,也会发生这种情况。对于这种情况,我一直认为 write 应该返回 ECONNRESET 错误。

如何防止同步输出挂断?或者更确切地说,我做错了什么,以至于write() 没有报告错误?

我应该使用send() 而不是write() 还是它们可以互换?

我正在使用两个线程的单独应用程序中测试网络。 主线程启动服务器线程,接受连接并立即关闭它。然后主线程通过连接到监听端口来模仿客户端行为。

主线程代码:

sockaddr_in serv_addr;
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(port);
int s = socket(AF_INET, SOCK_STREAM, 0);
if (bind(s, (sockaddr*)(&serv_addr),  sizeof(serv_addr)) < 0) {
    close(s);
    throw runtime_error(strerror(errno));
}
listen(s,1);
start_thread(s); // posix thread accepting a connection on s is started here
//Code below imitates  TCP/IP client
struct hostent *hp;
struct sockaddr_in addr;
if((hp = gethostbyname(host.c_str())) == NULL){
    throw HErrno();
}
bcopy(hp->h_addr, &addr.sin_addr, hp->h_length);
addr.sin_port = htons(port);
addr.sin_family = AF_INET;
int _socket = ::socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (_socket < 0) {
    throw Errno();
}
assert(ENOTCONN==107);
assert(_socket>=0);
if(::connect(_socket, (const struct sockaddr *)&addr, sizeof(struct sockaddr_in)) == -1){
    throw Errno();
}

while(true) {
  const char a[] = "pattern";
  if (write( _socket, a, 7)<0) // Writes 30000 times, then hangs
     break;
}

服务器线程代码:

int connection = accept(s);
close(connection);

编辑: 问题归结为我的编程错误。看来我没能正确开始接受线程。

【问题讨论】:

  • 您如何确保当您的客户端尝试连接时,服务器已经开始监听?
  • @fayyazkl,在接受线程启动之前有一个监听调用。接受调用成功并返回可读的 fd。
  • @fayyazkl 如果这是问题所在,他将无法获得连接,更不用说 write() 停顿了。

标签: linux sockets tcp


【解决方案1】:

您是否为SIGPIPE 设置了一个处理程序,而不是设置为SIG_IGN

【讨论】:

    【解决方案2】:

    每个 TCP 连接都有一个接收缓冲区,用于存储已接收但未传递给应用程序的数据。如果在您的服务器中您不执行 read(),则数据会累积在接收缓冲区中,并且在某一时刻此接收缓冲区已满,导致 TCP 发送 Window=0 消息。

    您的服务器线程代码应如下所示:

    char a[10];
    int connection = accept(s);
    while(true)
        if (read( connection , a, 7)<=0)
            break;
    close(connection);
    

    【讨论】:

    • 如果客户端从不写入永远打开的连接,这将永远挂起。如何在不阅读的情况下终止连接?以及为什么close() 不释放此连接实例的所有缓冲区?
    • 1) 如果您的客户执行了 close(),那就没问题了。 read() 返回 0 并且循环关闭。 2) 当然使用 close(),但是在所有写入完成之后,你会执行 close()。 3) close() 释放接收端的所有缓冲区,但无法控制发送端。
    • 1) 显然,在真正的应用程序服务器中,将无法控制客户端的操作。因此,依赖客户的行为毫无意义。 2)这里相同 - 客户端进行写入,而不是服务器。我们无法可靠地预测所有写入何时完成。 3)那为什么客户端的TCP/IP不断收到ack并且没有报错呢?
    • @Basilevs 如果客户端从不关闭他的套接字,它也会永远挂起。解决方案有三个:(1)写东西,(2)关闭套接字,(3)在读取端使用读取超时。
    猜你喜欢
    • 1970-01-01
    • 2012-04-12
    • 1970-01-01
    • 2012-11-19
    • 2011-02-07
    • 1970-01-01
    • 1970-01-01
    • 2017-05-24
    • 2018-08-29
    相关资源
    最近更新 更多