netty系列之:文本聊天室
文章目錄
- 簡介
- 聊天室的工作流程
- 文本處理器
- 初始化ChannelHandler
- 真正的消息處理邏輯
- 總結(jié)
簡介
經(jīng)過之前的系列文章,我們已經(jīng)知道了netty的運(yùn)行原理,還介紹了基本的netty服務(wù)搭建流程和消息處理器的寫法。今天本文會給大家介紹一個更加復(fù)雜的例子,文本聊天室。
聊天室的工作流程
今天要介紹的是文本聊天室,對于文本聊天室來說,首先需要建立一個服務(wù)器,用于處理各個客戶端的連接,對于客戶端來說,需要建立和服務(wù)器的連接,然后向服務(wù)器輸入聊天信息。服務(wù)器收到聊天信息之后,會對消息進(jìn)行響應(yīng),并將消息返回至客戶端,這樣一個聊天室的流程就完成了。
文本處理器
之前的文章中,我們有提到過,netty的傳輸只支持ByteBuf類型,對于聊天室直接輸入的字符串是不支持的,需要對字符串進(jìn)行encode和decode轉(zhuǎn)換。
之前我們介紹的encode和decode的類叫做ObjectDecoder和ObjectEncoder。今天我們再介紹兩個專門處理字符串的StringDecoder和StringEncoder。
StringEncoder要比ObjectEncoder簡單很多,因?yàn)閷τ趯ο髞碚f,我們還需要在Byte數(shù)組的頭部設(shè)置Byte數(shù)組的大小,從而保證對象所有數(shù)據(jù)讀取正確。對于String來說,就比較簡單了,只需要保證一次讀入的數(shù)據(jù)都是字符串即可。
StringEncoder繼承自MessageToMessageEncoder,其核心的encode代碼如下:
protected void encode(ChannelHandlerContext ctx, CharSequence msg, List<Object> out) throws Exception {if (msg.length() == 0) {return;}out.add(ByteBufUtil.encodeString(ctx.alloc(), CharBuffer.wrap(msg), charset));}從上面的代碼可以看出,核心實(shí)際上是調(diào)用了ByteBufUtil.encodeString方法,將String轉(zhuǎn)換成了ByteBuf。
對于字符串編碼來說,還需要界定一個編碼的范圍,比如我們需要知道需要一次編碼多少字符串,一般來說我們通過回車符來界定一次字符串輸入的結(jié)束。
netty也提供了這樣的非常便利的類叫做DelimiterBasedFrameDecoder,通過傳入不同的Delimiter,我們可以將輸入拆分成不同的Frame,從而對一行字符串進(jìn)行處理。
new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()))我再看一下StringDecoder的核心代碼,StringDecoder繼承自MessageToMessageDecoder:
protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception {out.add(msg.toString(charset));}通過調(diào)用ByteBuf的toString方法,將BuyteBuf轉(zhuǎn)換成為字符串,并且輸出到channel中。
初始化ChannelHandler
在initChannel的時候,我們需要向ChannelPipeline中添加有效的Handler。對于本例來說,需要添加StringDecoder、StringEncoder、DelimiterBasedFrameDecoder和真正處理消息的自定義handler。
我們將初始化Pipeline的操作都放在一個新的ChatServerInitializer類中,這個類繼承自ChannelInitializer,其核心的initChannel方法如下:
public void initChannel(SocketChannel ch) throws Exception {ChannelPipeline pipeline = ch.pipeline();// 添加行分割器pipeline.addLast(new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));// 添加String Decoder和String Encoder,用來進(jìn)行字符串的轉(zhuǎn)換pipeline.addLast(DECODER);pipeline.addLast(ENCODER);// 最后添加真正的處理器pipeline.addLast(SERVER_HANDLER);}ChatServerInitializer在Bootstrap中的childHandler中進(jìn)行添加:
childHandler(new ChatServerInitializer())真正的消息處理邏輯
有了上面的邏輯之后,我們最后只需要專注于真正的消息處理邏輯即可。
這里我們的邏輯是當(dāng)客戶端輸入“再見”的時候,就關(guān)閉channel,否則就將消息回寫給客戶端。
其核心邏輯如下:
public void channelRead0(ChannelHandlerContext ctx, String request) throws Exception {// 如果讀取到"再見"就關(guān)閉channelString response;// 判斷是否關(guān)閉boolean close = false;if (request.isEmpty()) {response = "你說啥?\r\n";} else if ("再見".equalsIgnoreCase(request)) {response = "再見,我的朋友!\r\n";close = true;} else {response = "你是不是說: '" + request + "'?\r\n";}// 寫入消息ChannelFuture future = ctx.write(response);// 添加CLOSE listener,用來關(guān)閉channelif (close) {future.addListener(ChannelFutureListener.CLOSE);}}通過判斷客戶端的出入,來設(shè)置是否關(guān)閉按鈕,這里的關(guān)閉channel是通過向ChannelFuture中添加ChannelFutureListener.CLOSE來實(shí)現(xiàn)的。
ChannelFutureListener.CLOSE是一個ChannelFutureListener,它會在channel執(zhí)行完畢之后關(guān)閉channel,事實(shí)上這是一個非常優(yōu)雅的關(guān)閉方式。
ChannelFutureListener CLOSE = new ChannelFutureListener() {@Overridepublic void operationComplete(ChannelFuture future) {future.channel().close();}};對于客戶端來說,其核心就是從命令行讀取輸入,這里使用InputStreamReader接收命令行輸入,并使用BufferedReader對其緩存。
然后將命令行輸入通過調(diào)用 ch.writeAndFlush寫入到channel中,最后監(jiān)聽命令行輸入,如果監(jiān)聽到“再見“,則等待server端關(guān)閉channel,其核心代碼如下。
// 從命令行輸入ChannelFuture lastWriteFuture = null;BufferedReader in = new BufferedReader(new InputStreamReader(System.in));for (;;) {String line = in.readLine();if (line == null) {break;}// 將從命令行輸入的一行字符寫到channel中l(wèi)astWriteFuture = ch.writeAndFlush(line + "\r\n");// 如果輸入'再見',則等待server端關(guān)閉channelif ("再見".equalsIgnoreCase(line)) {ch.closeFuture().sync();break;}}// 等待所有的消息都寫入channel中if (lastWriteFuture != null) {lastWriteFuture.sync();}總結(jié)
經(jīng)過上面的介紹,一個簡單的聊天室就建成了。后續(xù)我們會繼續(xù)探索更加復(fù)雜的應(yīng)用,希望大家能夠喜歡。
本文的例子可以參考:learn-netty4
本文已收錄于 http://www.flydean.com/10-netty-chat/
最通俗的解讀,最深刻的干貨,最簡潔的教程,眾多你不知道的小技巧等你來發(fā)現(xiàn)!
歡迎關(guān)注我的公眾號:「程序那些事」,懂技術(shù),更懂你!
總結(jié)
以上是生活随笔為你收集整理的netty系列之:文本聊天室的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: netty系列之:自动重连
- 下一篇: netty系列之:使用UDP协议