目标
- 实现返回当前服务器时间的功能
- 采用半双工模式(主要是因为采用telnet为客户端,然后telnet默认情况下是半双工)
- 客户端连接后,可以发送 time,stop命令. 只有客户端发送stop命令后,服务段才主动断开链路。
- 不考虑读半包和写半包的情况
代码和注释
废话不多说,尽在代码中。直接拷贝即可运行(jdk7或以上)
package com.aio;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.util.Date;
import java.util.concurrent.CountDownLatch;
/**
* Author : Rocky
* Date : 21/12/2016 15:17
* Description :
* Test :
*/
public class TimeServer {
public static void main(String[] args) throws InterruptedException {
AsynchronousServerSocketChannel assc = null;
try {
assc = AsynchronousServerSocketChannel.open();
assc.bind(new InetSocketAddress(8888));
} catch (IOException e) {
e.printStackTrace();
System.exit(1);
}
doAccept(assc);
CountDownLatch latch = new CountDownLatch(1);
latch.await();
}
private static void doAccept(AsynchronousServerSocketChannel assc) {
assc.accept(assc, new AcceptCompletionHandle());
}
private static class AcceptCompletionHandle implements CompletionHandler<AsynchronousSocketChannel, AsynchronousServerSocketChannel> {
@Override
public void completed(AsynchronousSocketChannel result, AsynchronousServerSocketChannel assc) {
//继续监听accept事件
assc.accept(assc, this);
//开始监听可读时间
ByteBuffer readBuf = ByteBuffer.allocate(1024);
result.read(readBuf, readBuf, new ReadCompletionHandler(result));
}
@Override
public void failed(Throwable exc, AsynchronousServerSocketChannel assc) {
System.out.println("accept异常,继续accept");
assc.accept(assc, this);
}
}
private static class ReadCompletionHandler implements CompletionHandler<Integer, ByteBuffer> {
private AsynchronousSocketChannel asc;
public ReadCompletionHandler(AsynchronousSocketChannel asc) {
this.asc = asc;
}
@Override
public void completed(Integer result, ByteBuffer readedData) {
//如果对端链路关闭
if (result < 0) {
try {
asc.close();
} catch (IOException e) {
e.printStackTrace();
}
return;
}
//如果读取到对端发送过来的数据
if (result > 0) {
readedData.flip();
byte[] data = new byte[readedData.remaining()];
readedData.get(data);
String command = null;
try {
command = new String(data, "UTF-8");
if ("time\r\n".equalsIgnoreCase(command)) {
doWrite(new Date().toString() + "\r\n");
} else if ("stop\r\n".equalsIgnoreCase(command)) {
doWriteAndClose("bye.\r\n");
} else if ("\r\n".equalsIgnoreCase(command)) {
doWrite("\r\n");
} else {
doWrite("unknown command\r\n");
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
doWrite("server error\r\n");
}
}
//如果未读取到数据
else {
//继续尝试读取对端发送的数据
ByteBuffer readBuf = ByteBuffer.allocate(1024);
asc.read(readBuf, readBuf, this);
}
}
private void doWriteAndClose(String response) {
ByteBuffer repBuf = null;
try {
repBuf = ByteBuffer.wrap(response.getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
if (repBuf != null) {
asc.write(repBuf, repBuf, new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer repBuf) {
if (repBuf.hasRemaining()) {
asc.write(repBuf, repBuf, this);
}
//写完成后,关闭链路
else {
try {
asc.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public void failed(Throwable exc, ByteBuffer repBuf) {
exc.printStackTrace();
try {
asc.close();
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
}
private void doWrite(String response) {
ByteBuffer repBuf = null;
try {
repBuf = ByteBuffer.wrap(response.getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
if (repBuf != null) {
asc.write(repBuf, repBuf, new WriteCompletionHandler(asc, this));
}
}
@Override
public void failed(Throwable exc, ByteBuffer readedData) {
exc.printStackTrace();
try {
asc.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private static class WriteCompletionHandler implements CompletionHandler<Integer, ByteBuffer> {
private AsynchronousSocketChannel asc;
private ReadCompletionHandler rch;
public WriteCompletionHandler(AsynchronousSocketChannel asc, ReadCompletionHandler rch) {
this.asc = asc;
this.rch = rch;
}
@Override
public void completed(Integer result, ByteBuffer repBuf) {
if (repBuf.hasRemaining()) {
asc.write(repBuf, repBuf, this);
}
//写完成后(对端读取完成),再尝试读(半双工模式)
else {
//继续尝试读取对端发送的数据
ByteBuffer readBuf = ByteBuffer.allocate(1024);
asc.read(readBuf, readBuf, rch);
}
}
@Override
public void failed(Throwable exc, ByteBuffer repBuf) {
exc.printStackTrace();
try {
asc.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
测试
telnet 127.0.0.1 8888
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
time
Wed Dec 21 17:05:44 CST 2016
time
Wed Dec 21 17:07:44 CST 2016
s
unknown command
f
unknown command
stop
bye.
Connection closed by foreign host.