Java的NIO采用selector来轮循,还是不错,小试牛刀,下附代码
Server:
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; public class NIOServer { private Selector selector; public void initServer(int port) throws IOException { ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();// 打开服务器套接字通道 serverSocketChannel.configureBlocking(false);// 设置为非阻塞模式 serverSocketChannel.socket().bind(new InetSocketAddress(port));// 绑定端口 this.selector = Selector.open();// 打开选择器 // 注册事件,当事件到达,selector。select()会返回,如果没有事件会一直阻塞 serverSocketChannel.register(this.selector, SelectionKey.OP_ACCEPT);// 注册选择器,接受客户端连接 } public void listen() throws IOException { System.out.println("服务器启动!开始轮询监听消息"); while (true) { int ready = this.selector.select(); if (ready == 0) { continue; } Iterator items = this.selector.selectedKeys().iterator(); while (items.hasNext()) { SelectionKey key = (SelectionKey) items.next(); // 删除已经选择的key,防止重复处理 items.remove(); // 请求连接 if (key.isAcceptable()) { ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key .channel(); if (serverSocketChannel == null) { continue; } SocketChannel socketChannel = serverSocketChannel.accept();//每次接受新的连接请求会产生新的通道, socketChannel.configureBlocking(false); socketChannel.write(ByteBuffer.wrap(new String( "你好,客户端,连接已建立,可以开始通信!").getBytes()));//连接成功,发消息给客户端 System.out.println("连接已经建立"); socketChannel.register(this.selector, SelectionKey.OP_READ);//准备好接收数据 } else if (key.isReadable()) { this.read(key); } } } } public void read(SelectionKey key) throws IOException { SocketChannel socketChannel = (SocketChannel) key.channel(); ByteBuffer byteBuffer = ByteBuffer.allocate(100); socketChannel.read(byteBuffer); byte[] data = byteBuffer.array(); String msg = new String(data).trim(); if (msg.equals("[email protected]#$%")) {//收到关闭指令,发送确认关闭的指令给客户端,结束自身通道 ByteBuffer sendBuffer = ByteBuffer.wrap(new String("bye close").getBytes()); while (sendBuffer.hasRemaining()) { socketChannel.write(sendBuffer); } socketChannel.close(); System.out.println("已经关闭"); } else { System.out.println("server Receive:" + msg);//服务器收到任何消息都会给客户端发送"收到" socketChannel.write(ByteBuffer.wrap(new String("收到!").getBytes())); } } public static void main(String[] args) { NIOServer server = new NIOServer(); try { server.initServer(1377); server.listen(); } catch (IOException e) { e.printStackTrace(); } } }
Client:
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; 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; public class NIOClient { private Selector selector; //初始化 public void initClient(String ipAddress, int port) throws IOException { SocketChannel socketChannel = SocketChannel.open();//打开套接字通道 socketChannel.configureBlocking(false);//非阻塞 this.selector = Selector.open();//打开选择器 socketChannel.connect(new InetSocketAddress(ipAddress, port));//指定ip地址和端口号用于连接 socketChannel.register(this.selector, SelectionKey.OP_CONNECT);//通道注册到选择器中,用于连接 } //监听 public void listen() throws IOException { while (true) { //当客户端关闭的时候需要结束轮循的动作,这样程序才会结束 if (!this.selector.isOpen()) { return; } int ready = this.selector.select();//这个才是真正的轮循啦,得到通道 if (ready == 0) { continue;//如果selector中没有SocketChannel,直接开始下一次咯 } Iterator items = this.selector.selectedKeys().iterator(); while (items.hasNext()) { SelectionKey key = (SelectionKey) items.next(); items.remove();//防止重复处理 if (key.isConnectable()) {//连接 System.out.println("正在连接,连接成功后,服务器会返回结果!"); SocketChannel socketChannel = (SocketChannel) key.channel(); // 如果正在连接,则完成连接 if (socketChannel.isConnectionPending()) { socketChannel.finishConnect(); } socketChannel.register(this.selector, SelectionKey.OP_READ);//重新注册该通道,用于读取数据 } else if (key.isReadable()) {//读取 this.read(key); } } } } /****************************************************/ /* * 客户端关闭思路 * 客户端 服务器 * * 用户输入bye,需要关闭客户端 * * "[email protected]#$%" -> "[email protected]#$%" 约定好的内容("[email protected]#$%") * * "bye close" <- "bye close" ----->服务器通道关闭 约定好的内容("bye close") * * 客户端通道关闭 服务器还可以接受别的请求 * */ public void read(SelectionKey key) throws IOException { SocketChannel socketChannel = (SocketChannel) key.channel(); ByteBuffer receiveBuffer = ByteBuffer.allocate(100); socketChannel.read(receiveBuffer); byte[] data = receiveBuffer.array(); String msg = new String(data).trim(); if (msg.equals("bye close")) {//客户端和服务器约定关闭,收到服务器响应后可以关闭 System.out.println("关闭"); socketChannel.close(); this.selector.close(); return; } System.out.println("client receive:" + msg); BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); String readMsg = reader.readLine(); ByteBuffer sendBuffer = ByteBuffer.allocate(256); sendBuffer.clear(); if(readMsg.equals("bye")){//告诉服务器,准备要撤退,当服务器返回bye close,关闭通道和选择器 System.out.println("准备关闭"); sendBuffer.put("[email protected]#$%".getBytes()); sendBuffer.flip(); }else{ sendBuffer.put(readMsg.getBytes()); sendBuffer.flip(); } while (sendBuffer.hasRemaining()) { socketChannel.write(sendBuffer); } } public static void main(String[] args) { NIOClient client = new NIOClient(); try { client.initClient("127.0.0.1", 1377); client.listen(); } catch (IOException e) { e.printStackTrace(); } } }
客户端可开启多个,用于连接服务器端,亲测可用,有问题联系作者,[email protected]
业务逻辑可自己完善,其实建议的搭建不难
时间: 2024-11-02 20:46:47