这里首先声明:我导入的jar包是5.0的jar包。
在写这个demo之前最好先学习一下NIO 的基础方法,至少知道哪个方法是干什么用的。
这篇博文的代码实现了一个简单的时间服务器:客户端发送请求,服务端判断接收到的请求返回不同的信息。
NIO服务端代码:
public class TimeServer {
public static void main(String[] args) {
int port = 9090;
if(args!=null&&args.length>0){
try{
port = Integer.valueOf(args[0]);
}catch(NumberFormatException e){
//采用默认值
}
}
/**
* 创建一个多路复用类,它是一个独立的线程,负责轮询多路复用器Selector
* 可以处理多个客户端的并发接入
*/
MultiplexerTimeServer timeServer = new MultiplexerTimeServer(port);
new Thread(timeServer,"NIO-MultiplexerTimeServer-001").start();
}
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.Set;
public class MultiplexerTimeServer implements Runnable {
private Selector selector;
private ServerSocketChannel servChannel;
private volatile boolean stop;
/**
* 初始化多路复用器、绑定监听窗口
*
* @param port
*/
public MultiplexerTimeServer(int port) {
try {
//多路复用器,可以同时轮询多个Channel
selector = Selector.open();
//打开ServerSocketChannel,用于监听客户端的连接
servChannel = ServerSocketChannel.open();
//绑定监听的端口,设置连接为非阻塞模式
servChannel.configureBlocking(false);
servChannel.socket().bind(new InetSocketAddress(port), 1024);
//将ServerSocketChannel注册到多路复用器上,监听ACCEPT事件
servChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("The time server is start in port :" + port);
} catch (IOException e) {
e.printStackTrace();
System.exit(1);
}
}
public void stop() {
this.stop = true;
}
@Override
public void run() {
while (!stop) {
try {
//多路复用器在线程run方法的无限循环体内轮询准备就绪的Key
selector.select(1000);
Set<SelectionKey> selectionKey = selector.selectedKeys();
Iterator<SelectionKey> it = selectionKey.iterator();
SelectionKey key = null;
while (it.hasNext()) {
key = it.next();
it.remove();
try {
handleInput(key);
} catch (Exception e) {
key.channel();
if (key.channel() != null)
key.channel().close();
}
}
} catch (Throwable t) {
t.printStackTrace();
}
}
/**
* 多路复用器关闭后,所有注册在上面的Channel和Pipe等资源都会被自动去注册 并关闭,所以不需要重复释放资源
*/
if (selector != null) {
try {
selector.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void handleInput(SelectionKey key) throws IOException {
if(key.isValid()){
//处理新接入的请求消息
if(key.isAcceptable()){
//接收新的连接
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
SocketChannel sc = ssc.accept();
sc.configureBlocking(false);
//添加新的连接到多路复用器
sc.register(selector, SelectionKey.OP_READ);
}
if(key.isReadable()){
//读取数据
SocketChannel sc = (SocketChannel) key.channel();
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
int readBytes = sc.read(readBuffer);
if(readBytes>0){
readBuffer.flip();
byte[] bytes = new byte[readBuffer.remaining()];
readBuffer.get(bytes);
String body = new String(bytes, "utf-8");
System.out.println("The time server receive order :"+body);
String currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body)?
new java.util.Date(System.currentTimeMillis()).toString()
:"BAD ORDER";
doWrite(sc,currentTime);
}else if(readBytes<0){
//对端关闭连接
key.channel();
sc.close();
}else {
;//读到0字节,忽略
}
}
}
}
private void doWrite(SocketChannel channel, String response) throws IOException {
if(response!=null&&response.trim().length()>0){
byte[] bytes = response.getBytes();
ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);
writeBuffer.put(bytes);
writeBuffer.flip();
channel.write(writeBuffer);
}
}
}
NIO客户端代码:
public class TimeClient {
public static void main(String[] args) {
int port = 9090;
if(args!=null&&args.length>0){
try{
port = Integer.valueOf(args[0]);
}catch(NumberFormatException e){
//采用默认值
}
}
new Thread(new TimeClientHandle("127.0.0.1",port),"TimeClient-001").start();
}
}
public class TimeClientHandle implements Runnable{
private String host;
private int port;
private Selector selector;
private SocketChannel socketChannel;
private volatile boolean stop;
public TimeClientHandle(String host,int port){
this.host = host==null?"127.0.0.1":host;
this.port = port;
try {
selector = Selector.open();
socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
}catch (IOException e){
e.printStackTrace();
System.exit(1);
}
}
@Override
public void run() {
try {
doConnect();
}catch (IOException e){
e.printStackTrace();
System.exit(1);
}
while (!stop){
try{
selector.select(1000);
Set<SelectionKey> selectionKey = selector.selectedKeys();
Iterator<SelectionKey> it = selectionKey.iterator();
SelectionKey key = null;
while (it.hasNext()){
key = it.next();
it.remove();
try {
handleInput(key);
}catch (Exception e){
if(key!=null){
key.cancel();
if (key.channel()!=null)
key.channel().close();
}
}
}
}catch (Exception e){
e.printStackTrace();
System.exit(1);
}
}
/**
* 多路复用器关闭后,所有注册在上面的Channel和Pipe等资源都会被自动去注册 并关闭,所以不需要重复释放资源
*/
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()) {
System.out.println("连接成功...");
if (sc.finishConnect()) {
sc.register(selector, SelectionKey.OP_READ);
doWrite(sc);
} else
System.exit(1);
}
if (key.isReadable()){
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
int readBytes = sc.read(readBuffer);
if (readBytes>0){
readBuffer.flip();
byte[] bytes = new byte[readBuffer.remaining()];
readBuffer.get(bytes);
String body = new String(bytes,"UTF-8");
System.out.println("Now is :"+body);
this.stop = true;
}else if(readBytes<0){
//对端链路关闭
key.channel();
sc.close();
}else
; //读到0字节,忽略
}
}
}
private void doConnect() throws IOException{
//如果连接成功,则注册到多路复用器上,发送请求消息,读应答
if(socketChannel.connect(new InetSocketAddress(host,port))){
socketChannel.register(selector,SelectionKey.OP_READ);
doWrite(socketChannel);
}else
socketChannel.register(selector,SelectionKey.OP_CONNECT);
}
private void doWrite(SocketChannel sc) throws IOException{
byte[] req = "QUERY TIME ORDER".getBytes();
ByteBuffer writeBuffer = ByteBuffer.allocate(req.length);
writeBuffer.put(req);
writeBuffer.flip();
sc.write(writeBuffer);
if(!writeBuffer.hasRemaining()){
System.out.println("Send order 2 server succeed.");
}
}
}