2011-05-22 19 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