1/* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18package java.nio; 19 20import java.io.FileDescriptor; 21import java.io.IOException; 22import java.net.PlainServerSocketImpl; 23import java.net.ServerSocket; 24import java.net.Socket; 25import java.net.SocketAddress; 26import java.net.SocketImpl; 27import java.net.SocketTimeoutException; 28import java.nio.channels.ClosedChannelException; 29import java.nio.channels.IllegalBlockingModeException; 30import java.nio.channels.NotYetBoundException; 31import java.nio.channels.ServerSocketChannel; 32import java.nio.channels.SocketChannel; 33import java.nio.channels.spi.SelectorProvider; 34import libcore.io.ErrnoException; 35import libcore.io.IoUtils; 36import static libcore.io.OsConstants.*; 37 38/** 39 * The default ServerSocketChannel. 40 */ 41final class ServerSocketChannelImpl extends ServerSocketChannel implements FileDescriptorChannel { 42 43 private final ServerSocketAdapter socket; 44 private final SocketImpl impl; 45 46 private boolean isBound = false; 47 48 private final Object acceptLock = new Object(); 49 50 public ServerSocketChannelImpl(SelectorProvider sp) throws IOException { 51 super(sp); 52 this.socket = new ServerSocketAdapter(this); 53 this.impl = socket.getImpl$(); 54 } 55 56 @Override public ServerSocket socket() { 57 return socket; 58 } 59 60 @Override public SocketChannel accept() throws IOException { 61 if (!isOpen()) { 62 throw new ClosedChannelException(); 63 } 64 if (!isBound) { 65 throw new NotYetBoundException(); 66 } 67 68 // Create an empty socket channel. This will be populated by ServerSocketAdapter.accept. 69 SocketChannelImpl result = new SocketChannelImpl(provider(), false); 70 try { 71 begin(); 72 synchronized (acceptLock) { 73 try { 74 socket.implAccept(result); 75 } catch (SocketTimeoutException e) { 76 if (shouldThrowSocketTimeoutExceptionFromAccept(e)) { 77 throw e; 78 } 79 // Otherwise, this is a non-blocking socket and there's nothing ready, so we'll 80 // fall through and return null. 81 } 82 } 83 } finally { 84 end(result.socket().isConnected()); 85 } 86 return result.socket().isConnected() ? result : null; 87 } 88 89 private boolean shouldThrowSocketTimeoutExceptionFromAccept(SocketTimeoutException e) { 90 if (isBlocking()) { 91 return true; 92 } 93 Throwable cause = e.getCause(); 94 if (cause instanceof ErrnoException) { 95 if (((ErrnoException) cause).errno == EAGAIN) { 96 return false; 97 } 98 } 99 return true; 100 } 101 102 @Override protected void implConfigureBlocking(boolean blocking) throws IOException { 103 IoUtils.setBlocking(impl.getFD$(), blocking); 104 } 105 106 synchronized protected void implCloseSelectableChannel() throws IOException { 107 if (!socket.isClosed()) { 108 socket.close(); 109 } 110 } 111 112 public FileDescriptor getFD() { 113 return impl.getFD$(); 114 } 115 116 private static class ServerSocketAdapter extends ServerSocket { 117 private final ServerSocketChannelImpl channelImpl; 118 119 ServerSocketAdapter(ServerSocketChannelImpl aChannelImpl) throws IOException { 120 this.channelImpl = aChannelImpl; 121 } 122 123 @Override public void bind(SocketAddress localAddress, int backlog) throws IOException { 124 super.bind(localAddress, backlog); 125 channelImpl.isBound = true; 126 } 127 128 @Override public Socket accept() throws IOException { 129 if (!channelImpl.isBound) { 130 throw new IllegalBlockingModeException(); 131 } 132 SocketChannel sc = channelImpl.accept(); 133 if (sc == null) { 134 throw new IllegalBlockingModeException(); 135 } 136 return sc.socket(); 137 } 138 139 public Socket implAccept(SocketChannelImpl clientSocketChannel) throws IOException { 140 Socket clientSocket = clientSocketChannel.socket(); 141 boolean connectOK = false; 142 try { 143 synchronized (this) { 144 super.implAccept(clientSocket); 145 clientSocketChannel.setConnected(); 146 clientSocketChannel.setBound(true); 147 clientSocketChannel.finishAccept(); 148 } 149 connectOK = true; 150 } finally { 151 if (!connectOK) { 152 clientSocket.close(); 153 } 154 } 155 return clientSocket; 156 } 157 158 @Override public ServerSocketChannel getChannel() { 159 return channelImpl; 160 } 161 162 @Override public boolean isBound() { 163 return channelImpl.isBound; 164 } 165 166 @Override public void bind(SocketAddress localAddress) throws IOException { 167 super.bind(localAddress); 168 channelImpl.isBound = true; 169 } 170 171 @Override public void close() throws IOException { 172 synchronized (channelImpl) { 173 if (channelImpl.isOpen()) { 174 channelImpl.close(); 175 } else { 176 super.close(); 177 } 178 } 179 } 180 } 181} 182