NIO-关于的网络通信:
1、会 将通道上的关注的事件注册到Selector管家上。
2、然后Selector会返回一个SelectionKey,方便后面进行轮询的。
3、将key自动加入keys的集合中去。
4、关注事件的发生。
5、做完下面的工作以后会专门select()返回,返回一个key,然后去执行对应的事件。
6、然后轮询处理看里面有没有处理相关的事情。做完以后会继续轮询。
只能SelectedKeys当中的一个key会触发事件。读的同时不能去写。
7、当做完的时候从selectedKeys中将它移除掉,如果不移除会重复的去执行这样的事件。
在程序中:首先看server端的代码如下:
import java.io.IOException;import java.net.InetSocketAddress;import java.net.ServerSocket;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.util.Iterator;import java.util.Set;/*** 服务端接收客户端连接事件 SelectionKey.OP_ACCEPT(16)* 客户端连接服务端事件 SelectionKey.OP_CONNECT(8)* 读事件 SelectionKey.OP_READ(1)* 写事件 SelectionKey.OP_WRITE(4)*(2)NIO实现原理* 服务端和客户端各自维护一个管理通道的对象,我们称之为selector,* 该对象能检测一个或多个通道 (channel) 上的事件。我们以服务端为例,* 如果服务端的selector上注册了读事件,某时刻客户端给服务端发送了一些数据,* 阻塞I/O这时会调用read()方法阻塞地读取数据,* 而NIO的服务端会在selector中添加一个读事件。服务端的处理线程会轮询地访问selector,* 如果访问selector时发现有感兴趣的事件到达,则处理这些事件,如果没有感兴趣的事件到达,* 则处理线程会一直阻塞直到感兴趣的事件到达为止。*/public class NIOServer {/*标识数字*/private int flag = 0;/*缓冲区大小*/private int BLOCK = 4096;/*接受数据缓冲区*/private ByteBuffer sendbuffer = ByteBuffer.allocate(BLOCK);/*发送数据缓冲区*/private ByteBuffer receivebuffer = ByteBuffer.allocate(BLOCK);private Selector selector;public NIOServer(int port) throws IOException {// 打开服务器套接字通道ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();// 服务器配置为非阻塞,默认为阻塞的。serverSocketChannel.configureBlocking(false);// 检索与此通道关联的服务器套接字ServerSocket serverSocket = serverSocketChannel.socket();// 进行服务的绑定serverSocket.bind(new InetSocketAddress(port));// 通过open()方法找到Selectorselector = Selector.open();// 注册到selector,等待连接serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);System.out.println("Server Start----8888:");}// 监听private void listen() throws IOException {// 轮询IO事件while (true) {System.out.println("=====");// 选择一组键,并且相应的通道已经打开//此方法执行处于阻塞模式的选择操作。selector.select();System.out.println("===+++==");// 返回此选择器的已选择键集。Set<SelectionKey> selectionKeys = selector.selectedKeys();Iterator<SelectionKey> iterator = selectionKeys.iterator();while (iterator.hasNext()) {SelectionKey selectionKey = iterator.next();iterator.remove();handleKey(selectionKey);}}}// 处理请求private void handleKey(SelectionKey selectionKey) throws IOException {// 接受请求ServerSocketChannel server = null;SocketChannel client = null;String receiveText;String sendText;int count=0;// 测试此键的通道是否已准备好接受新的套接字连接。if (selectionKey.isAcceptable()) {System.out.println("_++++++");// 返回为之创建此键的通道。server = (ServerSocketChannel) selectionKey.channel();// 接受到此通道套接字的连接。// 此方法返回的套接字通道(如果有)将处于阻塞模式。client = server.accept();System.out.println("=============");// 配置为非阻塞client.configureBlocking(false);// 注册到selector,等待连接client.register(selector, SelectionKey.OP_READ);} else if (selectionKey.isReadable()) {// 返回为之创建此键的通道。client = (SocketChannel) selectionKey.channel();//将缓冲区清空以备下次读取receivebuffer.clear();//读取服务器发送来的数据到缓冲区中count = client.read(receivebuffer);if (count > 0) {receiveText = new String( receivebuffer.array(),0,count);System.out.println("服务器端接受客户端数据--:"+receiveText);client.register(selector, SelectionKey.OP_WRITE);}} else if (selectionKey.isWritable()) {//将缓冲区清空以备下次写入sendbuffer.clear();// 返回为之创建此键的通道。client = (SocketChannel) selectionKey.channel();sendText="message from server--" + flag++;//向缓冲区中输入数据sendbuffer.put(sendText.getBytes());//将缓冲区各标志复位,因为向里面put了数据标志被改变要想从中读取数据发向服务器,就要复位sendbuffer.flip();//输出到通道client.write(sendbuffer);System.out.println("服务器端向客户端发送数据--:"+sendText);client.register(selector, SelectionKey.OP_READ);}}/*** @param args* @throws IOException*/public static void main(String[] args) throws IOException {int port = 8888;NIOServer server = new NIOServer(port);server.listen();}}
运行的结果如下:
客户端的代码如下:
import java.io.IOException;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.util.Iterator;import java.util.Set;public class NIOClient {/*标识数字*/private static int flag = 0;/*缓冲区大小*/private static int BLOCK = 4096;/*接受数据缓冲区*/private static ByteBuffer sendbuffer = ByteBuffer.allocate(BLOCK);/*发送数据缓冲区*/private static ByteBuffer receivebuffer = ByteBuffer.allocate(BLOCK);/*服务器端地址*/private final static InetSocketAddress SERVER_ADDRESS = new InetSocketAddress("localhost", 8888);public static void main(String[] args) throws IOException {// 打开socket通道SocketChannel socketChannel = SocketChannel.open();// 设置为非阻塞方式socketChannel.configureBlocking(false);// 打开选择器Selector selector = Selector.open();// 注册连接服务端socket动作socketChannel.register(selector, SelectionKey.OP_CONNECT);// 连接socketChannel.connect(SERVER_ADDRESS);// 分配缓冲区大小内存Set<SelectionKey> selectionKeys;Iterator<SelectionKey> iterator;SelectionKey selectionKey;SocketChannel client;String receiveText;String sendText;int count=0;while (true) {//选择一组键,其相应的通道已为 I/O 操作准备就绪。//此方法执行处于阻塞模式的选择操作。selector.select();//返回此选择器的已选择键集。selectionKeys = selector.selectedKeys();//System.out.println(selectionKeys.size());iterator = selectionKeys.iterator();while (iterator.hasNext()) {selectionKey = iterator.next();if (selectionKey.isConnectable()) {System.out.println("client connect");client = (SocketChannel) selectionKey.channel();// 判断此通道上是否正在进行连接操作。// 完成套接字通道的连接过程。if (client.isConnectionPending()) {client.finishConnect();System.out.println("完成连接!");sendbuffer.clear();sendbuffer.put("Hello,Server".getBytes());sendbuffer.flip();client.write(sendbuffer);}client.register(selector, SelectionKey.OP_READ);} else if (selectionKey.isReadable()) {client = (SocketChannel) selectionKey.channel();//将缓冲区清空以备下次读取receivebuffer.clear();//读取服务器发送来的数据到缓冲区中count=client.read(receivebuffer);if(count>0){receiveText = new String( receivebuffer.array(),0,count);System.out.println("客户端接受服务器端数据--:"+receiveText);client.register(selector, SelectionKey.OP_WRITE);}} else if (selectionKey.isWritable()) {sendbuffer.clear();client = (SocketChannel) selectionKey.channel();sendText = "message from client--" + (flag++);sendbuffer.put(sendText.getBytes());//将缓冲区各标志复位,因为向里面put了数据标志被改变要想从中读取数据发向服务器,就要复位sendbuffer.flip();client.write(sendbuffer);System.out.println("客户端向服务器端发送数据--:"+sendText);client.register(selector, SelectionKey.OP_READ);}}selectionKeys.clear();}}}
运行的结果如下:一直循环和打印相应的数据.