1. 简介
JDK中的java.net.Socket 和 java.net.ServerSocket实现非常古老,可以上溯到JDK 1.0,该实现是混合了JAVA和C代码,非常难于维护和调试。另外,该实现使用线程堆栈作为I/O缓冲区,这带来一系列的移植性和可用性问题。
因此JDK 13中实现了新的java.net.SocketImpl的实现类sun.nio.ch.NioSocketImpl,以替代原实现。
2. 分析
2.1 类图
2.2 主要方法
2.2.1 create
创建SocketImpl实例时,判断是否显式开启了USE_PLAINSOCKETIMPL,如是则使用旧的PlainSocketImpl,否则使用NioSocketImpl。
/**
* Creates an instance of platform's SocketImpl
*/
@SuppressWarnings("unchecked")
static <S extends SocketImpl & PlatformSocketImpl> S createPlatformSocketImpl(boolean server) {
if (USE_PLAINSOCKETIMPL) {
return (S) new PlainSocketImpl(server);
} else {
return (S) new NioSocketImpl(server);
}
}
2.2.1 bind
NioSocketImpl
@Override
protected void bind(InetAddress host, int port) throws IOException {
synchronized (stateLock) {
ensureOpen();
if (localport != 0)
throw new SocketException("Already bound");
NetHooks.beforeTcpBind(fd, host, port);
Net.bind(fd, host, port);
// set the address field to the given host address to keep
// compatibility with PlainSocketImpl. When binding to 0.0.0.0
// then the actual local address will be ::0 when IPv6 is enabled.
address = host;
localport = Net.localAddress(fd).getPort();
}
}
AbstractPlainSocketImpl
protected synchronized void bind(InetAddress address, int lport)
throws IOException
{
synchronized (fdLock) {
if (!closePending && (socket == null || !socket.isBound())) {
NetHooks.beforeTcpBind(fd, address, lport);
}
}
socketBind(address, lport);
if (socket != null)
socket.setBound();
if (serverSocket != null)
serverSocket.setBound();
}
- NioSocketImpl使用了新的对象锁stateLock替代旧的fdLock,使用时机更加明确。
- NIO相关的底层操作均被迁移到sun.nio.ch.Net类中的native方法,而非之前散落在各个类的native方法。
- NioSocketImpl与NIO共享相同的JDK内部基础类sun.nio.ch.Net,不再需要自己的native代码,提高了代码的可维护性。
2.2.2 listen
NioSocketImpl
@Override
protected void listen(int backlog) throws IOException {
synchronized (stateLock) {
ensureOpen();
if (localport == 0)
throw new SocketException("Not bound");
Net.listen(fd, backlog < 1 ? 50 : backlog);
}
}
AbstractPlainSocketImpl
protected synchronized void listen(int count) throws IOException {
socketListen(count);
}
- NioSocketImpl使用对象锁stateLock进行并发控制,而AbstractPlainSocketImpl则在native代码中进行并发控制,难以调试
- NioSocketImpl增加了对于边界条件的判断
2.2.3 accept
NioSocketImpl
/**
* Accepts a new connection so that the given SocketImpl is connected to
* the peer. The SocketImpl must be a newly created NioSocketImpl.
*/
@Override
protected void accept(SocketImpl si) throws IOException {
NioSocketImpl nsi = (NioSocketImpl) si;
if (nsi.state != ST_NEW)
throw new SocketException("Not a newly created SocketImpl");
FileDescriptor newfd = new FileDescriptor();
InetSocketAddress[] isaa = new InetSocketAddress[1];
// acquire the lock, adjusting the timeout for cases where several
// threads are accepting connections and there is a timeout set
ReentrantLock acceptLock = readLock;
int timeout = this.timeout;
long remainingNanos = 0;
if (timeout > 0) {
remainingNanos = tryLock(acceptLock, timeout, MILLISECONDS);
if (remainingNanos <= 0) {
assert !acceptLock.isHeldByCurrentThread();
throw new SocketTimeoutException("Accept timed out");
}
} else {
acceptLock.lock();
}
// accept a connection
try {
int n = 0;
FileDescriptor fd = beginAccept();
try {
if (remainingNanos > 0) {
// accept with timeout
configureNonBlocking(fd);
n = timedAccept(fd, newfd, isaa, remainingNanos);
} else {
// accept, no timeout
n = Net.accept(fd, newfd, isaa);
while (IOStatus.okayToRetry(n) && isOpen()) {
park(fd, Net.POLLIN);
n = Net.accept(fd, newfd, isaa);
}
}
} finally {
endAccept(n > 0);
assert IOStatus.check(n);
}
} finally {
acceptLock.unlock();
}
// get local address and configure accepted socket to blocking mode
InetSocketAddress localAddress;
try {
localAddress = Net.localAddress(newfd);
IOUtil.configureBlocking(newfd, true);
} catch (IOException ioe) {
nd.close(newfd);
throw ioe;
}
// set the fields
synchronized (nsi.stateLock) {
nsi.fd = newfd;
nsi.stream = true;
nsi.closer = FileDescriptorCloser.create(nsi);
nsi.localport = localAddress.getPort();
nsi.address = isaa[0].getAddress();
nsi.port = isaa[0].getPort();
nsi.state = ST_CONNECTED;
}
}
AbstractPlainSocketImpl
/**
* Accepts connections.
* @param s the connection
*/
protected void accept(SocketImpl s) throws IOException {
acquireFD();
try {
socketAccept(s);
} finally {
releaseFD();
}
}
- NioSocketImpl使用ReentrantLock进行并发控制,而AbstractPlainSocketImpl在native代码中进行并发控制。
- NioSocketImpl将大量代码逻辑迁移到Java代码中,便于维护和调试。
3. 总结
综上所述,新的NioSocketImpl类将大量native代码迁移到Java中,并使用Java内置锁和显式锁进行并发控制,提高了代码的可维护性。