import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
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.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
public class EchoServer {
private static int SOCKET_NUM = 55555;
private DateFormat dateFormatter = new SimpleDateFormat("yyyyMMdd HH:mm:ss");
/**
* @param args
*/
public static void main(String[] args) {
new EchoServer().start();
}
public void start() {
try {
Selector selector = bindServer(); // 綁定服務端口,并定義一個事件選擇器對象記錄套接字通道的事件
/* 通過此循環來遍例事件 */
while (true) {
log("Waiting events.");
int n = selector.select(); // 查詢事件如果一個事件都沒有,這里就會阻塞
log("Got events: " + n);
ByteBuffer echoBuffer = ByteBuffer.allocate(50); // 定義一個byte緩沖區來存儲收發的數據
/* 循環遍例所有產生的事件 */
for (SelectionKey key : selector.selectedKeys()) {
SocketChannel sc;
selector.selectedKeys().remove(key); // 將本此事件從迭帶器中刪除
/* 如果產生的事件為接受客戶端連接(當有客戶端連接服務器的時候產生) */
if ((key.readyOps() & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT) {
ServerSocketChannel subssc = (ServerSocketChannel) key.channel(); // 定義一個服務器socket通道
sc = subssc.accept(); // 將臨時socket對象實例化為接收到的客戶端的socket
sc.configureBlocking(false); // 將客戶端的socket設置為異步
sc.register(selector, SelectionKey.OP_READ); // 將客戶端的socket的讀取事件注冊到事件選擇器中
System.out.println("Got new client:" + sc);
}
/* 如果產生的事件為讀取數據(當已連接的客戶端向服務器發送數據的時候產生) */
else if ((key.readyOps() & SelectionKey.OP_READ) == SelectionKey.OP_READ) {
sc = (SocketChannel) key.channel(); // 臨時socket對象實例化為產生本事件的socket
ByteArrayOutputStream bos = new ByteArrayOutputStream(); // 定義一個用于存儲byte數據的流對象,存儲全部信息
echoBuffer.clear(); // 先將客戶端的數據清空
try {
// 循環讀取所有客戶端數據到byte緩沖區中,當有數據的時候read函數返回數據長度
// NIO會自動的將緩沖區一次容納不下的自動分段
int readInt = 0; // 為讀取到數據的長度
while ((readInt = sc.read(echoBuffer)) > 0) {
// 如果獲得數據長度比緩沖區大小小的話
if (readInt < echoBuffer.capacity()) {
byte[] readByte = new byte[readInt]; // 建立一個臨時byte數組,將齊長度設為獲取的數據的長度
// 循環向此臨時數組中添加數據
for (int i = 0; i < readInt; i++) {
readByte[i] = echoBuffer.get(i);
}
bos.write(readByte); // 將此數據存入byte流中
}
// 否則就是獲得數據長度等于緩沖區大小
else {
bos.write(echoBuffer.array()); // 將讀取到的數據寫入到byte流對象中
}
}
// 當循環結束時byte流中已經存儲了客戶端發送的所有byte數據
log("Recive msg: " + new String(bos.toByteArray()));
} catch (Exception e) {
e.printStackTrace(); // 當客戶端在讀取數據操作執行之前斷開連接會產生異常信息
key.cancel(); // 將本socket的事件在選擇器中刪除
break;
}
writeBack(sc, bos.toByteArray()); // 向客戶端寫入收到的數據
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 綁定服務端口,初始化整個服務
* @throws IOException
*/
private Selector bindServer() throws IOException {
log("Start binding server socket:" + SOCKET_NUM);
Selector selector = Selector.open(); // 定義一個事件選擇器對象記錄套接字通道的事件
ServerSocketChannel ssc = ServerSocketChannel.open(); // 定義一個異步服務器socket對象
ssc.configureBlocking(false);// 將此socket對象設置為異步
ServerSocket ss = ssc.socket(); // 定義服務器socket對象-用來指定異步socket的監聽端口等信息
InetSocketAddress address = new InetSocketAddress(SOCKET_NUM); // 定義存放監聽端口的對象
ss.bind(address); // 將服務器與這個端口綁定
ssc.register(selector, SelectionKey.OP_ACCEPT); // 將異步的服務器socket對象的接受客戶端連接事件注冊到selector對象內
log("Binded socket at:" + SOCKET_NUM);
return selector;
}
private boolean writeBack(SocketChannel sc, byte[] b) {
ByteBuffer echoBuffer = ByteBuffer.allocate(b.length); // 建立這個byte對象的ByteBuffer
echoBuffer.put(b); // 將數據存入
echoBuffer.flip(); // 將緩沖區復位以便于進行其他讀寫操作
try {
// 向客戶端寫入數據,數據為接受到數據
sc.write(echoBuffer);
} catch (IOException e) {
e.printStackTrace();
return false;
}
System.out.println("Msg echo back: " + new String(echoBuffer.array()));
return true;
}
private void log(Object msg) {
System.out.println("SERVER [" + dateFormatter.format(new Date()) + "]: " + msg);
}
}
@import url(http://www.shnenglu.com/cutesoft_client/cuteeditor/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/css/cuteeditor.css);
From: http://topic.csdn.net/u/20120719/16/a5d0c2ca-d669-4902-9f3e-d4b8f4ad90b3.html?32104