当前位置: 首页 > 后端技术 > Java

Netty系列:文字聊天室介绍

时间:2023-04-01 19:11:10 Java

经过前面的系列文章,我们已经知道了netty的运行原理,同时也介绍了netty的基本服务构建流程和消息处理器的编写方法。今天这篇文章给大家介绍一个比较复杂的例子,文字聊天室。聊天室的工作流程今天我们来介绍一下文字聊天室。对于文字聊天室,我们首先需要建立一个服务器来处理各个客户端的连接。对于客户端来说,我们需要和服务器建立连接,然后向服务器发送聊天信息。服务器接收到聊天信息后,会响应消息并将消息返回给客户端,这样一个聊天室的过程就完成了。在上一篇文字处理器的文章中,我们提到过netty的传输只支持ByteBuf类型,不支持聊天室直接输入的字符串,需要对字符串进行编码和解码。我们之前介绍的encode类和decode类分别叫做ObjectDecoder和ObjectEncoder。今天介绍两个专门处理字符串的StringDecoder和StringEncoder。StringEncoder比ObjectEncoder要简单的多,因为对于一个对象,我们还需要在Byte数组的头部设置Byte数组的大小,这样才能保证正确读取到该对象的所有数据。对于String来说,比较简单,只需要保证一次读入的数据都是字符串即可。StringEncoder继承自MessageToMessageEncoder,其核心编码代码如下:}out.add(ByteBufUtil.encodeString(ctx.alloc(),CharBuffer.wrap(msg),字符集));从上面的代码可以看出,内核其实是调用了ByteBufUtil.encodeString方法将String转成ByteBuf。对于字符串编码,还需要定义一个编码范围。例如,我们需要知道一次需要编码多少个字符串。一般来说,我们使用回车来定义字符串输入的结束。Netty也提供了这样一个非常方便的类,叫做DelimiterBasedFrameDecoder。通过传入不同的Delimiter,我们可以将输入拆分成不同的Frames来处理一行字符串。newDelimiterBasedFrameDecoder(8192,Delimiters.lineDelimiter()))我再来看看StringDecoder的核心代码。StringDecoder继承自MessageToMessageDecoder:protectedvoiddecode(ChannelHandlerContextctx,ByteBufmsg,Listout)throwsException{out.add(msg.toString(charset));}通过调用ByteBuf的toString方法,将BuyteBuf转成字符串输出到通道中。初始化ChannelHandler在初始化Channel的时候,我们需要给ChannelPipeline添加一个有效的Handler。对于此示例,您需要添加StringDecoder、StringEncoder、DelimiterBasedFrameDecoder和一个实际处理消息的自定义处理程序。我们把初始化Pipeline的操作放在一个新的ChatServerInitializer类中,它继承自ChannelInitializer,其核心的initChannel方法如下://添加分行器pipeline.addLast(newDelimiterBasedFrameDecoder(8192,Delimiters.lineDelimiter()));//为字符串转换添加StringDecoder和StringEncoderpipeline.addLast(DECODER);pipeline.addLast(ENCODER);//最后添加真正的处理器pipeline.addLast(SERVER_HANDLER);}ChatServerInitializer在Bootstrap的childHandler中添加:childHandler(newChatServerInitializer())真正的消息处理逻辑有了上面的逻辑,我们只需要关注真正的消息处理逻辑即可。我们这里的逻辑是,当客户端输入“再见”时,通道关闭,否则将消息写回客户端。其核心逻辑如下:publicvoidchannelRead0(ChannelHandlerContextctx,Stringrequest)throwsException{//如果读到“goodbye”则关闭通道Stringresponse;//判断是否关闭booleanclose=false;if(request.isEmpty()){response="你说什么?\r\n";}elseif("Goodbye".equalsIgnoreCase(request)){response="再见,我的朋友!\r\n";关闭=真;}else{response="Didyousay:'"+request+"'?\r\n";}//写消息ChannelFuturefuture=ctx.write(response);//添加关闭监听器以关闭通道if(close){future.addListener(ChannelFutureListener.CLOSE);}}通过判断客户端的访问来设置是否关闭按钮。这里的关闭通道是通过在ChannelFuture中添加ChannelFutureListener.CLOSE来实现的。ChannelFutureListener.CLOSE是一个ChannelFutureListener,在通道执行完后关闭通道。事实上,这是一种非常优雅的关闭方式。ChannelFutureListenerCLOSE=newChannelFutureListener(){@OverridepublicvoidoperationComplete(ChannelFuturefuture){future.channel().close();}};对于客户端来说,核心是从命令行读取输入,这里使用InputStreamReader接收命令行输入,并使用BufferedReader对其进行缓冲。然后通过调用ch.writeAndFlush将命令行输入写入通道,最后监听命令行输入。如果听到“再见”,等待服务器关闭频道。核心代码如下。//从命令行输入ChannelFuturelastWriteFuture=null;BufferedReaderin=newBufferedReader(newInputStreamReader(System.in));对于(;;){字符串行=in.readLine();if(line==null){中断;}//将命令行输入的一行字符写入通道lastWriteFuture=ch.writeAndFlush(line+"\r\n");//如果你输入'goodbye',等待服务器关闭通道休息;}}//等待所有消息写入通道if(lastWriteFuture!=null){lastWriteFuture.sync();}总结过程通过上面的介绍,一个简单的聊天室就搭建好了。未来我们会继续探索更复杂的应用,希望大家喜欢。本文示例可参考:learn-netty4本文已收录于http://www.flydean.com/10-netty-chat/最流行的解读,最深刻的干货,最简洁的教程,很多你不知道的小技巧等你来发现!欢迎关注我的公众号:《程序那些事儿》,懂技术,更懂你!