使用同一主机创建套接字

发布于 2024-11-09 08:28:02 字数 313 浏览 0 评论 0原文

在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和端口)。

谢谢。

In Java, when creating sockets with the same parameters (ignoring the fact that this code will throw a ConnectException):

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

Is s1 equal to s2?
I mean, will closing s1 have the same effect on s2?

If not, how can I close a socket I don't have a reference to (only having the ip and port it was created with).

Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

我喜欢麦丽素 2024-11-16 08:28:02

s1s2 引用两个不同的 Socket 实例,因此关闭一个实例不会对另一个实例产生直接影响。

不过,您不会仅仅因为两个套接字连接到相同的地址和端口而得到 ConnectException。只要目标服务器能够接受它们,就没有什么可以阻止您将两个套接字连接到相同的目标地址和端口。当您仅指定目标地址/端口对时,操作系统会自动从 临时端口范围(编号高于 1024)。重要的是(远程地址、远程端口、本地地址、本地端口)组合(称为 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 元组是完全已知的并且可以确定其唯一性。 这个问题有更多细节。

这是一个简单的测试类,用于演示当多个套接字连接到同一目标主机和端口时会发生什么:

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());
        }
    }
}

}

输出将类似于:

本地地址:/127.0.0.1:43088 远程地址:/127.0.0.1:55555
本地地址:/127.0.0.1:43089 远程地址:/127.0.0.1:55555
本地地址:/127.0.0.1:43090 远程地址:/127.0.0.1:55555
本地地址:/127.0.0.1:43091 远程地址:/127.0.0.1:55555
本地地址:/127.0.0.1:43092 远程地址:/127.0.0.1:55555
本地地址:/127.0.0.1:43093 远程地址:/127.0.0.1:55555
本地地址:/127.0.0.1:43094 远程地址:/127.0.0.1:55555
本地地址:/127.0.0.1:43095 远程地址:/127.0.0.1:55555
本地地址:/127.0.0.1:43096 远程地址:/127.0.0.1:55555
本地地址:/127.0.0.1:43097 远程地址:/127.0.0.1:55555


 一些关闭后的套接字列表:
* 插座关闭 *
* 插座关闭 *
* 插座关闭 *
* 插座关闭 *
* 插座关闭 *
本地地址:/127.0.0.1:43093 远程地址:/127.0.0.1:55555
本地地址:/127.0.0.1:43094 远程地址:/127.0.0.1:55555
本地地址:/127.0.0.1:43095 远程地址:/127.0.0.1:55555
本地地址:/127.0.0.1:43096 远程地址:/127.0.0.1:55555
本地地址:/127.0.0.1:43097 远程地址:/127.0.0.1:55555

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

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

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

s1 and s2 refer to two distinct Socket instances so closing one will have no direct effect on the other.

You would not get a ConnectException just because of two sockets connecting to the same address and port though. There is nothing stopping you connecting two sockets to the same target address and port as long as the target server is able to accept them. When you only specify the target address/port pair the operating system automatically assigns a local port for the socket from the ephemeral port range (numbering somewhere above 1024). What matters is that the (remote address, remote port, local address, local port) combination (known as a 4-tuple) is unique; this is how the socket is differentiated by the underlying TCP/IP stack when it needs to match incoming packets to their corresponding sockets and is how a server is able to accept multiple connections on the same port.

If you were to do:

    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));

then you would get a BindException on the second socket's bind attempt, since only server sockets are allowed to create sockets bound to the same source port. This is because during accept() the 4-tuple is fully known and its uniqueness can be determined. This SO question has more detail.

Here is a simple test class to demonstrate what happens when multiple sockets connect to the same target host and port:

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());
        }
    }
}

}

And the output will be something like:

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

You can see firstly that all that is needed for the sockets to be unique is a single part of the 4-tuple to be different. Even though we are connecting from 127.0.0.1 to 127.0.0.1 port 55555, the fact that the source port is unique is enough.

The sockets are identified by their reference variables which correspond to socket descriptors when the JVM needs to ask the OS to perform operations, there is no way that they can be confused.

Forcing a socket to be closed when you don't own a reference to it is impossible I think. The only way to do it that I can think of would be to call out to the underlying operating system's native libraries (via JNI) or system utilities (using Runtime.getRuntime().exec(command) or similar). You would need to identify the connection by its 4-tuple and then request it be closed, but you would need to have elevated privileges to do so.

念三年u 2024-11-16 08:28:02

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

如果您单步执行代码并进行检查,您应该会发现 s1 设置为连接状态,而 s2 则断开连接。

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

The answer is no. This won't behave like you are anticipating. Only one connection to a port can be established at any given time (excluding asynchronous calls which behave somewhat differently).

if you stepped through your code and checked, you should find that s1 is set to be connected while s2 is disconnected.

As for how to close a socket you don't have a reference to, it doesn't function that way. You must create the instance of the Socket and maintain it throughout the execution of your program. If the reference is lost or disposed of, I believe you must recreate the Socket object.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文