目标
- 實作傳回目前伺服器時間的功能
- 采用半雙工模式(主要是因為采用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.