使用相同主机创建套接字

时间:2011-05-22 15:41:07

标签: java sockets networking

在Java中,创建具有相同参数的套接字时(忽略此代码将抛出ConnectException的事实):

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

s1是否等于s2? 我的意思是,关闭s1会对s2产生同样的效果吗?

如果没有,我怎么能关闭一个我没有引用的套接字(只有创建它的ip和端口)。

感谢。

2 个答案:

答案 0 :(得分: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

首先你可以看到套接字唯一需要的只是4元组的一部分是不同的。即使我们从127.0.0.1连接到127.0.0.1端口55555,源端口是唯一的这一事实就足够了。

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

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

答案 1 :(得分:1)

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

如果您单步检查并检查,则应该发现s1设置为在断开s2时连接。

至于如何关闭一个你没有引用的套接字,它不会那样运行。您必须创建Socket的实例并在程序执行期间维护它。如果引用丢失或丢失,我相信你必须重新创建Socket对象。