import java.nio.channels.*;
import java.nio.charset.*;
import java.nio.*;
import java.util.*;
import java.io.*;
import java.net.*;
public class NBlockingServer {
int port = 8000;
int BUFFERSIZE = 1024;
Selector selector = null;
ServerSocketChannel serverChannel = null;
HashMap clientChannelMap = null; //用來存放每一個客戶連接對應的套接字和通道
public NBlockingServer(int port) {
this.clientChannelMap = new HashMap();
this.port = port;
}
//throws IOException
public void initialize() {
//初始化,分別實例化一個選擇器,一個服務器端可選擇通道
try {
this.selector = Selector.open();
this.serverChannel = ServerSocketChannel.open();
this.serverChannel.configureBlocking(false);
InetAddress localhost = InetAddress.getLocalHost();
InetSocketAddress isa = new InetSocketAddress(localhost, this.port);
this.serverChannel.socket().bind(isa); //將該套接字綁定到服務器某一可用端口
}
catch (IOException ex) {
}
}
//結束時釋放資源 throws IOException
public void finalize() {
try {
this.serverChannel.close();
this.selector.close();
}
catch (IOException ex) {
}
}
//監聽端口,當通道準備好時進行相應操作 throws IOException, InterruptedException
public void portListening() {
//服務器端通道註冊OP_ACCEPT事件
try {
SelectionKey acceptKey = this.serverChannel.register(this.selector,
SelectionKey.OP_ACCEPT);
//當有(已註冊的事件)發生時,select()返回值將大於0
while (acceptKey.selector().select() > 0) {
//System.out.println("event happened");
//取得所有已經準備好的所有選擇鍵
Set readyKeys = this.selector.selectedKeys();
//使用迭代器對選擇鍵進行輪詢
Iterator i = readyKeys.iterator();
while (i.hasNext()) {
SelectionKey key = (SelectionKey) i.next();
i.remove(); //刪除當前將要處理的選擇鍵
if (key.isAcceptable()) { //如果是有客戶端連接請求
System.out.println("more client connect in!");
ServerSocketChannel nextReady = (ServerSocketChannel) key.channel();
SocketChannel sc = (SocketChannel) nextReady.accept();
//獲取客戶端套接字
//Socket s = nextReady.accept().socket();
Socket s = sc.socket();
//SocketChannel s = nextReady.accept();
//設置對應的通道爲異步方式並註冊感興趣事件
//SocketChannel schannel=(SocketChannel)s.getChannel();
s.getChannel().configureBlocking(false);
//SelectionKey readWriteKey =s.getChannel().register(this.selector,SelectionKey.OP_READ |SelectionKey.OP_WRITE);
SelectionKey readWriteKey = s.getChannel().register(this.selector,
SelectionKey.OP_READ);
//將註冊的事件與該套接字聯繫起來
readWriteKey.attach(s); //將給定的對象附加到此鍵。
//將當前建立連接的客戶端套接字及對應的通道存放在哈希表//clientChannelMap中
this.clientChannelMap.put(s, new ClientChInstance(s.getChannel()));
}
else if (key.isReadable()) { //如果是通道讀準備好事件
System.out.println("Readable");
//取得選擇鍵對應的通道和套接字
SelectableChannel nextReady = (SelectableChannel) key.channel();
Socket socket = (Socket) key.attachment(); //// 檢索當前的附加對象。
//處理該事件,處理方法已封裝在類ClientChInstance中
this.readFromChannel(socket.getChannel(),
(ClientChInstance)this.
clientChannelMap.get(socket));
}
else if (key.isWritable()) { //如果是通道寫準備好事件
System.out.println("writeable");
//取得套接字後處理,方法同上
Socket socket = (Socket) key.attachment(); // 檢索當前的附加對象。
SocketChannel channel = (SocketChannel) socket.getChannel();
this.writeToChannel(channel, "This is from server!");
}
}
}
}
catch (ClosedChannelException ex) {
}
catch (IOException ex) {
}
}
//對通道的寫操作
public void writeToChannel(SocketChannel channel, String message) {
try {
ByteBuffer buf = ByteBuffer.wrap(message.getBytes("gb2312"));
int nbytes = channel.write(buf);
}
catch (IOException ex) {
System.out.println("系統向客戶端發送數據出錯;");
}
}
//將讀入字節緩衝的信息解碼
public String decode(ByteBuffer byteBuffer) throws
CharacterCodingException {
Charset charset = Charset.forName("gb2312");
CharsetDecoder decoder = charset.newDecoder(); //爲此 charset 構造新的解碼器。
CharBuffer charBuffer = decoder.decode(byteBuffer); // 把單個輸入字節緩衝區的剩餘內容解碼到新分配的字符緩衝區的便捷方法。
String result = charBuffer.toString(); //
return result;
}
//對通道的讀操作throws IOException, InterruptedException
public void readFromChannel(SocketChannel channel,ClientChInstance clientInstance) {
try {
ByteBuffer byteBuffer = ByteBuffer.allocate(BUFFERSIZE);
int nbytes = channel.read(byteBuffer); //斷開是出錯
//System.out.println(nbytes);
byteBuffer.flip();
String result = this.decode(byteBuffer);
//當客戶端發出”@exit”退出命令時,關閉其通道
if (result.indexOf("@exit") >= 0) {
channel.close();
System.out.println("客戶端關閉");
}
else {
clientInstance.append(result.toString());
//讀入一行完畢,執行相應操作
//if (result.indexOf("\n") >= 0) {
System.out.println("client input" + result);
clientInstance.execute();
//}
}
}
catch (IOException ex) {
try {
channel.close();
}
catch (IOException ex1) {
}
}
}
//該類封裝了怎樣對客戶端的通道進行操作,具體實現可以通過重載execute()方法
public class ClientChInstance {
SocketChannel channel;
MessageData mdata; //業務處理模塊
StringBuffer buffer = new StringBuffer();
public ClientChInstance(SocketChannel channel) {
this.channel = channel;
this.mdata= new MessageData();
}
public void execute() {
//String message = "AAA00100311111111000001100000200707161008220033 99999999hello,welcomd!this is 請求連接 first send.\n";
String info=buffer.toString();
String reinfo=mdata.isRigthData(info);
System.out.println("系統向客戶端發送信息:"+reinfo);
if(reinfo!=null){
writeToChannel(this.channel, reinfo);
}
buffer = new StringBuffer();
}
//當一行沒有結束時,將當前字竄置於緩衝尾
public void append(String values) {
buffer.append(values);
//mdata=new MessageData(this);
}
}
//主程序
public static void main(String[] args) {
NBlockingServer nbServer = new NBlockingServer(8000);
try {
nbServer.initialize();
}
catch (Exception e) {
e.printStackTrace();
System.exit( -1);
}
try {
nbServer.portListening();
}
catch (Exception e) {
e.printStackTrace();
}
}
}