java网络编程-NIO
参考:
https://www.zhihu.com/question/29005375
https://blog.csdn.net/anxpp/article/details/51512200
https://www.cnblogs.com/Evsward/p/nio.html
NIO主要基于Linux系统的IO多路复用
java也是基于操作系统的功能做进一步的封装.下面展示基于java NIO的服务端和客户端实现.
服务端
public class NIOServer { private static final int port=8080; public static void main(String[] args) { ServerHandle serverHandle = new ServerHandle(port); new Thread(serverHandle).start(); } } // public class ServerHandle implements Runnable { private Selector selector; private ServerSocketChannel serverSocketChannel; private volatile boolean started; public ServerHandle(int port) { try { //创建选择器 selector = Selector.open(); //打开监听channel serverSocketChannel = ServerSocketChannel.open(); //Chanel设为非阻塞模式 serverSocketChannel.configureBlocking(false); //绑定端口 serverSocketChannel.socket().bind(new InetSocketAddress(port), 1024); //监听客户端连接请求 serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); System.out.println("==服务端启动=="); started = true; } catch (IOException e) { e.printStackTrace(); } } @Override public void run() { //循环遍历selector while (started) { //没有就绪事件时阻塞,有一个就会唤醒 try { selector.select(); Set<SelectionKey> selectionKeys = selector.selectedKeys(); Iterator<SelectionKey> iterator = selectionKeys.iterator(); while (iterator.hasNext()) { SelectionKey selectionKey = iterator.next(); iterator.remove(); //处理一个请求 try { handleInput(selectionKey); } catch (Exception e) { if (selectionKey != null) { selectionKey.cancel(); if (selectionKey.channel() != null) { selectionKey.channel().close(); } } } } } catch (IOException e) { e.printStackTrace(); } } //关闭selector,释放资源 if (selector != null) { try { selector.close(); } catch (IOException e) { e.printStackTrace(); } } } private void handleInput(SelectionKey key) { try { //判断请求是否有效 if (key.isValid()) { //处理新接入的请求消息 if (key.isAcceptable()) { ServerSocketChannel channel = (ServerSocketChannel) key.channel(); //创建socketChannel实例 SocketChannel socketChannel = channel.accept(); //设为非阻塞 socketChannel.configureBlocking(false); //注册为监听读事件 socketChannel.register(selector, SelectionKey.OP_READ); } //如果可读 if (key.isReadable()) { SocketChannel sc = (SocketChannel) key.channel(); //开辟一个1024的缓冲区 ByteBuffer buffer = ByteBuffer.allocate(1024); //读取流,返回读取的字节数 int readBytes = sc.read(buffer); if (readBytes > 0) { //将缓冲区的limit设为0,用于后续对缓冲区的读取 buffer.flip(); //根据缓冲区的可读字节创建字节数组 byte[] bytes = new byte[buffer.remaining()]; //将缓冲区内容复制到新建数组中 buffer.get(bytes); //将收到的消息打印到屏幕 String s = new String(bytes, "UTF-8"); System.out.println("server receive:" + s); //应答消息 doWrite(sc, "hello," + s); } else if (readBytes < 0) { key.cancel(); sc.close(); } } } } catch (IOException e) { e.printStackTrace(); } } //向channel写消息 private void doWrite(SocketChannel channel, String response) throws IOException { //将消息转换为字节数组 byte[] bytes = response.getBytes(); //根据消息大小创建bytebuffer ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length); //将字节数组复制到缓冲区 writeBuffer.put(bytes); writeBuffer.flip(); //发送缓冲区 channel.write(writeBuffer); } public void stop() { started = false; } }
客户端实现
public class NIOClient { private static final int port=8080; private static final String host="127.0.0.1"; public static void main(String[] args) throws IOException { ClientHandle clientHandle = new ClientHandle(host,port); new Thread(clientHandle).start(); Scanner scanner = new Scanner(System.in); while (scanner.hasNextLine()){ clientHandle.sendMsg(scanner.nextLine()); } } }
客户端handle
public class ClientHandle implements Runnable { private Selector selector; private SocketChannel socketChannel; private String host; private int port; private volatile boolean started; public ClientHandle(String host, int port) { this.host = host; this.port = port; try { selector = Selector.open(); socketChannel = SocketChannel.open(); socketChannel.configureBlocking(false); started = true; } catch (IOException e) { e.printStackTrace(); System.exit(1); } } @Override public void run() { System.out.println("==客户端启动=="); try { if (socketChannel.connect(new InetSocketAddress(host, port))){ }else { socketChannel.register(selector, SelectionKey.OP_CONNECT); } } catch (IOException e) { e.printStackTrace(); //如果没连上,后面就不用读写 System.exit(1); } while (started) { try { selector.select(); Set<SelectionKey> selectionKeys = selector.selectedKeys(); Iterator<SelectionKey> iterator = selectionKeys.iterator(); while (iterator.hasNext()) { SelectionKey key = iterator.next(); iterator.remove(); try { handleInput(key); } catch (Exception e) { if (key != null) { key.channel(); if (key.channel() != null) { key.channel().close(); } } e.printStackTrace(); } } } catch (IOException e) { e.printStackTrace(); System.exit(1); } } if (selector != null) { try { selector.close(); } catch (IOException e) { e.printStackTrace(); } } } private void handleInput(SelectionKey key) throws IOException { //判断请求是否有效 if (key.isValid()) { //处理新接入的请求消息 SocketChannel sc = (SocketChannel) key.channel(); if (key.isConnectable()) { if (sc.finishConnect()) { } else { System.exit(1); } } //如果可读 if (key.isReadable()) { //开辟一个1024的缓冲区 ByteBuffer buffer = ByteBuffer.allocate(1024); //读取流,返回读取的字节数 int readBytes = sc.read(buffer); if (readBytes > 0) { //将缓冲区的limit设为0,用于后续对缓冲区的读取 buffer.flip(); //根据缓冲区的可读字节创建字节数组 byte[] bytes = new byte[buffer.remaining()]; //将缓冲区内容复制到新建数组中 buffer.get(bytes); //将收到的消息打印到屏幕 String s = new String(bytes, "UTF-8"); System.out.println("client receive:" + s); } else if (readBytes < 0) { key.cancel(); sc.close(); } } } } //向channel写消息 private void doWrite(SocketChannel channel, String request) throws IOException { //将消息转换为字节数组 byte[] bytes = request.getBytes(); //根据消息大小创建bytebuffer ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length); //将字节数组复制到缓冲区 writeBuffer.put(bytes); writeBuffer.flip(); //发送缓冲区 channel.write(writeBuffer); } public void sendMsg(String msg) throws IOException { socketChannel.register(selector, SelectionKey.OP_READ); doWrite(socketChannel, msg); } }
浙公网安备 33010602011771号