1.java nio 网络编程 Server端
最近一段时间看了下java nio,对于它的实际用途还有待继续研究,也看了网上很多人的文章博客等,收益颇多,但是还是自己动手才能知道自己是否真的会写了,会写与是否理解nio工作机制还有一定距离。我又不是一个理论派,主要还是讲究实战的,好,废话不多说,直接准备动作写。
首先你需要了解几个nio网络编程中会用到的组件(类):
1.ByteBuffer
2.Selector
3.ServerSocketChannel
4.SocketChannel
5.SelectionKey
说一下nio server的整体实现思路:
打开Selector--》 打开ServerSocketChannel --> 配置ServerSocketChannel是否阻塞 --》 绑定ServerSocketChannel到某个主机以及端口 --》给ServerSocketChannel注册一个Selector --》 ServerSocketChannel监听accept事件 --》 客户端连接后获取SocketChannel --》 响应SocketChannel上的read/write事件
我写的可能和网络上一些人写的稍微有点不太一样,这也没办法,网络上打开10篇nio相关文章,有好几篇都是一样的,你抄我,我抄你的。个个图文并茂,个个都是架构师,分析reactor设计模式,多路复用,高性能,讲的头头是道,不过这和我没什么关系,我体会不到,应该是我太菜了。所以还是来点简单的,基础打好,总归不会有害处。
下面是具体实现代码:把这个代码拷贝到你的ide中,然后运行,用tcp连接工具,比如putty,cmd也行,不过cmd没有那么好用。有两个例子,第一个是只能少量字符传输,第二个支持大文本传输。
demo1:
package com.govert.project; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; 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; import java.util.Random; import java.util.Set; /** * 少量字符传输 */ public class NioServer { public static void main(String[] args) throws IOException { // read and write byte buffer ByteBuffer rb = ByteBuffer.allocate(1024); ByteBuffer wb = ByteBuffer.allocate(1024); // listen hostname and port String hostname = "127.0.0.1"; int port = 9000; // open Selector & check Selector selector = Selector.open(); if (selector.isOpen()) { System.err.println("selector open success."); } // open ServerSocketChannel & check ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); if (serverSocketChannel.isOpen()) { System.err.println("serverSocketChannel open success."); } // ServerSocketChannel config non-block & bind hostname and port serverSocketChannel.configureBlocking(false); // serverSocketChannel.socket().bind(new InetSocketAddress(hostname, port));// old way bind serverSocketChannel.bind(new InetSocketAddress(hostname, port));// 1.7+ // ServerSocketChannel register selector serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); // handle event loop while (true) { selector.select(); Set<SelectionKey> keys = selector.selectedKeys(); Iterator<SelectionKey> kit = keys.iterator(); while (kit.hasNext()) { SelectionKey key = kit.next(); // read event if (key.isReadable()) { SocketChannel socketChannel = (SocketChannel) key.channel(); rb.clear(); socketChannel.read(rb); rb.flip(); System.err.println("client request data: " + new String(rb.array(), 0, rb.limit(), "utf-8")); // key.interestOps(SelectionKey.OP_WRITE); } // write event else if (key.isWritable()) { SocketChannel socketChannel = (SocketChannel) key.channel(); String resp = "server response data: " + new Random().nextInt(1000) + 1 + "\r\n"; wb.clear(); wb.put(resp.getBytes("utf-8")); wb.flip(); socketChannel.write(wb); // key.interestOps(SelectionKey.OP_READ); } // accept event else if (key.isAcceptable()) { SocketChannel socketChannel = serverSocketChannel.accept(); socketChannel.configureBlocking(false); socketChannel.register(selector, SelectionKey.OP_READ); } } // clear keys keys.clear(); } } }
demo2:
package com.govert.project; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.*; import java.util.Iterator; import java.util.Random; import java.util.Set; /** * 大文本传输 */ public class NioServer { public static void main(String[] args) throws IOException { // read and write byte buffer ByteBuffer rb = ByteBuffer.allocate(1024); ByteBuffer wb = ByteBuffer.allocate(1024); // listen hostname and port String hostname = "127.0.0.1"; int port = 9000; // open Selector & check Selector selector = Selector.open(); if (selector.isOpen()) { System.err.println("Selector open success."); } // open ServerSocketChannel & check ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); if (serverSocketChannel.isOpen()) { System.err.println("ServerSocketChannel open success."); } // ServerSocketChannel config non-block & bind hostname and port serverSocketChannel.configureBlocking(false); // serverSocketChannel.socket().bind(new InetSocketAddress(hostname, port));// old way bind serverSocketChannel.bind(new InetSocketAddress(hostname, port));// 1.7+ // ServerSocketChannel register selector serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); // handle event loop while (true) { selector.select(); Set<SelectionKey> keys = selector.selectedKeys(); Iterator<SelectionKey> kit = keys.iterator(); while (kit.hasNext()) { SelectionKey key = kit.next(); // read event if (key.isReadable()) { SocketChannel socketChannel = (SocketChannel) key.channel(); // large text ByteArrayOutputStream baos = new ByteArrayOutputStream(); StringBuilder sb = new StringBuilder(); while (true) { if (socketChannel.read(rb) < 1 && rb.hasRemaining()) break; rb.flip(); baos.write(rb.array(), 0, rb.limit()); rb.clear(); } // System.err.println("client request data: " + new String(baos.toByteArray(), 0, baos.size(), "utf-8")); // key.interestOps(SelectionKey.OP_WRITE); } // write event else if (key.isWritable()) { SocketChannel socketChannel = (SocketChannel) key.channel(); // large response StringBuilder sb = new StringBuilder(); for (int i = 1; i < 1001; i++) { sb.append("server respnse data: 第" + i + "条响应结果.\r\n"); } byte[] bytes = sb.toString().getBytes("utf-8"); int length = bytes.length; int loop = (length + 1023) / 1024; int lastLoop = loop - 1; int len; for (int i = 0; i < loop; i++) { if (i == lastLoop) len = length - i * 1024; else len = 1024; wb.put(bytes, i * 1024, len); wb.flip(); socketChannel.write(wb); wb.clear(); } // key.interestOps(SelectionKey.OP_READ); } // accept event else if (key.isAcceptable()) { SocketChannel socketChannel = serverSocketChannel.accept(); socketChannel.configureBlocking(false); socketChannel.register(selector, SelectionKey.OP_READ); } } // clear keys keys.clear(); } } }