NIO监听网络通信

来源:互联网 发布:中超球员数据 编辑:程序博客网 时间:2024/05/16 10:20

NIO中比较重要的有:Buffer,ByteBuffer(要了解此类的postion、limit等)、chanel(SelectableChannel、ServerSocketChannel、SocketChanel)和Selector、SelectorKey,注意SelectorKey的种类ServerSocketChannel比SocketChanel多一个accept,其他三个为connect、read和write,因为ServerSocketChannel对应于旧IO中的ServerSocket,SocketChannel对应于旧IO中的Socket。DataProgramChannel与SocketChanel类似。


下面是一个Server和Client的例子,用来使用NIO进行通信。简单的说,就是创建好对象channel,接着在channel上注册好监听事件SelectorKey,然后调用channel的select()得到所有的Key的set,然后iterate该set根据具体的Key类型进行处理,基本上Key分为accept, read 和write,注意在指定方法处理key之前要将此key从interator中删除, accept就是server接受client的请求,read和write是读写byteBuffer。注意的是*Buffer有两个方法比较重要clear和flip,前者是清空,后者是紧实,因为*Buffer在创建的时候指定了大小,而每次读写实际上Buffer中的东东可能未满,就要通过flip来紧实一下喽,即在读Buffer中的数据之前都要进行一下flip, 如果这个buffer还要重用则就要进行clear


import java.io.IOException;import java.net.InetAddress;import java.net.InetSocketAddress;import java.nio.ByteBuffer;import java.nio.channels.SelectionKey;import java.nio.channels.Selector;import java.nio.channels.ServerSocketChannel;import java.nio.channels.SocketChannel;import java.nio.charset.Charset;import java.util.Date;import java.util.Iterator;import java.util.Set;public class Server{private final Selector selector;private final ServerSocketChannel serverSocketChannel;public Server(int port) throws IOException{// 创建选择器selector = Selector.open();// 打开监听信道serverSocketChannel = ServerSocketChannel.open();InetSocketAddress adress = new InetSocketAddress(InetAddress.getLocalHost(),port);//与本地端口绑定serverSocketChannel.socket().bind(adress);// 设置为非阻塞模式serverSocketChannel.configureBlocking(false);// 注册选择器.并在注册过程中指出该信道可以进行Accept操作serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT);}public void start() {System.out.println("the server is started......");while (true) {try {int nKeys = selector.select();if (nKeys > 0){// selectedKeys()中包含了每个准备好某一I/O操作的信道的SelectionKeySet<SelectionKey> scSet = selector.selectedKeys();Iterator<SelectionKey> iter = scSet.iterator();while (iter.hasNext()) {SelectionKey key = (SelectionKey) iter.next();iter.remove();dispatch(key);}}} catch (IOException e) {e.printStackTrace();}}}public void dispatch(SelectionKey key) {// 有客户端连接请求时//if ((key.readyOps() & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT) {if (key.isAcceptable()) {try {System.out.println("Key is acceptable");ServerSocketChannel ssc = (ServerSocketChannel) key.channel();SocketChannel socket = (SocketChannel) ssc.accept();socket.configureBlocking(false);socket.register(selector, SelectionKey.OP_READ);} catch (IOException e) {e.printStackTrace();}}// 从客户端读取数据//else if ((key.readyOps() & SelectionKey.OP_READ) == SelectionKey.OP_READ) {else if (key.isReadable()) {System.out.println("the key is readable");// new Thread(new ReadeHandler(key)).start();try {SocketChannel socket = (SocketChannel) key.channel();ByteBuffer buffer = ByteBuffer.allocate(1024);int bytesRead = socket.read(buffer);if (bytesRead > 0) {buffer.flip();// 将字节转化为为UTF-16的字符串String receivedString = Charset.forName("UTF-16").newDecoder().decode(buffer).toString();// 控制台打印出来System.out.println("接收到来自"+ socket.socket().getRemoteSocketAddress()+ "的信息:" + receivedString);// 准备发送的文本String sendString = "你好,客户端. @"+ new Date().toString() + ",已经收到你的信息:"+ receivedString;buffer = ByteBuffer.wrap(sendString.getBytes("UTF-16"));socket.write(buffer);// 设置为下一次读取或是写入做准备key.interestOps(SelectionKey.OP_READ);}} catch (IOException e) {//客户端断开连接,所以从Selector中取消注册 key.cancel();if(key.channel() != null)try {key.channel().close();System.out.println("the client socket is closed!");} catch (IOException e1) {e1.printStackTrace();}}}// 客户端可写时else if (key.isWritable()) {    System.out.println("tHe key is writable");    //new Thread(new WriteHandler(key)).start();    //do something}}public static void main(String[] args) throws IOException {Server server = new Server(9911);server.start();}}


import java.io.IOException;import java.net.InetAddress;import java.net.InetSocketAddress;import java.nio.ByteBuffer;import java.nio.channels.SelectionKey;import java.nio.channels.Selector;import java.nio.channels.SocketChannel;import java.nio.charset.Charset;public class Client {// 信道选择器private Selector selector;// 与服务器通信的信道SocketChannel socketChannel;public Client(int port)throws IOException{selector = Selector.open();socketChannel = SocketChannel.open(new InetSocketAddress(InetAddress.getLocalHost(),port));    socketChannel.configureBlocking(false);    socketChannel.register(selector, SelectionKey.OP_READ);    // 启动读取线程    new Thread(new ClientReadThread()).start();}//发送字符串到服务器public void sendMsg(String message) throws IOException{ByteBuffer writeBuffer = ByteBuffer.wrap(message.getBytes("UTF-16"));    socketChannel.write(writeBuffer);}public static void main(String[] args) throws IOException{    Client client = new Client(9911);        client.sendMsg("你好!Nio!");}class ClientReadThread implements Runnable{public void run() {    try {      while (selector.select() > 0) {        // 遍历每个有可用IO操作Channel对应的SelectionKey        for (SelectionKey sk : selector.selectedKeys()) {                    // 如果该SelectionKey对应的Channel中有可读的数据          if (sk.isReadable()) {            // 使用NIO读取Channel中的数据            SocketChannel sc = (SocketChannel) sk.channel();            ByteBuffer buffer = ByteBuffer.allocate(1024);            sc.read(buffer);            buffer.flip();                        // 将字节转化为为UTF-16的字符串               String receivedString = Charset.forName("UTF-16").newDecoder().decode(buffer).toString();                        // 控制台打印出来            System.out.println("接收到来自服务器" + sc.socket().getRemoteSocketAddress() + "的信息:" + receivedString);            // 为下一次读取作准备            sk.interestOps(SelectionKey.OP_READ);          }          // 删除正在处理的SelectionKey          selector.selectedKeys().remove(sk);        }      }    } catch (IOException ex) {      ex.printStackTrace();    }   }}}


原创粉丝点击