Netty专题-(3)NIO网络编程
之前在上一章Netty專題-(2)NIO三大核心中介紹了NIO的三大核心類Selector 、 Channel 和 Buffer,這一章我們將利用這些核心進行編程實現相關的一些功能。在正式進入編程之前我們還需要介紹一些概念。
1 NIO網絡編程關系圖
(1)當客戶端連接時,會通過 ServerSocketChannel 得到 SocketChannel
(2)Selector 進行監聽 select 方法, 返回有事件發生的通道的個數
(3)將 socketChannel 注冊到 Selector 上, register(Selector sel, int ops), 一個 selector 上可以注冊多個 SocketChannel
(4)注冊后返回一個 SelectionKey, 會和該 Selector 關聯(集合)
(5)進一步得到各個 SelectionKey (有事件發生)
(6)在通過 SelectionKey 反向獲取 SocketChannel , 方法 channel()
(7)通過得到的 channel完成業務處理
2 NIO網絡編程入門案例
為了讓大家更好的對上面的流程理解,這里首先給大家一個樣例,使用NIO實現服務器端和客戶端之間的數據簡單通訊(非阻塞),希望大家好好看完和自己動手操作,這樣對大家理解 NIO 非阻塞網絡編程機制有很大的幫助。我們直接上代碼:
首先是服務端:
然后是客戶端:
//得到一個網絡通道SocketChannel socketChannel = SocketChannel.open();//設置非阻塞socketChannel.configureBlocking(false);//提供服務器端的ip 和 端口InetSocketAddress inetSocketAddress = new InetSocketAddress("127.0.0.1", 6666);//連接服務器if (!socketChannel.connect(inetSocketAddress)) {while (!socketChannel.finishConnect()) {System.out.println("因為連接需要時間,客戶端不會阻塞,可以做其它工作..");}}//...如果連接成功,就發送數據String str = "hello, likangmin~";//Wraps a byte array into a bufferByteBuffer buffer = ByteBuffer.wrap(str.getBytes());//發送數據,將 buffer 數據寫入 channelsocketChannel.write(buffer);System.in.read();以上創建的步驟基本是按照之前介紹的流程走的,大家應該可以看的明白。之后我們首先啟動服務端;
然后啟動客戶端:
成功接收。
3 NIO相關類介紹
3.1 SelectionKey
(1)SelectionKey基本介紹
SelectionKey,表示 Selector 和網絡通道的注冊關系, 共四種:
在其源碼中表示為:
public static final int OP_READ = 1 << 0; public static final int OP_WRITE = 1 << 2; public static final int OP_CONNECT = 1 << 3; public static final int OP_ACCEPT = 1 << 4;(2)SelectionKey 相關方法
3.2 ServerSocketChannel
(1)ServerSocketChannel 在服務器端監聽新的客戶端 Socket 連接
(2)ServerSocketChannel相關方法
3.3 SocketChannel
(1)SocketChannel,網絡 IO 通道,具體負責進行讀寫操作。NIO 把緩沖區的數據寫入通道,或者把通道里的數 據讀到緩沖區。
(2)SocketChannel相關方法
4 NIO應用實例-群聊系統
了解了NIO的網絡編程以后,接下來我們具體實現一個比較難一點的要求:
實現的效果圖大致如下:
大家在往下看之前可以自己先動手實現一下,然后跟我這里給出的示例進行對比,這樣更有助于大家進一步理解 NIO 非阻塞網絡編程機制。
同樣的首先編寫服務端代碼:
然后編寫客戶端的代碼:
//定義相關的屬性private final String HOST = "127.0.0.1"; // 服務器的ipprivate final int PORT = 6667; //服務器端口private Selector selector;private SocketChannel socketChannel;private String username;//構造器, 完成初始化工作public GroupChatClient() throws IOException {selector = Selector.open();//連接服務器socketChannel = socketChannel.open(new InetSocketAddress("127.0.0.1", PORT));//設置非阻塞socketChannel.configureBlocking(false);//將channel 注冊到selectorsocketChannel.register(selector, SelectionKey.OP_READ);//得到usernameusername = socketChannel.getLocalAddress().toString().substring(1);System.out.println(username + " is ok...");}//向服務器發送消息public void sendInfo(String info) {info = username + " 說:" + info;try {socketChannel.write(ByteBuffer.wrap(info.getBytes()));}catch (IOException e) {e.printStackTrace();}}//讀取從服務器端回復的消息public void readInfo() {try {int readChannels = selector.select();if(readChannels > 0) {//有可以用的通道Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();while (iterator.hasNext()) {SelectionKey key = iterator.next();if(key.isReadable()) {//得到相關的通道SocketChannel sc = (SocketChannel) key.channel();//得到一個BufferByteBuffer buffer = ByteBuffer.allocate(1024);//讀取sc.read(buffer);//把讀到的緩沖區的數據轉成字符串String msg = new String(buffer.array());System.out.println(msg.trim());}}iterator.remove(); //刪除當前的selectionKey, 防止重復操作} else {//System.out.println("沒有可以用的通道...");}}catch (Exception e) {e.printStackTrace();}}然后在服務端的main方法中增加如下代碼:
//創建服務器對象GroupChatServer groupChatServer = new GroupChatServer();groupChatServer.listen();同樣的在客戶端的main方法中增加如下代碼:
//啟動我們客戶端GroupChatClient chatClient = new GroupChatClient();//啟動一個線程, 每個3秒,讀取從服務器發送數據new Thread() {public void run() {while (true) {chatClient.readInfo();try {Thread.currentThread().sleep(3000);}catch (InterruptedException e) {e.printStackTrace();}}}}.start();//發送數據給服務器端Scanner scanner = new Scanner(System.in);while (scanner.hasNextLine()) {String s = scanner.nextLine();chatClient.sendInfo(s);}然后就可以啟動服務端了:
之后我們可以啟動多個客戶端進行測試,可能有的沒有開啟相關的配置,將客戶端run起來以后不能開啟第二個,這里只需要進行一下簡單的配置即可,筆者用的是IDEA,所以介紹一下IDEA的開啟方式。大家按照我的方式即可。
我們開啟兩個客戶端進行測試:
然后發消息進行測試,在63886中發送:你好,我是63886:
可以看到服務器對消息進行了轉發:
同時63939也收到了來自63886的消息:
同樣的,我們在客戶端63939中發送消息:你好,我是63939:
同樣服務器也進行了轉發:
客戶端63886也收到了63939的消息:
使用NIO實現簡單的群聊基本已經實現,大家看一下自己的與我這里提供的有什么差別。當然有些功能可能不完善,這里只是作為一個簡單的案例,太復雜的功能可以學習完以后在進行添加。下一章我們將介紹一下NIO的零拷貝。
猜你感興趣:
Netty專題-(1)初識Netty
Netty專題-(2)NIO三大核心
Netty專題-(3)NIO網絡編程
相關專題持續更新中,敬請期待…
更多文章請點擊:更多…
總結
以上是生活随笔為你收集整理的Netty专题-(3)NIO网络编程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Netty专题-(2)NIO三大核心
- 下一篇: ZooKeeper概述与原理