Подтвердить что ты не робот

Выяснение, какие сетевые сокеты открыты в текущей виртуальной машине Java

Я пишу сквозной тест, который моя Java-программа выпускает все свои ресурсы - потоки, серверные сокеты, клиентские сокеты. Это библиотека, поэтому освобождение ресурсов путем выхода из JVM не является вариантом. Тестирование освобождения потоков было легко, потому что вы можете задать ThreadGroup для всех потоков в нем, но я еще не нашел хороший способ получить список всех сетевых сокетов, которые использует текущая JVM.

Есть ли способ получить из JVM список всех клиентских и серверных сокетов, похожих на netstat? Я использую Netty с OIO (т.е. java. net.ServerSocket и java.net.Socket) на Java 7. Решение должно работать как на Windows, так и на Linux.

Мое первое предпочтение было бы спросить его у JVM с использованием чистой Java. Я попытался найти MX Bean или аналогичный, но не нашел.

Другим вариантом может быть подключение к API-интерфейсам профилирования/отладки JVM и запрос всех экземпляров Socket и ServerSocket, но я не знаю, как это сделать и можно ли это сделать без собственного кода (AFAIK, JVMTI является только родным). Кроме того, он не должен делать тесты медленными (даже мой самый медленный сквозной тест составляет всего 0,5 секунды, что включает запуск другого процесса JVM).

Если опрос JVM не работает, третьим вариантом будет создание дизайна, который отслеживает все сокеты по мере их создания. Это имеет недостаток в возможности упустить место, где сокеты созданы. Поскольку я использую Netty, это представляется возможным, обертывая ChannelFactory и используя ChannelGroup.

4b9b3361

Ответ 1

Мне удалось подключиться к java.net.Socket и java.net.ServerSocket и просмотреть все новые экземпляры этих классов. Полный код можно увидеть в исходном репозитории. Вот краткий обзор подхода:

При создании экземпляра Socket или ServerSocket первым делом в его конструкторе является вызов setImpl(), который создает экземпляр объекта, который действительно реализует функцию сокета. Реализация по умолчанию представляет собой экземпляр java.net.SocksSocketImpl, но его можно переопределить, установив пользовательский java.net.SocketImplFactory через java.net.Socket#setSocketImplFactory и java.net.ServerSocket#setSocketFactory.

Это немного усложняется всеми реализациями 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);
    }
}

Реализация 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;
        }
    });

Обратите внимание, что setSocketFactory/setSocketImplFactory можно вызывать только один раз, поэтому вам нужно либо иметь только один тест, который делает это (например, у меня есть), либо вы должны создать статический синглтон (yuck!) для хранения этого шпиона.

Тогда возникает вопрос, что как узнать, закрыт ли сокет? У Socket и ServerSocket есть метод isClosed(), но он использует логическое внутреннее для этих классов для отслеживания того, закрыт ли он - экземпляр SocketImpl не имеет простого способа проверить, закрыт ли он. (BTW, как Socket, так и ServerSocket поддерживаются SocketImpl - нет "ServerSocketImpl".)

К счастью, у SocketImpl есть ссылка на Socket или ServerSocket, которые он поддерживает. Вышеупомянутый метод setImpl() вызывает impl.setSocket(this) или impl.setServerSocket(this), и его можно вернуть, вызвав java.net.SocketImpl#getSocket или java.net.SocketImpl#getServerSocket.

Вновь эти методы являются приватными пакетами, поэтому требуется немного отражения:

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 оттуда.

Теперь есть вся инфраструктура, необходимая для проверки в наших тестах, что мы хотим о Socket/ServerSocket:

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

Полный исходный код здесь.

Ответ 2

Я сам не пробовал, но в бюллетене JavaSpecialists есть аналогичная проблема:

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

Внизу он описывает подход с использованием AspectJ. Вероятно, вы можете поместить pointcut вокруг конструктора, который создает сокет, и иметь код, который регистрирует создание сокета там.