找出当前 Java VM 中打开的网络套接字

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11669554/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 05:59:07  来源:igfitidea点击:

Finding out what network sockets are open in the current Java VM

javasocketsmonitoringnetty

提问by Esko Luontola

I'm writing an end-to-end test that my Java program releases all of its resources - threads, server sockets, client sockets. It's a library, so releasing resources by exiting the JVM is not an option. Testing the releasing of threadswas easy, because you can ask a ThreadGroupfor all threads in it, but I haven't yet found a good way to get a list of all network sockets that the current JVM is using.

我正在编写一个端到端的测试,我的 Java 程序释放了它的所有资源——线程、服务器套接字、客户端套接字。它是一个库,因此通过退出 JVM 来释放资源不是一种选择。测试线程的释放很容易,因为您可以向ThreadGroup询问其中的所有线程,但我还没有找到一种获得当前 JVM 正在使用的所有网络套接字列表的好方法。

Is there some way to get from a JVM the list of all client and server sockets, similar to netstat?I'm using Nettywith OIO(i.e. java.net.ServerSocketand java.net.Socket) on Java 7. The solution needs to work on both Windows and Linux.

是否有某种方法可以从 JVM 获取所有客户端和服务器套接字的列表,类似于 netstat?我在 Java 7 上使用NettyOIO(即java.net.ServerSocketjava.net.Socket)。该解决方案需要在 Windows 和 Linux 上运行。

My first preference would be to ask it from the JVM using pure Java. I tried to look for an MX Bean or similar, but did not find any.

我的第一个偏好是使用纯 Java 从 JVM 中询问它。我试图寻找 MX Bean 或类似的东西,但没有找到。

Another option might be to connect to the JVM's profiling/debugging APIs and ask for all instances of Socket and ServerSocket, but I don't know how to do that and whether it can be done without native code (AFAIK, JVMTIis native-only). Also, it shouldn't make the tests slow (even my slowest end-to-end test is just 0.5 seconds, which includes starting another JVM process).

另一种选择可能是连接到 JVM 的分析/调试 API 并询问 Socket 和 ServerSocket 的所有实例,但我不知道如何做到这一点,以及是否可以在没有本机代码的情况下完成(AFAIK,JVMTI仅适用于本机)。此外,它不应该使测试变慢(即使是我最慢的端到端测试也只有 0.5 秒,其中包括启动另一个 JVM 进程)。

If interrogating the JVM doesn't work, a third option would be to create a design which tracks all sockets as they are created. This has the disadvantage of having a possibility of missing some place where sockets are created. Since I'm using Netty, it seems implementable by wrapping ChannelFactoryand using a ChannelGroup.

如果查询 JVM 不起作用,第三种选择是创建一个设计,在创建时跟踪所有套接字。这样做的缺点是可能会丢失创建套接字的某些位置。由于我使用的是 Netty,它似乎可以通过包装ChannelFactory并使用ChannelGroup 来实现

回答by Esko Luontola

I was able to hook into java.net.Socketand java.net.ServerSocketand spy allnew instances of those classes. The complete code can be seen in the source repository. Here is an overview of the approach:

我可以挂接到java.net.Socketjava.net.ServerSocket窥探所有这些类的新实例。完整的代码可以在源代码库中看到。以下是该方法的概述:

When a Socket or ServerSocket is instantiated, the first thing in its constructor is a call to setImpl()which instantiates the object which really implements the socket functionality. The default implementation is an instance of java.net.SocksSocketImpl, but it's possible to override that by setting a custom java.net.SocketImplFactorythrough java.net.Socket#setSocketImplFactoryand java.net.ServerSocket#setSocketFactory.

实例化 Socket 或 ServerSocket 时,其构造函数中的第一件事是调用setImpl()实例化真正实现套接字功能的对象。默认实现是 的实例java.net.SocksSocketImpl,但可以通过设置自定义java.net.SocketImplFactory通过java.net.Socket#setSocketImplFactory和来覆盖它java.net.ServerSocket#setSocketFactory

This is complicated a bit by all implementations of java.net.SocketImplbeing package-private, but with a little bit of reflection that's not too hard:

java.net.SocketImpl对于包私有的所有实现来说,这有点复杂,但有一点点反思,这并不太难:

private static SocketImpl newSocketImpl() {
    try {
        Class<?> defaultSocketImpl = Class.forName("java.net.SocksSocketImpl");
        Constructor<?> constructor = defaultSocketImpl.getDeclaredConstructor();
        constructor.setAccessible(true);
        return (SocketImpl) constructor.newInstance();
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

The SocketImplFactory implementation for spying on all sockets as they are created looks something like this:

SocketImplFactory 用于在创建时监视所有套接字的实现如下所示:

    final List<SocketImpl> allSockets = Collections.synchronizedList(new ArrayList<SocketImpl>());
    ServerSocket.setSocketFactory(new SocketImplFactory() {
        public SocketImpl createSocketImpl() {
            SocketImpl socket = newSocketImpl();
            allSockets.add(socket);
            return socket;
        }
    });

Note that setSocketFactory/setSocketImplFactory can be called only once, so you either need to have only one test which does that (like I have it), or you must create a static singleton (yuck!) for holding that spy.

请注意, setSocketFactory/setSocketImplFactory 只能调用一次,因此您要么只需要一个测试来执行此操作(就像我有的那样),要么您必须创建一个静态单例(糟糕!)来保存该间谍。

Then the question is that that how to find out whether the socket is closed? Both Socket and ServerSocket have a method isClosed(), but that uses a boolean internal to those classes for keeping track of whether it was closed - the SocketImpl instance does not have an easy way of checking whether it was closed. (BTW, both Socket and ServerSocket are backed by a SocketImpl - there is no "ServerSocketImpl".)

那么问题来了,如何判断socket是否关闭呢?Socket 和 ServerSocket 都有一个 method isClosed(),但它使用这些类内部的布尔值来跟踪它是否已关闭 - SocketImpl 实例没有一种简单的方法来检查它是否已关闭。(顺便说一句,Socket 和 ServerSocket 都由 SocketImpl 支持——没有“ServerSocketImpl”。)

Thankfully the SocketImpl has a reference to the Socket or ServerSocket which it is backing. The aforementioned setImpl()method calls impl.setSocket(this)or impl.setServerSocket(this), and it's possible to get that reference back by calling java.net.SocketImpl#getSocketor java.net.SocketImpl#getServerSocket.

谢天谢地, SocketImpl 有一个对它所支持的 Socket 或 ServerSocket 的引用。上述setImpl()方法调用impl.setSocket(this)or impl.setServerSocket(this),并且可以通过调用java.net.SocketImpl#getSocketor取回该引用java.net.SocketImpl#getServerSocket

Once again those methods are package-private, so a little bit of reflection is needed:

再一次,这些方法是包私有的,因此需要一些反射:

private static Socket getSocket(SocketImpl impl) {
    try {
        Method getSocket = SocketImpl.class.getDeclaredMethod("getSocket");
        getSocket.setAccessible(true);
        return (Socket) getSocket.invoke(impl);
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

private static ServerSocket getServerSocket(SocketImpl impl) {
    try {
        Method getServerSocket = SocketImpl.class.getDeclaredMethod("getServerSocket");
        getServerSocket.setAccessible(true);
        return (ServerSocket) getServerSocket.invoke(impl);
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

Note that getSocket/getServerSocket may not be called inside the SocketImplFactory, because Socket/ServerSocket sets them only after the SocketImpl is returned from there.

请注意,可能不会在 SocketImplFactory 内部调用 getSocket/getServerSocket,因为 Socket/ServerSocket 仅在从那里返回 SocketImpl 后才设置它们。

Now there is all the infrastructure necessary for checking in our tests whatever we want about the Socket/ServerSocket:

现在有所有必要的基础设施来检查我们的测试我们想要的关于 Socket/ServerSocket 的任何内容:

    for (SocketImpl impl : allSockets) {
        assertIsClosed(getSocket(impl));
    }

The full source code is here.

完整的源代码在这里

回答by James Kingsbery

I haven't tried it myself, but the JavaSpecialists newsletter presents a similar problem:

我自己没有尝试过,但是 JavaSpecialists 时事通讯提出了类似的问题:

http://www.javaspecialists.eu/archive/Issue169.html

http://www.javaspecialists.eu/archive/Issue169.html

At the bottom, he describes an approach using AspectJ. You could probably put the pointcut around the constructor that creates the socket, and have code that registers socket creation there.

在底部,他描述了一种使用 AspectJ 的方法。您可以将切入点放在创建套接字的构造函数周围,并在那里注册套接字创建的代码。