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

给你一囗甜甜゛ 提交于 2019-11-28 18:20:11

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:

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.

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

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

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.

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

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

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

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

The full source code is here.

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

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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!