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內置鎖和顯式鎖進行併發控制,提高了代碼的可維護性。