java_nio编程(二)
概述
在 Java 中编写 Socket 服务器, 通常有以下几种模式 :
1、一个客户端对应一个线程:如果连接非常多,分配的线程也会非常多,服务器可能会扛不住 2、线程池:线程池开销较大,排队现象严重 3、nio 非阻塞,一个线程可处理多个客户端
主要类
Selector
(选择器) ,能够检测多个注册的通道上是否有事件发生, 如果有事件发生, 便获取事件然后针对每个事件进行相应的处理。 这样就可以只用一个单线程去管理多个通道, 也就是管理多个连接。这样使得只有在连接真正有读写事件发生时,才会调用函数来进行读写,就大大地减少了系统开销, 并且不必为每个连接都创建一个线程, 不用去维护多个线程, 并且避免了多线程之间的上下文切换导致的开销
public abstract class Selector implements Closeable {}
/**
* Closes this selector.
*
* <p> If a thread is currently blocked in one of this selector's selection
* methods then it is interrupted as if by invoking the selector's {@link
* #wakeup wakeup} method.
*
* <p> Any uncancelled keys still associated with this selector are
* invalidated, their channels are deregistered, and any other resources
* associated with this selector are released.
*
* <p> If this selector is already closed then invoking this method has no
* effect.
*
* <p> After a selector is closed, any further attempt to use it, except by
* invoking this method or the {@link #wakeup wakeup} method, will cause a
* {@link ClosedSelectorException} to be thrown. </p>
*
* @throws IOException
* If an I/O error occurs
*/
它是一个抽象类,并且实现了Closeable的接口,实现了close的方法,并在注释中写道,Closes this selector,关闭这个选择器,并且这个Closeable是实现了如下的自动关闭的接口。
public interface Closeable extends AutoCloseable{}
常用方法如下所示:
得到一个选择器对象
public static Selector open()
监控所有注册的通道, 当其中有 IO 操作可以进行时, 将对应的 SelectionKey 加入到内部集合中并返回, 参数用来设置超时时间,主要是用衡量是否有客户端连接服务器端
public int select(long timeout)
从内部集合中得到所有的 SelectionKey,
public Set<SelectionKey> selectedKeys();
例如:Iterator<SelectionKey> keyIterator = selector.selectedKeys().iterator();
SelectionKey
该类是一个抽象类,表示selectableChannel在Selector中注册的标识.每个Channel向Selector注册时,都将会创建一个selectionKey,同时也代表了 Selector 和网络通道的注册关系,一共四种:
int OP_ACCEPT: 有新的网络连接可以 accept, 值为 16 int OP_CONNECT: 代表连接已经建立, 值为 8 int OP_READ :代表了读操作,值为 1 int OP_WRITE:代表 写操作, 值为 4
常用方法如下所示:
得到与之关联的 Selector 对象
public abstract Selector selector()
例如:Selector selector = key.selector();
得到与之关联的通道
public abstract SelectableChannel channel()
例如:Channel targetChannel = key.channel();
得到与之关联的共享数据
public final Object attachment()
设置或改变监听事件
public abstract SelectionKey interestOps(int ops),
是否可以 accept
public final boolean isAcceptable()
例如:
if (key.isAcceptable()) { //连接请求事件
SocketChannel sc = listenerChannel.accept();
sc.configureBlocking(false);
sc.register(selector, SelectionKey.OP_READ);
//得到客户端地址和端口号
System.out.println(sc.getRemoteAddress().toString().substring(1) + "上线了...");
}
是否可以读
public final boolean isReadable()
例如:
if (key.isReadable()) { //读取数据事件
readMsg(key);
}
是否可以写
public final boolean isWritable()
ServerSocketChannel
用来在服务器端监听新的客户端 Socket 连接
常用方法如下所示:
public static ServerSocketChannel open(), 得到一个 ServerSocketChannel 通道 public final ServerSocketChannel bind(SocketAddress local), 设置服务器端端口号 public final SelectableChannel configureBlocking(boolean block), 设置阻塞或非阻塞模式,取值 false 表示采用非阻塞模式 public SocketChannel accept(), 接受一个连接, 返回代表这个连接的通道对象 public final SelectionKey register(Selector sel, int ops), 注册一个选择器并设置监听事件
SocketChannel
网络 IO 通道, 具体负责进行读写操作。 NIO 总是把缓冲区的数据写入通道, 或者把通道里的数据读到缓冲区
public static SocketChannel open(), 得到一个 SocketChannel 通道 public final SelectableChannel configureBlocking(boolean block), 设置阻塞或非阻塞模式,取值 false 表示采用非阻塞模式 public boolean connect(SocketAddress remote), 连接服务器 public boolean finishConnect(), 如果上面的方法连接失败, 接下来就要通过该方法完成连接操作 public int write(ByteBuffer src), 往通道里写数据 public int read(ByteBuffer dst), 从通道里读数据 public final SelectionKey register(Selector sel, int ops, Object att), 注册一个选择器并设置监听事件, 最后一个参数可以设置共享数据 public final void close(), 关闭通道
附图:

selector帮助ServerSocketChannel监控SocketChannel上的各种事件;

浙公网安备 33010602011771号