[英]Finding out what network sockets are open in the current Java VM
我正在編寫一個端到端測試,我的Java程序發布了它的所有資源 - 線程,服務器套接字,客戶端套接字。 它是一個庫,因此通過退出JVM釋放資源不是一種選擇。 測試線程的釋放很容易,因為您可以向ThreadGroup詢問其中的所有線程,但我還沒有找到一種獲取當前JVM正在使用的所有網絡套接字列表的好方法。
有沒有辦法從JVM獲取所有客戶端和服務器套接字的列表,類似於netstat? 我在Java 7上使用Netty和OIO (即java.net.ServerSocket和java.net.Socket )。該解決方案需要在Windows和Linux上運行。
我的第一個偏好是使用純Java從JVM中詢問它。 我試圖尋找一個MX Bean或類似的,但沒有找到任何。
另一個選擇可能是連接到JVM的分析/調試API並詢問Socket和ServerSocket的所有實例,但我不知道如何做到這一點以及是否可以在沒有本機代碼的情況下完成(AFAIK, JVMTI僅限本機) )。 此外,它不應該使測試變慢(即使我最慢的端到端測試只有0.5秒,其中包括啟動另一個JVM進程)。
如果詢問JVM不起作用,第三種選擇是創建一個設計,在設置時跟蹤所有套接字。 這樣做的缺點是可能會遺漏一些創建套接字的地方。 由於我使用Netty,它似乎可以通過包裝ChannelFactory和使用ChannelGroup來實現 。
我能夠掛鈎到java.net.Socket
和java.net.ServerSocket
並窺探這些類的所有新實例。 可以在源存儲庫中看到完整的代碼。 以下是該方法的概述:
當實例化Socket或ServerSocket時,它的構造函數中的第一件事是調用setImpl()
,它實例化實際實現套接字功能的對象。 默認實現是java.net.SocksSocketImpl
一個實例,但是可以通過java.net.Socket#setSocketImplFactory
和java.net.ServerSocket#setSocketFactory
設置自定義java.net.SocketImplFactory
來覆蓋它。
java.net.SocketImpl
的所有實現都是package-private,這有點復雜,但是有一些反思並不太難:
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實例沒有一種簡單的方法來檢查它是否被關閉。 (順便說一下,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);
}
}
請注意,可能不會在SocketImplFactory中調用getSocket / getServerSocket,因為Socket / ServerSocket僅在從那里返回SocketImpl之后才設置它們。
現在有了檢查我們的測試所需的所有基礎結構,無論我們想要什么關於Socket / ServerSocket:
for (SocketImpl impl : allSockets) {
assertIsClosed(getSocket(impl));
}
完整的源代碼在這里 。
我自己沒有嘗試過,但JavaSpecialists時事通訊提出了類似的問題:
http://www.javaspecialists.eu/archive/Issue169.html
在底部,他描述了使用AspectJ的方法。 您可以將切入點放在創建套接字的構造函數周圍,並且具有在那里注冊套接字創建的代碼。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.