Java Nio Buffer 十三

2018/01/15 Java

Java Nio Buffer 十三

Socket 通道

Socket 通道有与文件通道不同的特征。 socket 通道类可以运行非阻塞模式并且是可选择的。这两个性能可以激活大程序(如网络服务器和中间件组件)巨大的可伸缩性和灵活性。再也没有为每个 socket 连接使用一个线程的必要了,也避免了管理大量线程所需的上下文交换总开销。借助新的 NIO 类,一个或几个线程就可以管理成百上千的活动 socket 连接了并且只有很少甚至可能没有性能损失。

Socket 通道类的类图如下: 图一

全部 socket 通道类(DatagramChannel、SocketChannel 和 ServerSocketChannel)在被实例化时 都会创建一个对等 socket 对象。这些是我们所熟悉的来自 java.net 的类(Socket、ServerSocket 和 DatagramSocket),它们已经被更新以识别通道。对等 socket 可以通过调用 socket( )方法从一个 通道上获取。此外,这三个 java.net 类现在都有 getChannel( )方法。虽然每个 socket 通道(在 java.nio.channels 包中)都有一个关联的 java.net socket 对 象,却并非所有的 socket 都有一个关联的通道。如果用传统方式(直接实例化)创建了一个 Socket 对象,它就不会有关联的 SocketChannel 并且它的 getChannel( )方法将总是返回 null。

Socket 通道委派协议操作给对等 socket 对象。如果在通道类中存在似乎重复的 socket 方法,那 么将有某个新的或者不同的行为同通道类上的这个方法相关联。

非阻塞模式

Socket 通道可以在非阻塞模式下运行。这个陈述虽然简单却有着深远的含义。传统 Java socket 的阻塞性质曾经是 Java 程序可伸缩性的最重要制约之一。非阻塞 I/O 是许多复杂的、高性能的程序构建的基础。
要把一个 socket 通道置于非阻塞模式,要依靠所有 socket 通道类的公有类: SelectableChannel。下面的方法就是关于通道的阻塞模式的:


public abstract class SelectableChannel
    extends AbstractChannel
    implements Channel
{
    // This is a partial API listing
    public abstract void configureBlocking (boolean block)
        throws IOException;
    public abstract boolean isBlocking( );
    public abstract Object blockingLock( );
}

有条件的选择(readiness selection)是一种可以用来查询通道的机制,该查询可以判断通道是 否准备好执行一个目标操作,如读或写。非阻塞 I/O 和可选择性是紧密相连的,那也正是管理阻塞模式的 API 代码要在 SelectableChannel 类中定义的原因。SelectableChannel 的剩余 API 将在第 四章中讨论。

设置或重新设置一个通道的阻塞模式是很简单的,只要调用 configureBlocking( )方法即可,传 递参数值为 true 则设为阻塞模式,参数值为 false 值设为非阻塞模式。

可以通过调用 isBlocking( )方法来判断某个 socket 通道当前处于哪种模式。

服务器端的使用经常会考虑到非阻塞 socket 通道,因为它们使同时管理很多 socket 通道变得更 容易。但是,在客户端使用一个或几个非阻塞模式的 socket 通道也是有益处的,例如,借助非阻塞 socket 通道,GUI 程序可以专注于用户请求并且同时维护与一个或多个服务器的会话。在很多程序 上,非阻塞模式都是有用的。

我们也会需要防止 socket 通道的阻塞模式被更改。API 中有一个 blockingLock( )方 法,该方法会返回一个非透明的对象引用。返回的对象是通道实现修改阻塞模式时内部使用的。只 有拥有此对象的锁的线程才能更改通道的阻塞模式(对象的锁是用同步的 Java 密码获取的)。

代码片段示例如下:

Socket socket = null;
Object lockObj = serverChannel.blockingLock( );
// have a handle to the lock object, but haven't locked it yet
// may block here until lock is acquired
synchronize (lockObj)
{
    // This thread now owns the lock; mode can't be changed
    boolean prevState = serverChannel.isBlocking( );
    serverChannel.configureBlocking (false);
    socket = serverChannel.accept( );
    serverChannel.configureBlocking (prevState);
}
// lock is now released, mode is allowed to change
if (socket != null) {
    doSomethingWithTheSocket (socket);
}

ServerSocketChannel

ServerSocketChannel 的完整 API:

public abstract class ServerSocketChannel
    extends AbstractSelectableChannel
{
    public static ServerSocketChannel open( ) throws IOException
    public abstract ServerSocket socket( );
    public abstract ServerSocket accept( ) throws IOException;
    public final int validOps( )
}

ServerSocketChannel 是一个基于通道的 socket 监听器。它同我们所熟悉的 java.net.ServerSocket 执行相同的基本任务,不过它增加了通道语义,因此能够在非阻塞模式下运行。

用静态的 open( )工厂方法创建一个新的 ServerSocketChannel 对象,将会返回同一个未绑定的 java.net.ServerSocket 关联的通道。该对等 ServerSocket 可以通过在返回的 ServerSocketChannel 上调 用 socket( )方法来获取。作为 ServerSocketChannel 的对等体被创建的 ServerSocket 对象依赖通道实 现。这些 socket 关联的 SocketImpl 能识别通道。通道不能被封装在随意的 socket 对象外面。

由于 ServerSocketChannel 没有 bind( )方法,因此有必要取出对等的 socket 并使用它来绑定到一个端口以开始监听连接。我们也是使用对等 ServerSocket 的 API 来根据需要设置其他的 socket 选项。

示例代码如下:

ServerSocketChannel ssc = ServerSocketChannel.open( );
ServerSocket serverSocket = ssc.socket( );
// Listen on port 1234
serverSocket.bind (new InetSocketAddress (1234));
Show Disqus Comments

Search

    Table of Contents