找出当前Java VM中打开的网络套接字 [英] Finding out what network sockets are open in the current Java VM

查看:240
本文介绍了找出当前Java VM中打开的网络套接字的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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 threads was easy, because you can ask a ThreadGroup for 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.

有没有办法从JVM获取所有客户端和服务器套接字的列表,类似于netstat?我正在使用 Netty OIO (即 java.net.ServerSocket java.net.Socket )该解决方案需要在Windows和Linux上运行。

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

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

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.

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

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, JVMTI is 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不起作用,第三种选择是创建一个在创建时跟踪所有套接字的设计。这样做的缺点是可能会遗漏一些创建套接字的地方。由于我使用Netty,它似乎可以通过包装 ChannelFactory来实现并使用 ChannelGroup

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 ChannelFactory and using a ChannelGroup.

推荐答案

我能够挂钩 java.net.Socket java.net.ServerSocket 和spy 这些类的所有新实例。可以看到完整的代码。以下是该方法的概述:

I was able to hook into java.net.Socket and java.net.ServerSocket and spy all new instances of those classes. The complete code can be seen in the source repository. Here is an overview of the approach:

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

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.SocketImplFactory through java.net.Socket#setSocketImplFactory and java.net.ServerSocket#setSocketFactory.

java.net.SocketImpl 是包私有的,但有一点反思并不太难:

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

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

用于监视所有套接字的SocketImplFactory实现创建看起来像这样:

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

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

请注意,setSocketFactory / setSocketImplFactory只能被调用一次,因此您需要只进行一次测试这样做(就像我拥有它),或者你必须创建一个静态单例(yuck!)来保持那个间谍。

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.

然后问题是如何找出来插座是否关闭? Socket和ServerSocket都有一个方法 isClosed(),但它使用这些类的布尔内部来跟踪它是否被关闭 - SocketImpl实例没有一个简单的检查是否关闭的方式。 (顺便说一下,Socket和ServerSocket都有SocketImpl支持 - 没有ServerSocketImpl。)

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".)

值得庆幸的是,SocketImpl引用了Socket或ServerSocket,它是后盾。前面提到的 setImpl()方法调用 impl.setSocket(this) impl.setServerSocket(这个),可以通过调用 java.net.SocketImpl #getSocket java.net来获取该引用.SocketImpl #getServerSocket

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#getSocket or 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);
    }
}

请注意,getSocket / getServerSocket可能无法在SocketImplFactory,因为Socket / ServerSocket只在从那里返回SocketImpl之后设置它们。

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

现在有了检查我们的测试所需的所有基础结构,无论我们想要什么关于Socket / ServerSocket:

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

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

完整的源代码是 here

这篇关于找出当前Java VM中打开的网络套接字的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆