2011-05-22 59 views
2

在Java中,使用相同的参数创建套接字时(忽略了一个事实,这个代码将抛出一个ConnectException):套接字创建具有相同的主机

Socket s1 = new Socket("127.0.0.1", 7575); 
Socket s2 = new Socket("127.0.0.1", 7575); 

s1等于s2? 我的意思是,结束s1s2有同样的影响吗?

如果不是,我怎样才能关闭一个我没有参考的套接字(只有它创建的IP和端口)。

谢谢。

+0

如果你可以这样做(即通过's2'关闭s1'的套接字),这对于s1'的用户来说会很烦人...... – 2011-05-22 15:42:55

回答

3

s1s2指的是两个截然不同的Socket个实例,因此关闭一个将不会对另一个产生直接影响。

仅仅因为两个套接字连接到相同的地址和端口,您不会得到ConnectException。只要目标服务器能够接受它们,就没有什么能阻止你将两个套接字连接到相同的目标地址和端口。当您只指定目标地址/端口对时,操作系统会自动为ephemeral port range(编号在1024以上)的套接字分配一个本地端口。重要的是,(remote address, remote port, local address, local port)组合(称为4元组)是唯一的;这是当底层TCP/IP协议栈需要将传入数据包与其对应的套接字匹配时,套接字如何区分的,以及服务器如何能够接受同一端口上的多个连接。

如果你做的事:

Socket s1 = new Socket(); 
    s1.bind(new InetSocketAddress("127.0.0.1", 7070)); 
    s1.connect(new InetSocketAddress("127.0.0.1", SERVER_PORT)); 

    Socket s2 = new Socket(); 
    s2.bind(new InetSocketAddress("127.0.0.1", 7070)); 
    s2.connect(new InetSocketAddress("127.0.0.1", SERVER_PORT)); 

,那么你会得到第二插座的绑定尝试一个BindException,因为只有服务器插槽允许创建绑定到相同的源端口插座。这是因为在accept()期间,4元组是完全已知的并且其唯一性可以确定。 This SO question有更多的细节。

下面是一个简单的测试类来说明当多个插座连接到同一个目标主机和端口会发生什么:

public class ConnectTest { 

static ServerSocket serverSock; 
static List<Socket> acceptedSockets = Collections.synchronizedList(new ArrayList<Socket>()); 
static final int SERVER_PORT = 55555; 

static class Server implements Runnable { 
    @Override 
    public void run() { 
     try { 
      serverSock = new ServerSocket(); 
      serverSock.bind(new InetSocketAddress("127.0.0.1", 55555)); 
      while (true) 
       { acceptedSockets.add(serverSock.accept()); } 
     } catch (IOException e) { e.printStackTrace(); } 
    } 
} 

public static void main(String[] args) throws Exception { 
    new Thread(new Server()).start(); 

    List<Socket> clientSockets = new ArrayList<Socket>(); 
    // open 10 sockets to the same target address/port 
    for (int i = 0; i < 10; i++) { 
     Socket s = new Socket("127.0.0.1", 55555); 
     System.out.println("Local address: " + s.getLocalSocketAddress() + 
       " Remote address: " + s.getRemoteSocketAddress()); 
     clientSockets.add(s); 
    } 

    // now close half 
    for (Socket s : clientSockets.subList(0, 5)) 
      s.close(); 

    // now list them again 
    System.out.println("\n\n Socket list after some closed:"); 
    for (Socket s : clientSockets) { 
     if (s.isClosed()) { 
      System.out.println("* Socket Closed *"); 
     } else { 
      System.out.println("Local address: " + s.getLocalSocketAddress() + 
       " Remote address: " + s.getRemoteSocketAddress()); 
     } 
    } 
} 

}

和输出会是这样的:

Local address: /127.0.0.1:43088 Remote address: /127.0.0.1:55555 
Local address: /127.0.0.1:43089 Remote address: /127.0.0.1:55555 
Local address: /127.0.0.1:43090 Remote address: /127.0.0.1:55555 
Local address: /127.0.0.1:43091 Remote address: /127.0.0.1:55555 
Local address: /127.0.0.1:43092 Remote address: /127.0.0.1:55555 
Local address: /127.0.0.1:43093 Remote address: /127.0.0.1:55555 
Local address: /127.0.0.1:43094 Remote address: /127.0.0.1:55555 
Local address: /127.0.0.1:43095 Remote address: /127.0.0.1:55555 
Local address: /127.0.0.1:43096 Remote address: /127.0.0.1:55555 
Local address: /127.0.0.1:43097 Remote address: /127.0.0.1:55555 


Socket list after some closed: 
* Socket Closed * 
* Socket Closed * 
* Socket Closed * 
* Socket Closed * 
* Socket Closed * 
Local address: /127.0.0.1:43093 Remote address: /127.0.0.1:55555 
Local address: /127.0.0.1:43094 Remote address: /127.0.0.1:55555 
Local address: /127.0.0.1:43095 Remote address: /127.0.0.1:55555 
Local address: /127.0.0.1:43096 Remote address: /127.0.0.1:55555 
Local address: /127.0.0.1:43097 Remote address: /127.0.0.1:55555 

您可以先看到所有需要的套接字是独一无二的,是四元组的一个部分是不同的。即使我们从127.0.0.1端口连接到127.0.0.1端口55555,源端口是唯一的事实就足够了。

当JVM需要操作系统执行操作时,套接字由它们的引用变量标识,这些引用变量对应于套接字描述符,但它们无法混淆。

强制一个套接字被关闭,当你没有对它的引用是不可能的我想。我能想到的唯一方法就是调用底层操作系统的本地库(通过JNI)或系统实用程序(使用Runtime.getRuntime().exec(command)或类似的方法)。您需要通过其4元组来识别连接,然后请求关闭它,但您需要提升特权才能完成此操作。

1

答案是否定的。这不会像你所期望的那样。在任何给定时间只能建立到端口的一个连接(不包括表现稍有不同的异步调用)。

如果您通过您的代码并检查,您应该会发现s1设置为连接,而s2断开连接。

至于如何关闭一个你没有参考的套接字,它不会以这种方式工作。您必须创建Socket的实例并在整个程序执行过程中对其进行维护。如果引用丢失或丢弃,我相信你必须重新创建Socket对象。

+0

感谢你回答Mathhew。 – 2011-05-22 15:48:07

+0

_“在任何给定时间只能建立到端口的一个连接(不包括表现稍有不同的异步调用)。”_这是不正确的,重要的是4元组是唯一的,见下文。 '异步呼叫'是一个红鲱鱼,它们与此无关。 – willjcroz 2011-05-22 20:26:52

+0

@willcroz不可否认,我对网络编程的理解是有限的。感谢您的更正。 – 2011-05-23 01:03:44

相关问题