2010-03-16 37 views
5

我有以下问题。我的客户端程序监视本地网络中服务器的可用性(使用Bonjour,但它不会反弹)。只要服务器被客户端应用程序“注意到”,客户端就会尝试创建一个套接字:Socket(serverIP,serverPort);我应该关闭两端的套接字吗?

在某些时候客户端可能会丢失服务器(Bonjour说服务器在网络中不再可见)。所以,客户决定使用套接字close,因为它不再有效。

在某些时候服务器再次出现。因此,客户端尝试创建与此服务器关联的新套接字。但!服务器可以拒绝创建此套接字,因为它(服务器)已经有一个与客户端IP和客户端端口关联的套接字。发生这种情况是因为客户端关闭了套接字,而不是由服务器关闭。它能发生吗?如果是这样的话,这个问题如何解决?

那么,我知道客户端不太可能尝试从同一端口(客户端端口)连接到服务器,因为客户端随机选择其端口。但它仍然会发生(偶然)。对?

回答

1

简答:是的,你应该关闭两端的插座。

虽然答案很简单,但实际上,如果您不在客户端/服务器协议中构建一些ACK/NACK方案,可能很难检测到对等方已停止响应。

即使您的协议确认您的处理线程可能正在等待ACK永远不会来自客户端,反之亦然。

如果您使用阻塞I/O,我将首先在套接字上设置读超时。不幸的是,如果对方变得没有响应,那么写入没有相应的超时。 我在我们的环境中发现了一个钝器,它的价值在于通过java.nio方法创建阻塞套接字,然后以可配置的间隔中断处理线程。

中断处理线程将关闭套接字,但是如果选择足够大的超时时间,您将知道存在问题。我们选择这种方法是因为应用程序最初是用阻塞I/O编写的,而将其转换为非阻塞的成本非常高。

但是,对于非阻塞I/O,您可以在更细粒度的时间间隔内检查连接状态,并更加智能地对缓慢/无响应的连接作出反应。

虽然非阻塞I/O需要较高的前期投资,但我认为它在后期的可靠性和吞吐量方面将带来更好的回报。

1

客户端操作系统将不会尽快将相同的端口分配给新的套接字。有几种机制可以阻止它。其中之一是TIME_WAIT状态,用于在连接关闭后保留一段时间的端口。

我不会担心它。

如果您确实需要检测断开连接,则必须实施由客户端和服务器启动的ping/pong协议。

2

是的,一旦检测到故障,请关闭插座。

如果未正确关闭,套接字将被“卡住”在“close_wait”中。 即使套接字已关闭,它的状态也会在短时间内处于time_wait状态。

但是,如果您将应用程序设计为对每个新连接使用不同的本地端口,则不需要等待旧套接字关闭。

(如当时你正在创建一个完全地不同的插座,因为插座被远程IP,远程端口,本地IP和本地端口识别。)

+1

“如果设计使用不同的本地端口为每个新连接的应用程序” 至于这就是自动发生的,没有必要为它设计的。避免在构建套接字时完全指定本地端口。 – EJP 2010-03-17 00:42:21

+0

哦,是的,我的意思是,不应该设计一个应用程序来依赖于来自哪个端口的请求。 – KarlP 2010-03-17 08:06:47

1

这听起来像您的客户端检测连接丢失到服务器(使用Bonjour),但您没有相应的功能在另一个方向。

当然,你也希望服务器端的非活动连接有一些超时,否则死连接将永远停留。除了你提到的潜在的IP地址/端口#冲突的问题之外,还有一个事实,即死连接消耗操作系统和应用程序资源(例如打开的文件描述符)

相反,你可能也想考虑不是当Bonjour表示该服务不再可见时,也很积极地从客户端关闭连接。如果您处于无线场景中,暂时性的连接丢失并不罕见,并且在连接恢复后(假设客户端仍具有相同的IP地址),TCP连接可能保持打开并且有效。最佳策略取决于你在谈论什么样的连接。如果它是一个相对无状态的连接,丢弃连接和重试的成本很低(如HTTP),那么在出现问题的第一个标志时抛出连接是有意义的。但是,如果它是一个具有重要用户状态的长期连接(如SSH登录会话),则更努力地保持连接处于活动状态是合理的。

2

为什么这是不可能发生的(注意,客户端强制使用相同的本地端口在其连接)的快速/肮脏的例证:

public class Server{ 

public static void main(String[] args) throws Exception { 
    new Thread(){ 
     java.net.ServerSocket server = new java.net.ServerSocket(12345); 
     java.util.ArrayList<java.net.Socket> l = new java.util.ArrayList<java.net.Socket>(); 
     public void run() { 
      try{ 
      while(true){ 
       java.net.Socket client = server.accept(); 
       System.out.println("Connection Accepted: S: "+client.getLocalPort()+", C: "+client.getPort()); 
       l.add(client); 
      } 
      }catch(Exception e){e.printStackTrace();} 
     } 
    }.start(); 
} 

和客户端(的东西有效替代服务器地址):

import java.net.InetAddress; 
import java.net.Socket; 


public class SocketTest { 
    public static void main(String[] args) throws Exception { 
     InetAddress server = InetAddress.getByName("192.168.0.256"); 
     InetAddress localhost = InetAddress.getLocalHost(); 
     Socket s = new Socket(server, 12345, localhost, 54321); 
     System.out.println("Client created socket"); 
     s.close(); 
     s = null; 
     System.gc(); 
     System.gc(); 
     Thread.sleep(1000); 
     s = new Socket(server, 12345, localhost, 54321); 
     System.out.println("Client created second socket"); 
     s.close(); 
     System.exit(55); 
    } 
} 

如果你启动服务器,然后尝试运行客户端的第一个连接会成功,但第二个将失败,“java.net.BindException:已使用的地址:连接”

-1

如果仅在阻塞套接字的情况下关闭服务器套接字,则客户端套接字将被关闭,但反之亦然。

否则它会在两端都更好。因为套接字对你来说是一个沉重的系统。它将永远使用系统的本地端口和远程端口。

感谢 苏尼尔·库马尔Sahoo

相关问题