向关闭的TCP/IP连接写入数据会挂起。

3
我的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);

编辑: 问题是由于我的编程错误导致的。看起来我没有正确地启动接受线程。


你是如何确保当客户端尝试连接时,服务器已经开始监听? - fkl
@fayyazkl,在启动线程之前有一个监听调用。接受调用成功并返回可读的fd。 - Basilevs
如果这是问题的话,他甚至无法建立连接,更别说write()阻塞了。 - user207421
2个回答

1
每个TCP连接都有一个接收缓冲区,用于存储已接收但未传递给应用程序的数据。如果在您的服务器上不执行read()操作,则数据将累积在接收缓冲区中,并且在某一时刻,该接收缓冲区变满,导致TCP发送Window=0消息。
您的服务器线程代码应该如下所示:
char a[10];
int connection = accept(s);
while(true)
    if (read( connection , a, 7)<=0)
        break;
close(connection);

如果客户端从未在永久打开的连接中写入数据,那么它将永远挂起。我如何在不读取数据的情况下终止连接?为什么close()不能释放此连接实例的所有缓冲区? - Basilevs
  1. 如果您的客户端执行了close(),那么没问题。read()返回0并且循环关闭。
  2. 当然要使用close(),但是在所有写操作完成后再执行close()。
  3. close()释放接收方的所有缓冲区,但无法控制发送方。
- banuj
  1. 在实际应用中,服务器显然无法控制客户端的行为。因此,依赖客户端的行为是没有意义的。
  2. 同样,在这里 - 客户端进行写操作,而不是服务器。我们无法可靠地预测何时完成所有写操作。
  3. 那么为什么客户端的TCP/IP继续接收确认和报告没有错误呢?
- Basilevs
1
@Basilevs 如果客户端从未关闭套接字,它也会永远挂起。解决方案有三个方面:(1)写入一些内容,(2)关闭套接字,(3)在读取端使用读取超时。 - user207421

0

你是否设置了 SIGPIPE 的处理程序,而不是将其设置为 SIG_IGN


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接