前言:
就如前文所讲述的, 聊天室往往是最基本的网络编程的学习案例. 本文以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的依赖配置如下:
<dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.0.29.Final</version> </dependency>
服务端的代码如下:
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设置代码如下所示:
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协议本身的复杂是密切先关的.
客户端:
这边只是个演示项目, 因此尽量简洁地去实现.
<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服务一切正常.
其实这是个悲伤的故事, 你觉得呢?
写在最后:
如果你觉得这篇文章对你有帮助, 请小小打赏下. 其实我想试试, 看看写博客能否给自己带来一点小小的收益. 无论多少, 都是对楼主一种由衷的肯定.