Java NIO增加了新的SocketChannel、ServerSocketChannel等類來提供對構建高性能的服務端程序的支持。 SocketChannel、ServerSocketChannel能夠在非阻塞的模式下工作,它們都是selectable的類。在構建服務器或者中間件時,推薦使用Java NIO。
在傳統的網絡編程中,我們通常使用一個專用線程(Thread)來處理一個Socket連接,通過使用NIO,一個或者很少幾個Socket線程就可以處理成千上萬個活動的Socket連接。
通常情況下,通過ServerSocketChannel.open()獲得一個ServerSocketChannel的實例,通過SocketChannel.open或者serverSocketChannel.accept()獲得一個SocketChannel實例。要使ServerSocketChannel或者SocketChannel在非阻塞的模式下操作,可以調用
serverSocketChannel.configureBlocking (false);
或者
socketChannel.configureBlocking (false);
語句來達到目的。通常情況下,服務端可以使用非阻塞的ServerSocketChannel,這樣,服務端的程序就可以更容易地同時處理多個socket線程。
下面我們來看一個綜合例子,這個例子使用了ServerSocketChannel、SocketChannel開發了一個非阻塞的、能處理多線程的Echo服務端程序,見示例12-14。
【程序源代碼】
1 // ==================== Program Discription ===================== 2 // 程序名稱:示例12-14 : SocketChannelDemo.java 3 // 程序目的:學習Java NIO#SocketChannel 4 // ============================================================== 5 6 7 import java.nio.ByteBuffer; 8 import java.nio.channels.ServerSocketChannel; 9 import java.nio.channels.SocketChannel; 10 import java.nio.channels.Selector; 11 import java.nio.channels.SelectionKey; 12 import java.nio.channels.SelectableChannel; 13 14 import java.net.Socket; 15 import java.net.ServerSocket; 16 import java.net.InetSocketAddress; 17 import java.util.Iterator; 18 19 public class SocketChannelDemo 20 21 { 22 public static int PORT_NUMBER = 23;//監聽端口 23 ServerSocketChannel serverChannel; 24 ServerSocket serverSocket ; 25 Selector selector ; 26 private ByteBuffer buffer = ByteBuffer.allocateDirect (1024); 27 28 public static void main (String [] args) 29 throws Exception 30 { 31 SocketChannelDemo server=new SocketChannelDemo(); 32 server.init(args); 33 server.startWork(); 34 } 35 36 37 public void init (String [] argv)throws Exception 38 { 39 int port = PORT_NUMBER; 40 41 if (argv.length > 0) { 42 port = Integer.parseInt (argv [0]); 43 } 44 45 System.out.println ("Listening on port " + port); 46 47 // 分配一個ServerSocketChannel 48 serverChannel = ServerSocketChannel.open(); 49 // 從ServerSocketChannel里獲得一個對應的Socket 50 serverSocket = serverChannel.socket(); 51 // 生成一個Selector 52 selector = Selector.open(); 53 54 // 把Socket綁定到端口上 55 serverSocket.bind (new InetSocketAddress (port)); 56 //serverChannel為非bolck 57 serverChannel.configureBlocking (false); 58 59 // 通過Selector注冊ServerSocetChannel 60 serverChannel.register (selector, SelectionKey.OP_ACCEPT); 61 62 } 63 64 public void startWork()throws Exception 65 66 { 67 while (true) { 68 69 int n = selector.select();//獲得IO準備就緒的channel數量 70 71 if (n == 0) { 72 continue; // 沒有channel準備就緒,繼續執行 73 } 74 75 // 用一個iterator返回Selector的selectedkeys 76 Iterator it = selector.selectedKeys().iterator(); 77 78 // 處理每一個SelectionKey 79 while (it.hasNext()) { 80 SelectionKey key = (SelectionKey) it.next(); 81 82 // 判斷是否有新的連接到達 83 if (key.isAcceptable()) { 84 //返回SelectionKey的ServerSocketChannel 85 ServerSocketChannel server = (ServerSocketChannel) key.channel(); 86 SocketChannel channel = server.accept(); 87 88 registerChannel (selector, channel, 89 SelectionKey.OP_READ); 90 91 doWork (channel); 92 } 93 94 // 判斷是否有數據在此channel里需要讀取 95 if (key.isReadable()) { 96 97 processData (key); 98 99 } 100 101 //刪除 selectedkeys 102 it.remove(); 103 } 104 } 105 } 106 protected void registerChannel (Selector selector, 107 SelectableChannel channel, int ops) 108 throws Exception 109 { 110 if (channel == null) { 111 return; 112 } 113 114 115 channel.configureBlocking (false); 116 117 channel.register (selector, ops); 118 } 119 120 //處理接收的數據 121 protected void processData (SelectionKey key) 122 throws Exception 123 { 124 125 126 SocketChannel socketChannel = (SocketChannel) key.channel(); 127 int count; 128 129 buffer.clear(); // 清空buffer 130 131 // 讀取所有的數據 132 while ((count = socketChannel.read (buffer)) > 0) { 133 buffer.flip(); 134 135 // send the data, don't assume it goes all at once 136 while (buffer.hasRemaining()) 137 { 138 //如果收到回車鍵,則在返回的字符前增加[echo]$字樣 139 if(buffer.get()==(char)13) 140 { 141 buffer.clear(); 142 buffer.put("[echo]$".getBytes()); 143 buffer.flip(); 144 145 } 146 socketChannel.write (buffer);//在Socket里寫數據 147 } 148 149 buffer.clear(); // 清空buffer 150 } 151 152 if (count < 0) { 153 // count<0,說明已經讀取完畢 154 socketChannel.close(); 155 } 156 } 157 158 159 private void doWork (SocketChannel channel)throws Exception 160 { 161 buffer.clear(); 162 buffer.put (" Hello,I am working,please input some thing,and i will echo to you! [echo] $".getBytes()); 163 buffer.flip(); 164 channel.write (buffer); 165 } 166 167 } |
使用:運行此程序,然后在控制臺輸入命令telnet localhost 23。
【程序輸出結果】如圖12-1所示。
圖12-1 輸出結果
【程序注解】
關于程序的解釋已經包含在程序里面了,在這里我們總結以下使用ServerSocket Channel開發服務端程序的過程:
(1)分配一個ServerSocketChannel。
(2)從ServerSocketChannel里獲得一個對應的ServerSocket。
(3)生成一個Selector實例。
(4)把ServerSocket綁定到端口上。
(5)設置ServerSocketChannel為非block模式(可選)。
(6)在Selector里注冊ServerSocetChannel。
(7)用一個無限循環語句始終查看Selector里是否有IO準備就緒的channel。如果有,就執行對應的處理,如果沒有,繼續循環。
小 結
在本章我們主要介紹了Java中的網絡編程。Java一開始就是一種網絡編程語言,到后來才應用到各個方面,所以在Java中進行網絡編程遠比在C/C++中方便。
我們介紹了幾個在網絡編程中很重要的類,如InetAddress、URL、URLConnection、Socket、 ServerSocket、DatagramSocket、DatagramPacket、MulticastSocket等。這些類包含了進行基本網絡編程的所有內容。要熟練地應用這些類,關鍵還是要多多練習。
基于套接字的編程基本上是客戶/服務器模式,我們具體介紹了編寫這種模式的步驟。在實例方面,我們給出了一個基于TCP的套接字客戶/服務器程序,與此相對應,還給出了基于UDP的客戶/服務器程序。兩者的模式是很相似的,其實這也就是編寫客戶/服務器程序的一般模式。 (T111)
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
