Path: blob/master/src/java.base/share/classes/sun/nio/ch/ServerSocketChannelImpl.java
41159 views
/*1* Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.nio.ch;2627import java.io.FileDescriptor;28import java.io.IOException;29import java.net.BindException;30import java.net.InetSocketAddress;31import java.net.ProtocolFamily;32import java.net.ServerSocket;33import java.net.SocketAddress;34import java.net.SocketOption;35import java.net.SocketTimeoutException;36import java.net.StandardSocketOptions;37import java.net.UnixDomainSocketAddress;38import java.nio.channels.AlreadyBoundException;39import java.nio.channels.AsynchronousCloseException;40import java.nio.channels.ClosedChannelException;41import java.nio.channels.IllegalBlockingModeException;42import java.nio.channels.NotYetBoundException;43import java.nio.channels.SelectionKey;44import java.nio.channels.ServerSocketChannel;45import java.nio.channels.SocketChannel;46import java.nio.channels.spi.SelectorProvider;47import java.nio.file.Path;48import java.util.Collections;49import java.util.HashSet;50import java.util.Set;51import java.util.Objects;52import java.util.concurrent.locks.ReentrantLock;53import static java.net.StandardProtocolFamily.INET;54import static java.net.StandardProtocolFamily.INET6;55import static java.net.StandardProtocolFamily.UNIX;5657import sun.net.NetHooks;58import sun.net.ext.ExtendedSocketOptions;5960/**61* An implementation of ServerSocketChannels62*/6364class ServerSocketChannelImpl65extends ServerSocketChannel66implements SelChImpl67{68// Used to make native close and configure calls69private static final NativeDispatcher nd = new SocketDispatcher();7071// The protocol family of the socket72private final ProtocolFamily family;7374// Our file descriptor75private final FileDescriptor fd;76private final int fdVal;7778// Lock held by thread currently blocked on this channel79private final ReentrantLock acceptLock = new ReentrantLock();8081// Lock held by any thread that modifies the state fields declared below82// DO NOT invoke a blocking I/O operation while holding this lock!83private final Object stateLock = new Object();8485// -- The following fields are protected by stateLock8687// Channel state, increases monotonically88private static final int ST_INUSE = 0;89private static final int ST_CLOSING = 1;90private static final int ST_CLOSED = 2;91private int state;9293// ID of native thread currently blocked in this channel, for signalling94private long thread;9596// Binding97private SocketAddress localAddress; // null => unbound9899// set true when exclusive binding is on and SO_REUSEADDR is emulated100private boolean isReuseAddress;101102// Our socket adaptor, if any103private ServerSocket socket;104105// -- End of fields protected by stateLock106107ServerSocketChannelImpl(SelectorProvider sp) throws IOException {108this(sp, Net.isIPv6Available() ? INET6 : INET);109}110111ServerSocketChannelImpl(SelectorProvider sp, ProtocolFamily family)112throws IOException113{114super(sp);115Objects.requireNonNull(family, "'family' is null");116if ((family != INET) && (family != INET6) && (family != UNIX)) {117throw new UnsupportedOperationException("Protocol family not supported");118}119if (family == INET6 && !Net.isIPv6Available()) {120throw new UnsupportedOperationException("IPv6 not available");121}122123this.family = family;124if (family == UNIX) {125this.fd = UnixDomainSockets.socket();126} else {127this.fd = Net.serverSocket(family, true);128}129this.fdVal = IOUtil.fdVal(fd);130}131132ServerSocketChannelImpl(SelectorProvider sp,133ProtocolFamily family,134FileDescriptor fd,135boolean bound)136throws IOException137{138super(sp);139140if (family == UNIX) {141this.family = UNIX;142} else {143this.family = Net.isIPv6Available() ? INET6 : INET;144}145this.fd = fd;146this.fdVal = IOUtil.fdVal(fd);147148if (bound) {149synchronized (stateLock) {150if (family == UNIX) {151localAddress = UnixDomainSockets.localAddress(fd);152} else {153localAddress = Net.localAddress(fd);154}155}156}157}158159/**160* Returns true if this channel is to a INET or INET6 socket.161*/162private boolean isNetSocket() {163return (family == INET) || (family == INET6);164}165166/**167* Returns true if this channel is to a UNIX socket.168*/169boolean isUnixSocket() {170return (family == UNIX);171}172173// @throws ClosedChannelException if channel is closed174private void ensureOpen() throws ClosedChannelException {175if (!isOpen())176throw new ClosedChannelException();177}178179@Override180public ServerSocket socket() {181synchronized (stateLock) {182if (socket == null) {183if (isNetSocket()) {184socket = ServerSocketAdaptor.create(this);185} else {186throw new UnsupportedOperationException("Not supported");187}188}189return socket;190}191}192193@Override194public SocketAddress getLocalAddress() throws IOException {195synchronized (stateLock) {196ensureOpen();197if (isUnixSocket()) {198return UnixDomainSockets.getRevealedLocalAddress(localAddress);199} else {200return Net.getRevealedLocalAddress(localAddress);201}202}203}204205@Override206public <T> ServerSocketChannel setOption(SocketOption<T> name, T value)207throws IOException208{209Objects.requireNonNull(name);210if (!supportedOptions().contains(name))211throw new UnsupportedOperationException("'" + name + "' not supported");212if (!name.type().isInstance(value))213throw new IllegalArgumentException("Invalid value '" + value + "'");214215synchronized (stateLock) {216ensureOpen();217if (isNetSocket()218&& name == StandardSocketOptions.SO_REUSEADDR219&& Net.useExclusiveBind()) {220// SO_REUSEADDR emulated when using exclusive bind221isReuseAddress = (Boolean) value;222} else {223// no options that require special handling224Net.setSocketOption(fd, Net.UNSPEC, name, value);225}226return this;227}228}229230@Override231@SuppressWarnings("unchecked")232public <T> T getOption(SocketOption<T> name)233throws IOException234{235Objects.requireNonNull(name);236if (!supportedOptions().contains(name))237throw new UnsupportedOperationException("'" + name + "' not supported");238239synchronized (stateLock) {240ensureOpen();241if (isNetSocket()242&& name == StandardSocketOptions.SO_REUSEADDR243&& Net.useExclusiveBind()) {244// SO_REUSEADDR emulated when using exclusive bind245return (T) Boolean.valueOf(isReuseAddress);246} else {247// no options that require special handling248return (T) Net.getSocketOption(fd, Net.UNSPEC, name);249}250}251}252253private static class DefaultOptionsHolder {254static final Set<SocketOption<?>> defaultInetOptions = defaultInetOptions();255static final Set<SocketOption<?>> defaultUnixDomainOptions = defaultUnixDomainOptions();256257private static Set<SocketOption<?>> defaultInetOptions() {258HashSet<SocketOption<?>> set = new HashSet<>();259set.add(StandardSocketOptions.SO_RCVBUF);260set.add(StandardSocketOptions.SO_REUSEADDR);261if (Net.isReusePortAvailable()) {262set.add(StandardSocketOptions.SO_REUSEPORT);263}264set.addAll(ExtendedSocketOptions.serverSocketOptions());265return Collections.unmodifiableSet(set);266}267268private static Set<SocketOption<?>> defaultUnixDomainOptions() {269HashSet<SocketOption<?>> set = new HashSet<>();270set.add(StandardSocketOptions.SO_RCVBUF);271return Collections.unmodifiableSet(set);272}273}274275@Override276public final Set<SocketOption<?>> supportedOptions() {277if (isUnixSocket()) {278return DefaultOptionsHolder.defaultUnixDomainOptions;279} else {280return DefaultOptionsHolder.defaultInetOptions;281}282}283284@Override285public ServerSocketChannel bind(SocketAddress local, int backlog) throws IOException {286synchronized (stateLock) {287ensureOpen();288if (localAddress != null)289throw new AlreadyBoundException();290if (isUnixSocket()) {291localAddress = unixBind(local, backlog);292} else {293localAddress = netBind(local, backlog);294}295}296return this;297}298299private SocketAddress unixBind(SocketAddress local, int backlog) throws IOException {300UnixDomainSockets.checkPermission();301if (local == null) {302// Attempt up to 10 times to find an unused name in temp directory.303// If local address supplied then bind called only once304boolean bound = false;305int attempts = 0;306while (attempts < 10 && !bound) {307try {308Path path = UnixDomainSockets.generateTempName().getPath();309UnixDomainSockets.bind(fd, path);310bound = true;311} catch (BindException e) { }312attempts++;313}314if (!bound)315throw new BindException("Could not bind to temporary name");316} else {317Path path = UnixDomainSockets.checkAddress(local).getPath();318UnixDomainSockets.bind(fd, path);319}320Net.listen(fd, backlog < 1 ? 50 : backlog);321return UnixDomainSockets.localAddress(fd);322}323324private SocketAddress netBind(SocketAddress local, int backlog) throws IOException {325InetSocketAddress isa;326if (local == null) {327isa = new InetSocketAddress(Net.anyLocalAddress(family), 0);328} else {329isa = Net.checkAddress(local, family);330}331@SuppressWarnings("removal")332SecurityManager sm = System.getSecurityManager();333if (sm != null)334sm.checkListen(isa.getPort());335NetHooks.beforeTcpBind(fd, isa.getAddress(), isa.getPort());336Net.bind(family, fd, isa.getAddress(), isa.getPort());337Net.listen(fd, backlog < 1 ? 50 : backlog);338return Net.localAddress(fd);339}340341/**342* Marks the beginning of an I/O operation that might block.343*344* @throws ClosedChannelException if the channel is closed345* @throws NotYetBoundException if the channel's socket has not been bound yet346*/347private void begin(boolean blocking) throws ClosedChannelException {348if (blocking)349begin(); // set blocker to close channel if interrupted350synchronized (stateLock) {351ensureOpen();352if (localAddress == null)353throw new NotYetBoundException();354if (blocking)355thread = NativeThread.current();356}357}358359/**360* Marks the end of an I/O operation that may have blocked.361*362* @throws AsynchronousCloseException if the channel was closed due to this363* thread being interrupted on a blocking I/O operation.364*/365private void end(boolean blocking, boolean completed)366throws AsynchronousCloseException367{368if (blocking) {369synchronized (stateLock) {370thread = 0;371if (state == ST_CLOSING) {372tryFinishClose();373}374}375end(completed);376}377}378379@Override380public SocketChannel accept() throws IOException {381int n = 0;382FileDescriptor newfd = new FileDescriptor();383SocketAddress[] saa = new SocketAddress[1];384385acceptLock.lock();386try {387boolean blocking = isBlocking();388try {389begin(blocking);390n = implAccept(this.fd, newfd, saa);391if (blocking) {392while (IOStatus.okayToRetry(n) && isOpen()) {393park(Net.POLLIN);394n = implAccept(this.fd, newfd, saa);395}396}397} finally {398end(blocking, n > 0);399assert IOStatus.check(n);400}401} finally {402acceptLock.unlock();403}404405if (n > 0) {406return finishAccept(newfd, saa[0]);407} else {408return null;409}410}411412private int implAccept(FileDescriptor fd, FileDescriptor newfd, SocketAddress[] saa)413throws IOException414{415if (isUnixSocket()) {416UnixDomainSockets.checkPermission();417String[] pa = new String[1];418int n = UnixDomainSockets.accept(fd, newfd, pa);419if (n > 0)420saa[0] = UnixDomainSocketAddress.of(pa[0]);421return n;422} else {423InetSocketAddress[] issa = new InetSocketAddress[1];424int n = Net.accept(fd, newfd, issa);425if (n > 0)426saa[0] = issa[0];427return n;428}429}430431/**432* Accepts a new connection with a given timeout. This method requires the433* channel to be configured in blocking mode.434*435* @apiNote This method is for use by the socket adaptor.436*437* @param nanos the timeout, in nanoseconds438* @throws IllegalBlockingModeException if the channel is configured non-blocking439* @throws SocketTimeoutException if the timeout expires440*/441SocketChannel blockingAccept(long nanos) throws IOException {442int n = 0;443FileDescriptor newfd = new FileDescriptor();444SocketAddress[] saa = new SocketAddress[1];445446acceptLock.lock();447try {448// check that channel is configured blocking449if (!isBlocking())450throw new IllegalBlockingModeException();451452try {453begin(true);454// change socket to non-blocking455lockedConfigureBlocking(false);456try {457long startNanos = System.nanoTime();458n = implAccept(fd, newfd, saa);459while (n == IOStatus.UNAVAILABLE && isOpen()) {460long remainingNanos = nanos - (System.nanoTime() - startNanos);461if (remainingNanos <= 0) {462throw new SocketTimeoutException("Accept timed out");463}464park(Net.POLLIN, remainingNanos);465n = implAccept(fd, newfd, saa);466}467} finally {468// restore socket to blocking mode (if channel is open)469tryLockedConfigureBlocking(true);470}471} finally {472end(true, n > 0);473}474} finally {475acceptLock.unlock();476}477478assert n > 0;479return finishAccept(newfd, saa[0]);480}481482private SocketChannel finishAccept(FileDescriptor newfd, SocketAddress sa)483throws IOException484{485try {486// newly accepted socket is initially in blocking mode487IOUtil.configureBlocking(newfd, true);488489// check permitted to accept connections from the remote address490if (isNetSocket()) {491@SuppressWarnings("removal")492SecurityManager sm = System.getSecurityManager();493if (sm != null) {494InetSocketAddress isa = (InetSocketAddress) sa;495sm.checkAccept(isa.getAddress().getHostAddress(), isa.getPort());496}497}498return new SocketChannelImpl(provider(), family, newfd, sa);499} catch (Exception e) {500nd.close(newfd);501throw e;502}503}504505@Override506protected void implConfigureBlocking(boolean block) throws IOException {507acceptLock.lock();508try {509lockedConfigureBlocking(block);510} finally {511acceptLock.unlock();512}513}514515/**516* Adjust the blocking. acceptLock must already be held.517*/518private void lockedConfigureBlocking(boolean block) throws IOException {519assert acceptLock.isHeldByCurrentThread();520synchronized (stateLock) {521ensureOpen();522IOUtil.configureBlocking(fd, block);523}524}525526/**527* Adjusts the blocking mode if the channel is open. acceptLock must already528* be held.529*530* @return {@code true} if the blocking mode was adjusted, {@code false} if531* the blocking mode was not adjusted because the channel is closed532*/533private boolean tryLockedConfigureBlocking(boolean block) throws IOException {534assert acceptLock.isHeldByCurrentThread();535synchronized (stateLock) {536if (isOpen()) {537IOUtil.configureBlocking(fd, block);538return true;539} else {540return false;541}542}543}544545/**546* Closes the socket if there are no accept in progress and the channel is547* not registered with a Selector.548*/549private boolean tryClose() throws IOException {550assert Thread.holdsLock(stateLock) && state == ST_CLOSING;551if ((thread == 0) && !isRegistered()) {552state = ST_CLOSED;553nd.close(fd);554return true;555} else {556return false;557}558}559560/**561* Invokes tryClose to attempt to close the socket.562*563* This method is used for deferred closing by I/O and Selector operations.564*/565private void tryFinishClose() {566try {567tryClose();568} catch (IOException ignore) { }569}570571/**572* Closes this channel when configured in blocking mode.573*574* If there is an accept in progress then the socket is pre-closed and the575* accept thread is signalled, in which case the final close is deferred576* until the accept aborts.577*/578private void implCloseBlockingMode() throws IOException {579synchronized (stateLock) {580assert state < ST_CLOSING;581state = ST_CLOSING;582if (!tryClose()) {583long th = thread;584if (th != 0) {585nd.preClose(fd);586NativeThread.signal(th);587}588}589}590}591592/**593* Closes this channel when configured in non-blocking mode.594*595* If the channel is registered with a Selector then the close is deferred596* until the channel is flushed from all Selectors.597*/598private void implCloseNonBlockingMode() throws IOException {599synchronized (stateLock) {600assert state < ST_CLOSING;601state = ST_CLOSING;602}603// wait for any accept to complete before trying to close604acceptLock.lock();605acceptLock.unlock();606synchronized (stateLock) {607if (state == ST_CLOSING) {608tryClose();609}610}611}612613/**614* Invoked by implCloseChannel to close the channel.615*/616@Override617protected void implCloseSelectableChannel() throws IOException {618assert !isOpen();619if (isBlocking()) {620implCloseBlockingMode();621} else {622implCloseNonBlockingMode();623}624}625626@Override627public void kill() {628synchronized (stateLock) {629if (state == ST_CLOSING) {630tryFinishClose();631}632}633}634635/**636* Returns true if channel's socket is bound637*/638boolean isBound() {639synchronized (stateLock) {640return localAddress != null;641}642}643644/**645* Returns the local address, or null if not bound646*/647SocketAddress localAddress() {648synchronized (stateLock) {649return localAddress;650}651}652653/**654* Translates native poll revent set into a ready operation set655*/656public boolean translateReadyOps(int ops, int initialOps, SelectionKeyImpl ski) {657int intOps = ski.nioInterestOps();658int oldOps = ski.nioReadyOps();659int newOps = initialOps;660661if ((ops & Net.POLLNVAL) != 0) {662// This should only happen if this channel is pre-closed while a663// selection operation is in progress664// ## Throw an error if this channel has not been pre-closed665return false;666}667668if ((ops & (Net.POLLERR | Net.POLLHUP)) != 0) {669newOps = intOps;670ski.nioReadyOps(newOps);671return (newOps & ~oldOps) != 0;672}673674if (((ops & Net.POLLIN) != 0) &&675((intOps & SelectionKey.OP_ACCEPT) != 0))676newOps |= SelectionKey.OP_ACCEPT;677678ski.nioReadyOps(newOps);679return (newOps & ~oldOps) != 0;680}681682public boolean translateAndUpdateReadyOps(int ops, SelectionKeyImpl ski) {683return translateReadyOps(ops, ski.nioReadyOps(), ski);684}685686public boolean translateAndSetReadyOps(int ops, SelectionKeyImpl ski) {687return translateReadyOps(ops, 0, ski);688}689690/**691* Translates an interest operation set into a native poll event set692*/693public int translateInterestOps(int ops) {694int newOps = 0;695if ((ops & SelectionKey.OP_ACCEPT) != 0)696newOps |= Net.POLLIN;697return newOps;698}699700public FileDescriptor getFD() {701return fd;702}703704public int getFDVal() {705return fdVal;706}707708public String toString() {709StringBuilder sb = new StringBuilder();710sb.append(this.getClass().getName());711sb.append('[');712if (!isOpen()) {713sb.append("closed");714} else {715synchronized (stateLock) {716SocketAddress addr = localAddress;717if (addr == null) {718sb.append("unbound");719} else if (isUnixSocket()) {720sb.append(UnixDomainSockets.getRevealedLocalAddressAsString(addr));721} else {722sb.append(Net.getRevealedLocalAddressAsString(addr));723}724}725}726sb.append(']');727return sb.toString();728}729}730731732