我能够连接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 只能调用一次,因此您要么只需要一个测试来执行此操作(就像我有它一样),要么您必须创建一个静态单例(糟糕!)来保存该间谍。
那么问题来了,那如何判断socket是否关闭呢?Socket 和 ServerSocket 都有一个方法isClosed()
,但它使用这些类内部的布尔值来跟踪它是否已关闭 - SocketImpl 实例没有一种简单的方法来检查它是否已关闭。(顺便说一句,Socket 和 ServerSocket 都由 SocketImpl 支持 - 没有“ServerSocketImpl”。)
值得庆幸的是,SocketImpl 引用了它所支持的 Socket 或 ServerSocket。上述setImpl()
方法调用impl.setSocket(this)
or ,并且可以通过调用orimpl.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));
}
完整的源代码在这里。