NIO程式設計之ServerSocketChannel用法詳解
之前一直看不懂NIO中的ServerSocketChannel和SocketChannel的區別,看了這篇博文,感覺通俗易懂,於是決定分享一下。。。。
在用nio通訊的過程我用以下情景給你模擬:
- 學校(ServerSocketChannel) 2。 學校教務處(Selector) 3。 老師 (ServerSocket ) 4。 學生 (SocketChannel) 5。 員工號/學生號(SelectionKey)
學校相當於我們的網路應用程式,一旦學校啟動,學校就不停止,不斷執行,直到學期結束; 要啟動學校就要:
ServerSocketChannel ssc= ServerSocketChannel.open();//新建NIO通道 ssc.configureBlocking( false );//使通道為非阻塞
老師: 相當於服務端的Socket,一個老師對應多個學生,多個學生向老師請教,老師會一一做出回答。而學校要正常運營當然當不了老師,所以在開學之前,必須先聘請專業的老師來任教
ServerSocket ss = ssc.socket();//建立基於NIO通道的socket連線
ss.bind(new InetSocketAddress("127.0.0.1",SERVERPORT));//新建socket通道的埠
學校教務處: 老師都有了,但是需要有部門對老師和學生做統一的管理, 如果你去一個學校找一個人,實在是找不到,你可以告訴教務處,那個人是學生還是老師,是老師的話員工編號老師姓名的多少,是學生的話學號和姓名是多少,教務處就會找到告訴你他在哪裡。
//將NIO通道選繫結到擇器,當然繫結後分配的主鍵為skey
SelectionKey skey = ssc.register( selector, SelectionKey.OP_ACCEPT );
ssc註冊了選擇器後,其下的老師ServerSocket就也入了員工冊了。所以老師的編號就是skey
學生(相當於Client): 學校、老師、教務處都有了,現在就可以招生了! 如果有學生來報名:
while(true){//除非學期結束,否則一直等待學生 int num = selector.select();//獲取通道內是否有選擇器的關心事件, 意思是有多少學生報告 if(num<1){continue; } Set selectedKeys = selector.selectedKeys();//獲取通道內關心事件的集合 ,這裡的集合就是老師和學生的編號集合,如果key是學生的,那就是老學生來問問題,如果key是老師的,那就是招生辦的老師帶著一個新生來註冊 Iterator it = selectedKeys.iterator(); while (it.hasNext()) {//遍歷每個key (學生key和老師key) ....... } ..... }
既然有學生來報告,那有兩種可能,一種是招生老師帶著新生來註冊的,一種是老生來問問題的。 上面的while (it.hasNext()) 體可以這樣寫:
while (it.hasNext()) {//遍歷每個事件
try{
SelectionKey key = (SelectionKey)it.next(); //先得到這個學生的編號key
//判斷是新生報道還是老生問問題
if ((key.readyOps() & SelectionKey.OP_ACCEPT)
== SelectionKey.OP_ACCEPT) {
//這是招生老師的Key說明是新生註冊,先找到招生老師,再由招生老師找到新生,就可以給新生註冊學號了
ServerSocketChannel serverChanel = (ServerSocketChannel)key.channel(); //通過key把學校和老師找到了
//從serverSocketChannel中創建出與客戶端的連線socketChannel 有了老師才有學生,不可能我教計算機的,來一個想學李小龍的都讓他報名
SocketChannel sc = serverChanel.accept(); //學生報名成功
sc.configureBlocking( false );
// 把新連線註冊到選擇器,新生被接收後給註冊個新學號
SelectionKey newKey = sc.register( selector,
SelectionKey.OP_READ ); //註冊學號成功,並分配學生的許可權
it.remove(); //新生註冊任務完成了,呵呵
System.out.println( "Got connection from "+sc );
}else
//讀客戶端資料的事件,此時有客戶端發資料過來,客戶端事件 這是老學生來問問題了。
if((key.readyOps() & SelectionKey.OP_READ)== SelectionKey.OP_READ){
// 讀取資料 ,接受學生的問題
SocketChannel sc = (SocketChannel)key.channel(); //通過學號知道是誰問的問題
//下面接受問題
int bytesEchoed = 0;
while((bytesEchoed = sc.read(echoBuffer))> 0){
System.out.println("bytesEchoed:"+bytesEchoed);
}
echoBuffer.flip();
System.out.println("limet:"+echoBuffer.limit());
byte [] content = new byte[echoBuffer.limit()];
echoBuffer.get(content);
String result=new String(content);
doPost(result,sc); //相應老師會去做回答的,細節自己去寫吧
echoBuffer.clear();
it.remove(); //任務完成,記得上面也是一樣,要remove掉,否則下一次又來一次任務,就死迴圈了
}
}catch(Exception e){}
}
}
補充你的補充:
ssc.register( selector, SelectionKey.OP_ACCEPT ); 這個方法是把ssc註冊繫結到選擇器selector 這樣下次你想找ssc或者判斷一個物件是不是ssc就可以通過selector來查詢,查詢是通過判斷ssc的key得到的。 至於第二個引數SelectionKey.OP_ACCEPT 你可以理解成ssc的key型別或者操作許可權 如果 ssc是學校老師,那麼繫結成功後 老師就擁有了OP_ACCEPT的許可權或者說他的key型別是SelectionKey.OP_ACCEPT Accept是接受的意思,這是不是很像socket程式設計裡的 accept()方法呢? 是的,沒錯,我們正是通過這個引數給了老師招生和帶學生來註冊的許可權。
而學生呢? 他擁有的許可權為SelectionKey.OP_READ 表示有收發讀取訊息的許可權,即問問題的許可權,因此他不能幫別的學生註冊。
所以你回到上面仔細看看while結構體裡面做了判斷如下:
if ((key.readyOps() & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT) {…} 很明顯,擁有Accept許可權的人只可能是老師,那老師有什麼事會找教務處? 那肯定就是他是招生辦的,招到一個學生來報名來註冊了。 然後,馬上給這個新連上來的客戶端分配了一個key SelectionKey newKey = sc.register( selector, SelectionKey.OP_READ ); 看,這裡只給他OP_READ,而不是Accept哦
另一個if else if((key.readyOps() & SelectionKey.OP_READ)== SelectionKey.OP_READ){
//很明顯,這是這學生,因為所有帶OP_READ的人都是前面由招生辦老師帶過來註冊過的。