版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_36381640/article/details/82660462
package com.gsau.NIO; 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.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.Iterator; /** * @Description: 创建NIOServer * @Date: 2018/9/12 13:16 * @author: wgq * @version: 1.0 * @param: */ public class NIOServer { private Selector selector; // 通道管理器 /** * @Description: 获得一个通道并且做一些初始化的工作(为他注册一个Selector) * @Date: 2018/9/12 13:17 * @author: wgq * @version: 1.0 * @param: */ public void initServer(int port) throws IOException { ServerSocketChannel serverChannel = ServerSocketChannel.open(); // 获得一个ServerSocket通道,相当于传统socket中的ServerSocket serverChannel.configureBlocking(false); // 设置通道为非阻塞 serverChannel.socket().bind(new InetSocketAddress(port)); // 将该通道对应的ServerSocket绑定到port端口,ip默认就是本机的ip this.selector = Selector.open(); // 获得一个通道管理器 serverChannel.register(selector, SelectionKey.OP_ACCEPT); // 将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_ACCEPT事件,注册该事件后,当该事件到达时selector.select()会返回,如果该事件没到达selector.select()会一直阻塞 } /** * @Description: 采用轮询的方式监听selector上是否有需要处理的事件,如果有,则进行处理 * @Date: 2018/9/12 13:19 * @author: wgq * @version: 1.0 * @param: */ public void listen() throws IOException { System.out.println("服务端启动成功!"); while (true) { // 轮询访问selector selector.select(); // 当注册的事件到达时,方法返回;否则,该方法会一直阻塞,当一个事件到达的时候他会自动监听到 Iterator<?> ite = this.selector.selectedKeys().iterator(); // 获得selector中选中的项的迭代器,选中的项为注册的事件 while (ite.hasNext()) { SelectionKey key = (SelectionKey) ite.next(); //获取选中的KEY ite.remove(); // 删除已选的key,以防重复处理 handler(key); } } } /** * @Description: 处理请求的句柄 * @Date: 2018/9/12 13:19 * @author: wgq * @version: 1.0 * @param: */ public void handler(SelectionKey key) throws IOException { if (key.isAcceptable()) { // 客户端请求连接事件 handlerAccept(key); } else if (key.isReadable()) { // 获得可读的事件 handelerRead(key); } } /** * @Description: 处理连接的请求 * @Date: 2018/9/12 13:22 * @author: wgq * @version: 1.0 * @param: */ public void handlerAccept(SelectionKey key) throws IOException { ServerSocketChannel server = (ServerSocketChannel) key.channel(); //打开一个通道和客户端进行交互 SocketChannel channel = server.accept(); // 获得和客户端连接的通道 channel.configureBlocking(false); // 设置成非阻塞 System.out.println("新的客户端连接"); // 在这里可以给客户端发送信息哦 channel.register(this.selector, SelectionKey.OP_READ); // 在和客户端连接成功之后,为了可以接收到客户端的信息,需要给通道设置读的权限。 } /** * @Description: 处理读的事件 * ByteBuffer:ByteBuffer的读写模式是分开的 * 创建方式: * 1.ByteBuffer buffer=ByteBuffer.allocate(256); * 2.ByteBuffer buffer=ByteBuffer.wrap(byteArray);这里的byteArray可以包含了数据,相当于写入了数据到缓冲区 * 写入数据: * 1.ByteBuffer buffer=ByteBuffer.wrap(byteArray) * 2.buffer.put(bytes) * 清除缓存区: * buffer.clear(); 这个方法实际上也不会改变缓冲区的数据,而只是简单的重置了缓冲区的主要索引值,不必为了每次读写都创建新的缓冲区,那样做会降低性能。 * 相反,要重用现在的缓冲区,在再次读取之前要清除缓冲区。 * 读取数据: * 调用buffer.get(bytes); * @Date: 2018/9/12 13:20 * @author: wgq * @version: 1.0 * @param: */ public void handelerRead(SelectionKey key) throws IOException { SocketChannel channel = (SocketChannel) key.channel(); // 服务器可读取消息:得到事件发生的Socket通道(其实就是这个请求对应的通道) ByteBuffer buffer = ByteBuffer.allocate(1024); // 创建读取的缓冲区 int read = channel.read(buffer); if (read > 0) { byte[] data = buffer.array(); String msg = new String(data).trim(); System.out.println("服务端收到信息:" + msg); ByteBuffer outBuffer = ByteBuffer.wrap("好的".getBytes()); //回写数据 channel.write(outBuffer); // 将消息回送给客户端 } else { System.out.println("客户端关闭"); key.cancel(); } } /** * @Description: 启动服务端进行测试 * @Date: 2018/9/12 13:26 * @author: wgq * @version: 1.0 * @param: */ public static void main(String[] args) throws IOException { NIOServer server = new NIOServer(); server.initServer(8000); server.listen(); } }