java.nio.channels.ServerSocketChannel
1.ServerSocketChannel的父子继承关系
2. ServerSocketChannel注释
1). 调用#open()方法创建server-socket channel
2). 在还未绑定的server-socket channel上尝试去调用#accept()方法会抛出一个NotYetBoundException异常
3). server-socket channel可以通过调用这个类定义的#bind(java.net.SocketAddress local, int backlog)方法来进行绑定
4). Server-socket channels在多个并发线程使用时是安全的
/**
* A selectable channel for stream-oriented listening sockets.
* ServerSocketChannel是给面向流的监听socket的一个可选通道
*
* <p> A server-socket channel is created by invoking one of the {@code open}
* methods of this class. The no-arg {@link #open() open} method opens a server-socket
* channel for an <i>Internet protocol</i> socket. The {@link #open(ProtocolFamily)}
* method is used to open a server-socket channel for a socket of a specified
* protocol family. It is not possible to create a channel for an arbitrary,
* pre-existing socket. A newly-created server-socket channel is open but not yet
* bound. An attempt to invoke the {@link #accept() accept} method of an
* unbound server-socket channel will cause a {@link NotYetBoundException}
* to be thrown. A server-socket channel can be bound by invoking one of the
* {@link #bind(java.net.SocketAddress, int) bind} methods defined by this class.
* 通过调用这个类的#open()方法创建server-socket channel
* #open()无参方法给网络协议socket创建server-socket channel
* #open(ProtocolFamily)方法被用来去给一个指定协议族的socket去创建一个server-socket channel
* 不能去给一个任意预先存在的socket创建channel
* 一个新创建的server-socket channel是open状态,但是还没有进行绑定
* 在还未绑定的server-socket channel上尝试去调用#accept()方法会抛出一个NotYetBoundException异常
* server-socket channel可以通过调用这个类定义的#bind(java.net.SocketAddress local, int backlog)方法来进行绑定
*
* <p> Socket options are configured using the {@link #setOption(SocketOption,Object)
* setOption} method. Server-socket channels for <i>Internet protocol</i> sockets
* support the following options:
* <blockquote>
* <table class="striped">
* <caption style="display:none">Socket options</caption>
* <thead>
* <tr>
* <th scope="col">Option Name</th>
* <th scope="col">Description</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <th scope="row"> {@link java.net.StandardSocketOptions#SO_RCVBUF SO_RCVBUF} </th>
* <td> The size of the socket receive buffer </td>
* </tr>
* <tr>
* <th scope="row"> {@link java.net.StandardSocketOptions#SO_REUSEADDR SO_REUSEADDR} </th>
* <td> Re-use address </td>
* </tr>
* </tbody>
* </table>
* </blockquote>
*
* <p> Server-socket channels for <i>Unix domain</i> sockets support:
* <blockquote>
* <table class="striped">
* <caption style="display:none">Socket options</caption>
* <thead>
* <tr>
* <th scope="col">Option Name</th>
* <th scope="col">Description</th>
* </tr>
* </thead>
* <tbody>
* <tr>
* <th scope="row"> {@link java.net.StandardSocketOptions#SO_RCVBUF SO_RCVBUF} </th>
* <td> The size of the socket receive buffer </td>
* </tr>
* </tbody>
* </table>
* </blockquote>
*
* <p> Additional (implementation specific) options may also be supported.
*
* <p> Server-socket channels are safe for use by multiple concurrent threads.
* Server-socket channels在多个并发线程使用时是安全的
* </p>
*
* @author Mark Reinhold
* @author JSR-51 Expert Group
* @since 1.4
*/
public abstract class ServerSocketChannel
extends AbstractSelectableChannel
implements NetworkChannel
{
}
3. ServerSocketChannel提供的方法
1). bind(SocketAddress, int)
/**
* Binds the channel's socket to a local address and configures the socket to
* listen for connections.
*
* <p> This method is used to establish an association between the socket and
* a local address. For <i>Internet protocol</i> sockets, once an association
* is established then the socket remains bound until the channel is closed.
*
* <p> The {@code backlog} parameter is the maximum number of pending
* connections on the socket. Its exact semantics are implementation specific.
* In particular, an implementation may impose a maximum length or may choose
* to ignore the parameter altogether. If the {@code backlog} parameter has
* the value {@code 0}, or a negative value, then an implementation specific
* default is used.
*
* @apiNote
* Binding a server socket channel for a <i>Unix Domain</i> socket, creates a
* file corresponding to the file path in the {@link UnixDomainSocketAddress}.
* This file persists after the channel is closed, and must be removed before
* another socket can bind to the same name. Binding to a {@code null} address
* causes the socket to be <i>automatically</i> bound to some unique file
* in a system temporary location. The associated socket file also persists
* after the channel is closed. Its name can be obtained from the channel's
* local socket address.
*
* @implNote
* Each platform enforces an implementation specific, maximum length for the
* name of a <i>Unix Domain</i> socket. This limitation is enforced when a
* channel is bound. The maximum length is typically close to and generally
* not less than 100 bytes. This limitation also applies to <i>automatically</i>
* bound server socket channels. See the <i>Unix domain</i>
* <a href="../../net/doc-files/net-properties.html#Unixdomain">networking
* properties</a> that can be used to select the temporary directory where
* these sockets are created.
*
* @param local
* The address to bind the socket, or {@code null} to bind to
* an automatically assigned socket address
* @param backlog
* The maximum number of pending connections
*
* @return This channel
*
* @since 1.7
*/
public abstract ServerSocketChannel bind(SocketAddress local, int backlog)
throws IOException;
2). open()
/**
* Opens a server-socket channel for an <i>Internet protocol</i> socket.
*
* <p> The new channel is created by invoking the {@link
* java.nio.channels.spi.SelectorProvider#openServerSocketChannel
* openServerSocketChannel} method of the system-wide default {@link
* java.nio.channels.spi.SelectorProvider} object.
*
* <p> The new channel's socket is initially unbound; it must be bound to a
* specific address via one of its socket's {@link
* java.net.ServerSocket#bind(SocketAddress) bind} methods before
* connections can be accepted. </p>
*
* @return A new socket channel
*/
public static ServerSocketChannel open() throws IOException {
return SelectorProvider.provider().openServerSocketChannel();
}
3). accept()
(1). 如果这个channel处于non-blocking非阻塞模式,并且如果没有挂起的连接,那么这个方法就会立即返回null
(2). 如果这个channel处于blocking阻塞模式,他就会一直阻塞,直到可以获得一个新的连接,或者发生了I/O错误。
(3). 无论这个channel的阻塞模式是什么样的,这个方法返回的SocketChannel都会是阻塞模式
(4). 根据UNIX socket或者INET socket进行不同的安全检查
/**
* Accepts a connection made to this channel's socket.
* 给这个channel的socket接收一个连接
*
* <p> If this channel is in non-blocking mode then this method will
* immediately return {@code null} if there are no pending connections.
* Otherwise it will block indefinitely until a new connection is available
* or an I/O error occurs.
* 如果这个channel处于non-blocking非阻塞模式,并且如果没有挂起的连接,那么这个方法就会立即返回null
* 否则他就会一直阻塞,直到可以获得一个新的连接,或者发生了I/O错误。
*
* <p> The socket channel returned by this method, if any, will be in
* blocking mode regardless of the blocking mode of this channel.
* 无论这个channel的阻塞模式是什么样的,这个方法返回的socket channel都会是阻塞模式
*
* <p> If bound to an <i>Internet protocol</i> socket address, this method
* performs exactly the same security checks as the {@link
* java.net.ServerSocket#accept accept} method of the {@link java.net.ServerSocket}
* class. That is, if a security manager has been installed then for each
* new connection this method verifies that the address and port number
* of the connection's remote endpoint are permitted by the security
* manager's {@link java.lang.SecurityManager#checkAccept checkAccept}
* method. If bound to a <i>Unix Domain</i> socket address, this method checks
* {@link NetPermission}{@code ("accessUnixDomainSocket")}.
* 如果绑定到一个网络协议socket地址,这个方法会和java.net.ServerSocket类的accept方法一样,也会进行合适的安全检查
* 如果一个安全管理已经安装了,那么对于每一个新的连接,此方法将验证安全管理器的checkAccept方法是否允许连接的远程端点的地址和端口号。
* 如果绑定到一个Unix Domain socket地址,这个方法会通过accessUnixDomainSocket检查网络许可。
*
* @return The socket channel for the new connection,
* or {@code null} if this channel is in non-blocking mode
* and no connection is available to be accepted
*/
public abstract SocketChannel accept() throws IOException;
ServerSocketChannelImpl为accpet()方法提供了实现
// Lock held by thread currently blocked on this channel
private final ReentrantLock acceptLock = new ReentrantLock();
@Override
public SocketChannel accept() throws IOException {
int n = 0;
// 文件描述符
FileDescriptor newfd = new FileDescriptor();
// socket地址
SocketAddress[] saa = new SocketAddress[1];
acceptLock.lock();
try {
// 阻塞模式 blocking = true
boolean blocking = isBlocking();
try {
begin(blocking);
n = implAccept(this.fd, newfd, saa);
if (blocking) {
while (IOStatus.okayToRetry(n) && isOpen()) {
park(Net.POLLIN);
n = implAccept(this.fd, newfd, saa);
}
}
} finally {
end(blocking, n > 0);
assert IOStatus.check(n);
}
} finally {
acceptLock.unlock();
}
if (n > 0) {
return finishAccept(newfd, saa[0]);
} else {
return null;
}
}
/**
* Marks the beginning of an I/O operation that might block.
*
* @throws ClosedChannelException if the channel is closed
* @throws NotYetBoundException if the channel's socket has not been bound yet
*/
private void begin(boolean blocking) throws ClosedChannelException {
if (blocking)
begin(); // set blocker to close channel if interrupted
synchronized (stateLock) {
ensureOpen();
if (localAddress == null)
throw new NotYetBoundException();
if (blocking)
thread = NativeThread.current();
}
}
private int implAccept(FileDescriptor fd, FileDescriptor newfd, SocketAddress[] saa)
throws IOException
{
if (isUnixSocket()) {
UnixDomainSockets.checkPermission();
String[] pa = new String[1];
int n = UnixDomainSockets.accept(fd, newfd, pa);
if (n > 0)
saa[0] = UnixDomainSocketAddress.of(pa[0]);
return n;
} else {
InetSocketAddress[] issa = new InetSocketAddress[1];
int n = Net.accept(fd, newfd, issa);
if (n > 0)
saa[0] = issa[0];
return n;
}
}
private SocketChannel finishAccept(FileDescriptor newfd, SocketAddress sa)
throws IOException
{
try {
// newly accepted socket is initially in blocking mode
IOUtil.configureBlocking(newfd, true);
// check permitted to accept connections from the remote address
if (isNetSocket()) {
@SuppressWarnings("removal")
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
InetSocketAddress isa = (InetSocketAddress) sa;
sm.checkAccept(isa.getAddress().getHostAddress(), isa.getPort());
}
}
return new SocketChannelImpl(provider(), family, newfd, sa);
} catch (Exception e) {
nd.close(newfd);
throw e;
}
}
java.nio.channels.spi.AbstractSelectableChannel#isBlocking()方法默认实现
// Blocking mode, protected by regLock
boolean blocking = true;
public final boolean isBlocking() {
synchronized (regLock) {
return blocking;
}
}
java.nio.channels.spi.AbstractSelectableChannel#begin()方法默认实现如下:
/**
* Marks the beginning of an I/O operation that might block indefinitely.
*
* <p> This method should be invoked in tandem with the {@link #end end}
* method, using a {@code try} ... {@code finally} block as
* shown <a href="#be">above</a>, in order to implement asynchronous
* closing and interruption for this channel. </p>
*/
protected final void begin() {
if (interruptor == null) {
interruptor = new Interruptible() {
public void interrupt(Thread target) {
synchronized (closeLock) {
if (closed)
return;
closed = true;
interrupted = target;
try {
AbstractInterruptibleChannel.this.implCloseChannel();
} catch (IOException x) { }
}
}};
}
blockedOn(interruptor);
Thread me = Thread.currentThread();
if (me.isInterrupted())
interruptor.interrupt(me);
}