Nio

一、Nio服务端程序启动和运行的详细过程:

1、打开一个serverSocketChannel(相当于ServerSocket),并绑定监听地址,设置连接为非阻塞
  servChannel = ServerSocketChannel.open();//建立通道
  servChannel.socket().bind(new InetSocketAddress(port), 1024);//通道绑定端口
  servChannel.configureBlocking(false);//设置通道非阻塞
2、创建多路复用器selector,将serverSocketChannel注册到selector监听accept事件
(其实就是把serverSocketChannel生成的文件描述符FD和selector绑定在一起以方便以后使用通道进行数据读写)
  selector = Selector.open();//创建selector
  servChannel.register(selector, SelectionKey.OP_ACCEPT);//通道和selector绑定,监听accept事件
3、selector每隔一秒无限轮询准备就绪的key(客户端准备就绪可以连接服务端就向selector注册一个准备就绪的key)
  selector.select(1000);//selector的等待时间是1s
  Set<SelectionKey> selectedKeys = selector.selectedKeys();//查询出就绪的key
  Iterator<SelectionKey> it = selectedKeys.iterator();
  SelectionKey key = null;
  while (it.hasNext()) {//遍历所有的key
    key = it.next();
    it.remove();
    try {
      //处理新的接入请求
    } catch (Exception e) {
    //处理异常就要处理掉这个key并关闭通道,客户端重新连接时需要重新注册key
    if (key != null) {
      key.cancel();
      if (key.channel() != null)
        key.channel().close();
      }
    }
  }
4、selector监听到有新连接请求,则要处理新的连接请求,设置连接为非阻塞,并把socketChannel向selector注册,监听读操作
  if (key.isAcceptable()) {
    //处理新的接入请求
    ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
    SocketChannel sc = ssc.accept();
    sc.configureBlocking(false);
    //把socketChannel向selector注册,监听读操作
    sc.register(selector, SelectionKey.OP_READ);
  }
5、selector监听到有数据可读,就从该key对应的socketChannel中读取数据
  if (key.isReadable()) {
    //读数据
    SocketChannel sc = (SocketChannel) key.channel();
    ByteBuffer readBuffer = ByteBuffer.allocate(1024);//设置buffer的块大小
    int readBytes = sc.read(readBuffer);
    if (readBytes > 0) {
      readBuffer.flip();
      byte[] bytes = new byte[readBuffer.remaining()];//处理读半包问题
      readBuffer.get(bytes);
      String body = new String(bytes, "UTF-8");
      System.out.println("The time server receive order : "+ body);
      String currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body) ? new java.util.Date(
        System.currentTimeMillis()).toString() : "BAD ORDER";
      doWrite(sc, currentTime);//服务端给客户端返回的结果
    } else if (readBytes < 0){//readBytes=-1

      //对端链路关闭
      key.cancel();
      sc.close();
    }
  }
6、服务端向客户端写回的结果
private void doWrite(SocketChannel channel, String response)
throws IOException {
if (response != null && response.trim().length() > 0) {
byte[] bytes = response.getBytes();
ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);
writeBuffer.put(bytes);
writeBuffer.flip();
channel.write(writeBuffer);
}
}

二、Nio客户端程序启动和运行的详细过程:
1、和服务端类似客户端需要有selector多路复用器和socketChannel
selector = Selector.open();//创建selector
socketChannel = SocketChannel.open();//创建客户端的socketChannel
socketChannel.configureBlocking(false);//设置通道非阻塞

2、连接服务端,
// 如果直接连接成功,则注册到多路复用器上,发送请求消息,读应答
if (socketChannel.connect(new InetSocketAddress(host, port))) {
socketChannel.register(selector, SelectionKey.OP_READ);//连接成功,注册到selector监听读数据
doWrite(socketChannel);
} else
socketChannel.register(selector, SelectionKey.OP_CONNECT);//连接不成功,注册到selector监听连接
}

3、selector每隔一秒无限轮询准备就绪的key
selector.select(1000);
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> it = selectedKeys.iterator();
SelectionKey key = null;
while (it.hasNext()) {
key = it.next();
it.remove();
try {
//处理和服务端新建立的连接
} catch (Exception e) {
//处理异常就要处理掉这个key并关闭通道,客户端重新连接时需要重新注册key
if (key != null) {
key.cancel();
if (key.channel() != null)
key.channel().close();
}
}
}

4、判读key如果连接成功则注册读监听,如果可读就从Channel中读数据
if (key.isValid()) {
// 判断是否连接成功
SocketChannel sc = (SocketChannel) key.channel();
if (key.isConnectable()) {
if (sc.finishConnect()) {
sc.register(selector, SelectionKey.OP_READ);
doWrite(sc);
} else
System.exit(1);// 连接失败,进程退出
}
if (key.isReadable()) {
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
int readBytes = sc.read(readBuffer);
if (readBytes > 0) {
readBuffer.flip();
byte[] bytes = new byte[readBuffer.remaining()];
readBuffer.get(bytes);
String body = new String(bytes, "UTF-8");
System.out.println("Now is : " + body);
this.stop = true;
} else if (readBytes < 0) {
// 对端链路关闭
key.cancel();
sc.close();
}
}
}
5、客户端向服务端发送数据
private void doWrite(SocketChannel sc) throws IOException {
byte[] req = "QUERY TIME ORDER".getBytes();
ByteBuffer writeBuffer = ByteBuffer.allocate(req.length);
writeBuffer.put(req);
writeBuffer.flip();
sc.write(writeBuffer);
if (!writeBuffer.hasRemaining())
System.out.println("Send order 2 server succeed.");
}


补充:
//多路复用器关闭后,所有注册在上面的Channel和Pipe等资源都会被自动去注册并关闭
selector.close();

---恢复内容结束---

posted @ 2016-04-10 16:43  扳掘de  阅读(151)  评论(0编辑  收藏  举报