在线聊天室的实现(2)--基于Netty 4.x的Echo服务器实现
前言:
就如前文所讲述的, 聊天室往往是最基本的网络编程的学习案例. 本文以WebSocket为底层协议, 实现一个简单的基于web客户端的Echo服务.
服务器采用Netty 4.x来实现, 源于其对websocket的超强支持, 基于卓越的性能和稳定.
本系列的文章链接如下:
1). websocket协议和javascript版的api
要点提示:
Netty作为高性能网络编程框架, 其所有的网络IO操作皆为异步方式驱动. 而其核心的概念之一: ChannelHandler. 由一组ChannelHandler构成了ChannelPipeline, 决定了其编解码(Codec)/数据流(DataFlow)/业务处理(Logic Handler)的具体行为.
ChannelHanlder的自由组合和清晰的职责划分, 让Netty更加的灵活和重要.
WebSocket协议包括握手和数据传输这两个阶段. 前者的握手是基于HTTP/HTTPS协议的, 而后者的数据传输则基于TCP的双向通讯模式. 数据以Frame的方式来组织和交互.
本文不是Netty的学习文章, 这边就略为带过, 具体见后边的解释代码.
服务端:
基于之上的要点提要, 我们迅速来进行服务端的代码编写.
使用netty 4.x版本, 其maven的依赖配置如下:
1 2 3 4 5 | <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version> 4.0 . 29 .Final</version> </dependency> |
服务端的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap.group(bossGroup, workerGroup) .channel(NioServerSocketChannel. class ) .childHandler( new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { // pipeline的设置, 参看下面 } }); ChannelFuture f = serverBootstrap.bind( 8123 ).sync(); f.channel().closeFuture().sync(); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } |
注: 这边是主体的服务器配置和启动代码, 其一如既然的简洁.
核心的pipeline设置代码如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | ChannelPipeline cp = socketChannel.pipeline(); // *) 支持http协议的解析 cp.addLast( new HttpServerCodec()); cp.addLast( new HttpObjectAggregator( 65535 )); // *) 对于大文件支持 chunked方式写 cp.addLast( new ChunkedWriteHandler()); // *) 对websocket协议的处理--握手处理, ping/pong心跳, 关闭 cp.addLast( new WebSocketServerProtocolHandler( "/echoserver" )); // *) 对TextWebSocketFrame的处理 cp.addLast( new SimpleChannelInboundHandler<TextWebSocketFrame>() { @Override protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception { // *) echo 逻辑 ctx.writeAndFlush( new TextWebSocketFrame(msg.text())); } }); |
注: HttpServerCodec和HttpObjectAggregator已经帮我们封装好了WebSocket的握手FullHttpRequest/FullHttpResponse包和各类数据Frame包. WebSocketServerProtocolHandler隐藏了握手的细节处理, 以及心跳处理和关闭响应. 多个ChannelHanlder的叠加和WebSocket协议本身的复杂是密切先关的.
客户端:
这边只是个演示项目, 因此尽量简洁地去实现.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | <div style= "margin:0 auto; width: 800px;" > <textarea id= "taMessages" style= "width: 360px; height: 200px;" readonly ></textarea> <br /> <input id= "btnMessage" type= "text" style= "float:left; width:300px;" /> <input id= "btnSend" type= "button" value= "Send" disabled= "disabled" onclick= "sendMessage();" /> </div> <script> /* 注意浏览器js的执行顺序 */ var wsServer = 'ws://localhost:8123/echoserver' ; //服务器地址 var websocket = new WebSocket(wsServer); //创建WebSocket对象 websocket.onopen = function (evt) { document.getElementById( "btnSend" ).disabled = false ; } websocket.onmessage = function (evt) { document.getElementById( "taMessages" ).value += evt.data; } websocket.onclose = function (evt) { } websocket.onerror = function (evt) { } function sendMessage() { var message = document.getElementById( 'btnMessage' ).value; if ( websocket.readyState == WebSocket.OPEN ) { websocket.send(message); } document.getElementById( 'btnMessage' ).value = '' ; } </script> |
注: 发送数据到服务端, 然后把服务端返回的数据追加到文本区域中.
效果:
在chrome浏览器中, 效果如下:
点击send按钮后, 经服务器返回其消息.
消息在大文本区域中展示. 看来Echo服务一切正常.
其实这是个悲伤的故事, 你觉得呢?
写在最后:
如果你觉得这篇文章对你有帮助, 请小小打赏下. 其实我想试试, 看看写博客能否给自己带来一点小小的收益. 无论多少, 都是对楼主一种由衷的肯定.
posted on 2015-08-05 19:25 mumuxinfei 阅读(4057) 评论(0) 编辑 收藏 举报
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
2014-08-05 Thrift 个人实战--RPC服务的发布订阅实现(基于Zookeeper服务)