java.nio.channels.SocketChannel

1. SocketChannel的父子继承关系

在这里插入图片描述

2. SocketChannel功能

从SocketChannel类的注释可以看到以下几点:
1). socket channel是面向流的连接socket的一个可选通道。
2). 通过调用#open()方法创建socket channel。根据不同参数,为不同的socket创建channel。
3). socket channel支持非阻塞连接。创建套接字通道,和建立到远程套接字的链接的过程可以通过#connect方法初始化,再由#finishConnect方法完成。
4). socket channel支持异步关闭,类似于在Channel类中指定的异步关闭操作。
5). socket channel对于多个并发线程来说是安全的。如果方法正在执行,读或写的操作会被阻塞。

/**
 * A selectable channel for stream-oriented connecting sockets.
 * socket channel是面向流的连接套接字的一个可选通道。
 *
 * <p> A socket channel is created by invoking one of the {@code open} methods of
 * this class. The no-arg {@link #open() open} method opens a socket channel
 * for an <i>Internet protocol</i> socket. The {@link #open(ProtocolFamily)}
 * method is used to open a 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 socket channel is open but not yet connected.  An
 * attempt to invoke an I/O operation upon an unconnected channel will cause a
 * {@link NotYetConnectedException} to be thrown.  A socket channel can be
 * connected by invoking its {@link #connect connect} method; once connected,
 * a socket channel remains connected until it is closed.  Whether or not a
 * socket channel is connected may be determined by invoking its {@link #isConnected()
 * isConnected} method.
 * 通过调用#open()方法创建socket channel
 * #open()无参方法给网络协议socket创建socket channel
 * #open(ProtocolFamily)方法被用来去给一个特殊的协议族的socket去创建一个socket channel
 * 不能去给一个任意预先存在的socket创建channel
 * 一个新创建的socket channel 是open状态,但是还没有连接
 * 在还没有连接的channel上尝试去调用I/O操作会抛出一个NotYetConnectedException异常
 * socket channel可以通过调用#connect()方法来进行连接
 * 一旦连接上,socket channel就会维持链接状态知道它被关闭
 * socket channel 是否处于连接状态,可以通过调用#isConnected()方法判断。
 *
 * <p> Socket channels support <i>non-blocking connection:</i>&nbsp;A socket
 * channel may be created and the process of establishing the link to the
 * remote socket may be initiated via the {@link #connect connect} method for
 * later completion by the {@link #finishConnect finishConnect} method.
 * Whether or not a connection operation is in progress may be determined by
 * invoking the {@link #isConnectionPending isConnectionPending} method.
 * socket channel支持非阻塞连接
 * 创建套接字通道,和建立到远程套接字的链接的过程可以通过#connect方法初始化,再由#finishConnect方法完成。
 * 连接操作是否正在进行可以通过调用#isConnectionPending方法来确定。
 *
 * <p> Socket channels support <i>asynchronous shutdown</i>, which is similar
 * to the asynchronous close operation specified in the {@link Channel} class.
 * If the input side of a socket is shut down by one thread while another
 * thread is blocked in a read operation on the socket's channel, then the read
 * operation in the blocked thread will complete without reading any bytes and
 * will return {@code -1}.  If the output side of a socket is shut down by one
 * thread while another thread is blocked in a write operation on the socket's
 * channel, then the blocked thread will receive an {@link
 * AsynchronousCloseException}.
 * socket channel支持异步关闭,类似于在Channel类中指定的异步关闭操作。
 *
 * <p> Socket options are configured using the {@link #setOption(SocketOption,Object)
 * setOption} method. Socket channels for  <i>Internet protocol</i> sockets support
 * 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_SNDBUF SO_SNDBUF} </th>
 *     <td> The size of the socket send buffer </td>
 *   </tr>
 *   <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_KEEPALIVE SO_KEEPALIVE} </th>
 *     <td> Keep connection alive </td>
 *   </tr>
 *   <tr>
 *     <th scope="row"> {@link java.net.StandardSocketOptions#SO_REUSEADDR SO_REUSEADDR} </th>
 *     <td> Re-use address </td>
 *   </tr>
 *   <tr>
 *     <th scope="row"> {@link java.net.StandardSocketOptions#SO_LINGER SO_LINGER} </th>
 *     <td> Linger on close if data is present (when configured in blocking mode
 *          only) </td>
 *   </tr>
 *   <tr>
 *     <th scope="row"> {@link java.net.StandardSocketOptions#TCP_NODELAY TCP_NODELAY} </th>
 *     <td> Disable the Nagle algorithm </td>
 *   </tr>
 * </tbody>
 * </table>
 * </blockquote>
 *
 * <p> 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_SNDBUF SO_SNDBUF} </th>
 *     <td> The size of the socket send buffer </td>
 *   </tr>
 *   <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_LINGER SO_LINGER} </th>
 *     <td> Linger on close if data is present (when configured in blocking mode
 *          only) </td>
 *   </tr>
 * </tbody>
 * </table>
 * </blockquote>
 *
 * <p> Additional (implementation specific) options may also be supported.
 *
 * <p> Socket channels are safe for use by multiple concurrent threads.  They
 * support concurrent reading and writing, though at most one thread may be
 * reading and at most one thread may be writing at any given time.  The {@link
 * #connect connect} and {@link #finishConnect finishConnect} methods are
 * mutually synchronized against each other, and an attempt to initiate a read
 * or write operation while an invocation of one of these methods is in
 * progress will block until that invocation is complete.  </p>
 * socket channel对于多个并发线程来说是安全的。
 * 尽管在任何给定的时间内最多有一个线程在读,最多有一个线程在写。但是他们仍然支持并发读和并发写。
 * #connect()方法#finishConnect()方法是相互同步的,
 * 当尝试调用正在执行的#connect()方法#finishConnect()方法时,读或写的操作都会阻塞,直到这个调用完成。
 *
 * @author Mark Reinhold
 * @author JSR-51 Expert Group
 * @since 1.4
 */

public abstract class SocketChannel
    extends AbstractSelectableChannel
    implements ByteChannel, ScatteringByteChannel, GatheringByteChannel, NetworkChannel 
{
	......
}

3. SocketChannel提供的方法

在这里插入图片描述

1). open() 方法

open()方法实现逻辑:
(1). 新的SocketChannel通过调用系统默认的java.nio.channels.spi.SelectorProvider对象的openSocketChannel方法创建的
(2). 第一次获取SelectorProvider对象按照一下顺序:
  a. 查看系统属性java.nio.channels.spi.selectorprovider是否存在,存在即它将当作为具体provider类的完全限定名。
  b. 如果一个provider类已经打包安装在一个jar文件中,同时这个jar文件对系统类加载器是可见的,并且这个jar文件在它的资源目录META-INF/services中包含一个名为java.nio.channels.spi.SelectorProvider的provider配置文件,然后取该文件中指定的第一个类名。
  c. 最后,如果在上面没有指定provider,系统默认的provider类就会实例化。
(3). 调用SocketChannelImpl.openSocketChannel()方法,new SocketChannelImpl(this),对自身进行实例构造。

    /**
     * Opens a socket channel.
     *
     * <p> The new channel is created by invoking the {@link
     * java.nio.channels.spi.SelectorProvider#openSocketChannel
     * openSocketChannel} method of the system-wide default {@link
     * java.nio.channels.spi.SelectorProvider} object.  </p>
     * 新的channel通过调用系统默认的java.nio.channels.spi.SelectorProvider对象的openSocketChannel方法创建的
     *
     * @return  A new socket channel
     *
     * @throws  IOException
     *          If an I/O error occurs
     */
    public static SocketChannel open() throws IOException {
        return SelectorProvider.provider().openSocketChannel();
    }
/**
     * Returns the system-wide default selector provider for this invocation of
     * the Java virtual machine.
     * 对这次虚拟机的调用,返回一个系统范围内默认的selcetor provider
     *
     * <p> The first invocation of this method locates the default provider
     * object as follows: </p>
     * 第一次调用这个方法的时候会按照下列次序定位默认的provider对象
     *
     * <ol>
     * 
     *   <li><p> If the system property
     *   <tt>java.nio.channels.spi.SelectorProvider</tt> is defined then it is
     *   taken to be the fully-qualified name of a concrete provider class.
     *   The class is loaded and instantiated; if this process fails then an
     *   unspecified error is thrown.  </p></li>
     *   如果定义了系统属性java.nio.channels.spi.selectorprovider,那么它将当作为具体provider类的完全限定名。
     *   加载并且实例化这个类;如果执行失败了,就抛出未指定的错误。
     *
     *   <li><p> If a provider class has been installed in a jar file that is
     *   visible to the system class loader, and that jar file contains a
     *   provider-configuration file named
     *   <tt>java.nio.channels.spi.SelectorProvider</tt> in the resource
     *   directory <tt>META-INF/services</tt>, then the first class name
     *   specified in that file is taken.  The class is loaded and
     *   instantiated; if this process fails then an unspecified error is
     *   thrown.  </p></li>
     *   如果一个provider类已经打包安装在一个jar文件中,同时这个jar文件对系统类加载器是可见的,
     *   并且这个jar文件在它的资源目录META-INF/services中包含一个名为java.nio.channels.spi.SelectorProvider的provider配置文件,
     *   然后取该文件中指定的第一个类名。加载并且实例化这个类;如果执行失败了,就抛出未指定的错误。
     *
     *   <li><p> Finally, if no provider has been specified by any of the above
     *   means then the system-default provider class is instantiated and the
     *   result is returned.  </p></li>
     * 	 最后,如果在上面没有指定provider,系统默认的provider类就会实例化,然后返回	
     *
     * </ol>
     *
     * <p> Subsequent invocations of this method return the provider that was
     * returned by the first invocation.  </p>
     * 之后调用这个方法返回的provider就是第一次调用返回的那个provider
     *
     * @return  The system-wide default selector provider
     */
    public static SelectorProvider provider() {
        synchronized (lock) {
            if (provider != null)
                return provider;
            return AccessController.doPrivileged(
                new PrivilegedAction<SelectorProvider>() {
                    public SelectorProvider run() {
                            if (loadProviderFromProperty())
                                return provider;
                            if (loadProviderAsService())
                                return provider;
                            provider = sun.nio.ch.DefaultSelectorProvider.create();
                            return provider;
                        }
                    });
        }
    }
    public SocketChannel openSocketChannel() throws IOException {
        return new SocketChannelImpl(this);
    }

2). bind()方法

bind()方法实现逻辑:
(1). SocketChannel抽象类将bind()方法重写为抽象方法,采用模版方法的设计模式要求子类必须实现

    @Override
    public abstract SocketChannel bind(SocketAddress local)
        throws IOException
        @Override

(2). SocketChannelImpl类对bind()方法的实现

	// Lock held by current reading or connecting thread
    private final ReentrantLock readLock = new ReentrantLock();
    // Lock held by current writing or connecting thread
    private final ReentrantLock writeLock = new ReentrantLock();
    
    public SocketChannel bind(SocketAddress local) throws IOException {
    	// 上读锁
        readLock.lock();
        try {
        	// 上写锁
            writeLock.lock();
            try {
                synchronized (stateLock) {
                    ensureOpen();
                    if (state == ST_CONNECTIONPENDING)
                        throw new ConnectionPendingException();
                    if (localAddress != null)
                        throw new AlreadyBoundException();
                    if (isUnixSocket()) {
                        localAddress = unixBind(local);
                    } else {
                    	// 网络绑定
                        localAddress = netBind(local);
                    }
                }
            } finally {
                writeLock.unlock();
            }
        } finally {
            readLock.unlock();
        }
        return this;
    }
    
    private SocketAddress netBind(SocketAddress local) throws IOException {
        InetSocketAddress isa;
        if (local == null) {
            isa = new InetSocketAddress(Net.anyLocalAddress(family), 0);
        } else {
        	// 对SocketAddress进行地址检查
            isa = Net.checkAddress(local, family);
        }
        @SuppressWarnings("removal")
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkListen(isa.getPort());
        }
        NetHooks.beforeTcpBind(fd, isa.getAddress(), isa.getPort());
        // 地址绑定
        Net.bind(family, fd, isa.getAddress(), isa.getPort());
        return Net.localAddress(fd);
    }

(3). 进行地址绑定

    static void bind(ProtocolFamily family, FileDescriptor fd,
                     InetAddress addr, int port) throws IOException
    {
        boolean preferIPv6 = isIPv6Available() &&
            (family != StandardProtocolFamily.INET);
        if (addr.isLinkLocalAddress()) {
            addr = IPAddressUtil.toScopedAddress(addr);
        }
        bind0(fd, preferIPv6, exclusiveBind, addr, port);
    }

	// native方法
    private static native void bind0(FileDescriptor fd, boolean preferIPv6,
                                     boolean useExclBind, InetAddress addr,
                                     int port)
        throws IOException;
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值