package com; 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.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.Set; /** * * 使用NIO完成聊天室服务端 * MINA框架(等NIO进一步了解深入后,可以多了解MINA) */ public class NIDServer { public void start() { try { //存放对应所有客户端的SocketChannel,用于广播消息 List<SocketChannel> allChannel = new ArrayList<SocketChannel>(); //创建ServerSocketChannel ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); //ServerSocketChannel默认是阻塞通道,我们可改为非阻塞通道 serverSocketChannel.configureBlocking(false);//设置为非阻塞 configure设置 //为ServerSocketChannel 绑定端口,客户端通过该端口进行连接 serverSocketChannel.bind(new InetSocketAddress(8088)); /* 创建多路选择器 这个是NIO事项非阻塞的关键API,用于监控多路设备的事件,并可以做出响应 使得单个线程仅需要轮询多路选择器就可以对多个设备做出事件处理 */ Selector selector = Selector.open(); /* 将ServerSocketChannel注册到多路选择器上,让其监控是否有客户端连接的事件 解放了原来需要让主线程调用serverSocket.accept()这里阻塞的情况 register:注册 */ serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); //让主线程一直关注选择器是否有事件需要处理了 while (true) { //当选择器发现注册在它上面的某些通道有事件时,该方法立即返回,否则会阻塞 selector.select(); //处理事件 // 通过选择器获取目前所有事件的通道 Set<SelectionKey> keySet = selector.selectedKeys(); // 遍历并处理每一个事件 for (SelectionKey key : keySet) { //判断该事件是否为可接受客户端连接的(ServerSocketChannel注册的OP_ACCEPT所响应的事件) if (key.isAcceptable()) { //获取该事件对应通道 ServerSocketChannel ssc = (ServerSocketChannel) key.channel(); //“接电话” SocketChannel socketChannel = ssc.accept(); //非阻塞的ServerSocketChannel的accept方法可能返回null if (socketChannel == null) {//null就忽略 continue; } //将SocketChannel设置为非阻塞式的 socketChannel.configureBlocking(false); //将SocketChannel注册到多路选择器上,关心的事件为:有数据可读取(该客户端发消息过来) socketChannel.register(selector, SelectionKey.OP_READ); //将该SocketChannel存入allChannel集合便于广播消息 allChannel.add(socketChannel); //通过SocketChannel获取远端计算机地址信息并输出 System.out.println(socketChannel.socket().getInetAddress().getHostAddress()+"上线了,当前在线人数:"+ +allChannel.size()); //如果该事件是表示有消息可读(该事件一定是一个SocketChannel响应的) //allocate : 分拨,分配 } else if (key.isReadable()){ SocketChannel sc = (SocketChannel) key.channel(); ByteBuffer buffer = ByteBuffer.allocate(1024); sc.read(buffer);//读取客户端发送过来的所有数据存入buffer buffer.flip();//position:0 limit:前面读进来的所有字节 if (buffer.limit()==0){//如果本次一个字节都没有读取到就忽略 buffer.clear(); continue; } //获取缓冲区中 // 本次读取到的所有字节:0--limit //创建一个与buffer中目前可用字节一样长的一个字节数组 byte[] data = new byte[buffer.limit()]; //要求buffer将所有可用字节存入我们传入的字节数组data中 buffer.get(data); String message = new String(data, StandardCharsets.UTF_8); message = sc.socket().getInetAddress().getHostAddress()+"说:"+message; //清除buffer后,position= 0,limit = 容量 buffer.clear(); //put后,position=转换的字节数组长度 limit = 容量 buffer.put(message.getBytes(StandardCharsets.UTF_8)); //广播消息给所有客户端 for (SocketChannel channel: allChannel ) { buffer.flip();//position=0 limit=转换的字节数组长度 channel.write(buffer); //write完毕后,position= 转换后的字节数组长度=limit } // buffer.clear(); System.out.print(message); } } } } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { NIDServer server = new NIDServer(); server.start(); } }
NIO(服务端改造)
最新推荐文章于 2023-11-14 00:06:11 发布