在使用java.io和Socket构建网络服务时,所有的网络服务将使用阻塞式方式进行客户端连接。而使用java.nio则可以构建一个非阻塞式的网络服务
一、Selector类
主要方法:
打开一个选择器。通过调用系统级默认 SelectorProvider 对象的 openSelector 方法来创建新的选择器。
选择一组键,其相应的通道已为 I/O 操作准备就绪。此方法执行处于阻塞模式的选择操作。仅在至少选择一个通道、调用此选择器的 wakeup 方法,或者当前的线程已中断(以先到者为准)后此方法才返回。 public abstract Set<SelectionKey> selectedKeys() 返回此选择器的已选择键集。可从已选择键集中移除键,但是无法直接添加键。试图向该键集中添加对象会导致抛出 UnsupportedOperationException。已选择键集是非线程安全的。
在进行非阻塞网络开发需使用SelectableChannel类向Select注册,而在NIO中实现网络程序需依靠ServerSocketChannel类与SocketChannel类,它们都是SelectableChannel的子类,SelectableChannel提供了注册Selector的方法和阻塞模式。
ServerSocketChannel 的主要方法:
public final SelectionKey register(Selector sel,int ops) throws ClosedChannelException
向给定的选择器注册此通道,返回一个选择键。 public abstract SelectableChannel configureBlocking(boolean block)throws IOException 调整此通道的阻塞模式。如果向一个或多个选择器注册了此通道,则尝试将此通道置于阻塞模式将导致抛出 IllegalBlockingModeException。可在任意时间调用此方法。新的阻塞模式仅影响在此方法返回后发起的 I/O 操作。对于某些实现而言,这可能需要在所有挂起的 I/O 操作完成之前阻塞其他操作。
如果调用此方法的同时正在进行另一个此方法或 register 方法的调用,则在另一个操作完成前将首先阻塞该调用。
参数block - 如果为 true,则此通道将被置于阻塞模式;如果为 false,则此通道将被置于非阻塞模式,
返回值:此可选择通道
public static ServerSocketChannel open() throws IOException 打开服务器套接字通道。通过调用系统级默认 SelectorProvider 对象的 openServerSocketChannel 方法来创建新的通道。 新通道的套接字最初是未绑定的;可以接受连接之前,必须通过它的某个套接字的 bind 方法将其绑定到具体的地址。
public abstract ServerSocket socket() 获取与此通道关联的服务器套接字。4种Selector域(来源 JAVA API)
如果要向客户端发送信息,则许通过SelectionKey类的方法判断服务器操作状态,儿想去的刻画段的连接也需要使用SelectionKey类例如:利用Selector创建一个非阻塞的服务器,此服务器向客户端返回当前时间
package com;import java.net.InetSocketAddress ;import java.net.ServerSocket ;import java.util.Set ;import java.util.Iterator ;import java.util.Date ;import java.nio.channels.ServerSocketChannel ;import java.nio.ByteBuffer ;import java.nio.channels.SocketChannel ;import java.nio.channels.Selector ;import java.nio.channels.SelectionKey ;public class DateServer{ public static void main(String args[]) throws Exception { // 表示五个监听端口 int ports[] = {8000,8001,8002,8003,8005,8006} ; // 通过open()方法找到Selector Selector selector = Selector.open() ; for(int i=0;i<ports.length;i++){ ServerSocketChannel initSer = null ; // 打开服务器的通道 initSer = ServerSocketChannel.open() ; // 服务器配置为非阻塞 initSer.configureBlocking(false) ; ServerSocket initSock = initSer.socket() ; InetSocketAddress address = null ; // 实例化绑定地址 address = new InetSocketAddress(ports) ; // 进行服务的绑定 initSock.bind(address) ; // 等待连接 initSer.register(selector,SelectionKey.OP_ACCEPT) ; System.out.println("服务器运行,在" + ports + "端口监听。") ; } // 要接收全部生成的key,并通过连接进行判断是否获取客户端的输出 int keysAdd = 0 ; // 选择一组键,并且相应的通道已经准备就绪 while((keysAdd=selector.select())>0){ // 取出全部生成的key Set<SelectionKey> selectedKeys = selector.selectedKeys() ; Iterator<SelectionKey> iter = selectedKeys.iterator() ; while(iter.hasNext()){ // 取出每一个key SelectionKey key = iter.next() ; if(key.isAcceptable()){ ServerSocketChannel server = (ServerSocketChannel)key.channel() ; // 接收新连接 SocketChannel client = server.accept() ; // 配置为非阻塞 client.configureBlocking(false) ; ByteBuffer outBuf = ByteBuffer.allocateDirect(1024) ; // 向缓冲区中设置内容 outBuf.put(("当前的时间为:" + new Date()).getBytes()) ; outBuf.flip() ; // 输出内容 Thread.sleep(3000); client.write(outBuf) ; // 关闭 client.close() ; } } // 清楚全部的key selectedKeys.clear() ; } }}
然后用telnet客户端测试。
|
|