Sockets 坏网络的socket编程

Sockets 坏网络的socket编程,sockets,tcp,network-programming,Sockets,Tcp,Network Programming,客户: socket(), bind(), listen() while (1) { accept() while((n = read()) { if (n == -1) abort(); /* never happended */ total_read += n } close() } 服务器: socket(), connect() and then for (1 to 1024) { write(1024 bytes) } exit(0);

客户:

socket(), bind(), listen()
while (1) {
  accept()
  while((n = read()) {
     if (n == -1) abort(); /* never happended */
     total_read += n
  }
  close()
}
服务器:

socket(), connect() and then 
for (1 to 1024) { 
   write(1024 bytes)
}
exit(0);
tcp4       0 130312  192.168.1.254.58573    A.B.C.D.8888    ESTABLISHED
现在,客户端在NAT下的Mac上运行,服务器在我的VPS上运行

一般来说,它工作良好,客户端发送所有数据,退出服务器并接收所有数据

然而,当客户端正在运行,但网络突然中断几分钟并重新恢复时,客户端在很长一段时间后不会退出。。。我用control+C杀死它并再次运行,服务器似乎不再读取数据,客户端仍在运行

以下是netstat显示的内容:

客户:

socket(), bind(), listen()
while (1) {
  accept()
  while((n = read()) {
     if (n == -1) abort(); /* never happended */
     total_read += n
  }
  close()
}
服务器:

socket(), connect() and then 
for (1 to 1024) { 
   write(1024 bytes)
}
exit(0);
tcp4       0 130312  192.168.1.254.58573    A.B.C.D.8888    ESTABLISHED
A.B.C.D是我的VPS地址 a、 b.c.d是我的公共客户地址

我的问题是:

1、为什么


2、服务器重新启动后工作正常,如何编写代码在不重新启动的情况下将其删除?

在TCP中,除非您尝试在连接上发送内容,否则无法判断连接是否失败。TCP不执行连接的主动监视实际上,存在可选的keepalive数据包,但这些数据包通常在连接空闲几个小时后才会发送。当您发送某个信息时,如果等待另一台机器返回确认信息时出现超时,则最终会出现错误。但是,如果您只是读取数据而没有发送数据,则无法判断连接是否已失败—看起来发送方没有任何要发送的内容


您可以通过设计应用程序来解决这个问题,这样客户端就需要每N秒发送一次内容。然后在服务器上设置一个计时器,检测到您在超过N秒的时间内没有收到任何东西。您应该增加一点额外的时间,以允许短暂的延迟。

当网络断开时,发生的情况是您的客户机一直在发送数据,并且在某个时候套接字发送缓冲区已满。我从您所展示的情况了解到您正在发送数据发送1024字节,1024次,总计1MB。发送缓冲区的默认值可能是16KB,但肯定小于1MB。然后,当客户端尝试写入时,它将永远被阻止

顺便说一句,现在我在回答你的问题,我不知道在多次TCP超时之后,TCP是否最终会放弃并关闭套接字,使套接字接口返回错误。我认为这不会发生…:-因此,如果网络出现问题,则连接会失败,但写入和读取不会失败

在服务器端,服务器在读取时被阻塞,因为它从未收到EOF

解决方案:

在客户端使用非阻塞套接字,如果网络断开,在某个点写入将返回错误EWoldblock。然后,您将意识到由于某种原因发送缓冲区已满。此时,您可以关闭连接并再次尝试连接。如果网络断开,您将收到一个错误


在服务器端,还可以使用非阻塞套接字并选择带有超时的函数。几次超时后,您可能会认为新连接有问题,并将其关闭。

thx就像FTP中的NOOP一样,我想从netstat了解有关RecvQ和SendQ的一些信息。服务器的netstat中有两项,第一项似乎是较新的连接,但第二项是旧的连接。可能是新连接的数据被发送到旧连接,我想…答案不错。这是我不明白的。客户端套接字不应该因为进程退出而隐式关闭吗?因此,是否关闭连接,服务器读取调用返回0?实际上,这并不重要,健壮的服务器套接字代码应该针对各种可能性编写,并具有合理的超时值来处理死连接。当客户端退出时,它将向服务器发送FIN。如果网络已启动,服务器将收到此消息,读取EOF并退出循环。但是如果网络关闭,服务器将永远不会收到此消息。如果出现网络问题,写入操作将失败。发件人不会永远被阻止。TCP会超时重试并重置连接,从而向发送方发送EConReset。默认发送缓冲区大小取决于平台。读取超时将解决读取问题。非阻塞I/O在这里既不是必要的,也不是解决方案。太多的错误信息和猜测-1@EJP,您是对的,客户端不会永远被阻止,只是会被阻止很长一段时间。然后写操作失败,出现主机不可访问错误EHOSTUNREACH,如果再次尝试写操作,它将返回EPIPE。我不知道write可以接收EconReset,read可以接收重置连接。当然,默认的发送缓冲区大小取决于平台,但它通常是一些KB,而不是1MB。你说得对,读取超时可以解决问题。非阻塞I/O对您来说可能不是必需的,但它肯定是一种解决方法。EHOSTUNREACH仅在连接期间发生。读可以接收EconReset,写也可以接收EconReset。非阻塞I/O不能解决这个问题。它只是防止您在TCP检测问题时阻塞。这仍然需要相同的时间。关闭连接只是因为
发送缓冲区已满是一个糟糕的建议。@EJP,这是错误的。当发送的段无法到达目标节点时,EHOSTUNREACH被设置为套接字的挂起错误。它可以在连接或写入时发生。除非收到设置了RST的段,否则不会看到错误ECONREET。您可以在Linux内核中看到它是如何工作的,例如在TCP用来向ip层发送段的函数ip_queue_xmit中。我还可以用C语言为您提供一个客户机-服务器示例。如果您想更好地理解它,可以向我发送电子邮件rodolk@yahoo.com或者我们可以打开另一个线程来讨论它。