上节课完成了netty的后端搭建,搞定了简单的http请求响应,今天来结合前端websocket来完成群聊功能。话不多说先上图:前端构建不使用复杂构建工具直接静态页面走起使用了zui样式库 http://zui.sexy/?#/,非常不错,有好多模板。我使用的是聊天模板改造 <link rel=“stylesheet” href=“https://cdnjs.cloudflare.com/ajax/libs/zui/1.8.1/css/zui.min.css"> <link rel=“stylesheet” href=“zui-theme.css”>主体部分<div class=“container”> <h1>mike多人聊天室,等你来聊</h1> <div class=“comments”> <section class=“comments-list” id=“chatlist”> <div class=“comment”> <a href=”###" class=“avatar”> <i class=“icon-user icon-2x”></i> </a> <div class=“content”> <div><strong>其他人</strong></div> <div class=“text”>其他人的聊天内容</div> </div> </div> <div class=“comment”> <a href="###" class=“avatar pull-right”> <i class=“icon-user icon-2x”></i> </a> <div class=“content pull-right”> <div><strong>我</strong></div> <div class=“text”>我说话的内容</div> </div> </div> </section> <footer> <div class=“reply-form” id=“commentReplyForm1”> <form class=“form”> <div class=“form-group”> <div class=“input-control has-label-left”> <input id=“userName” type=“text” class=“form-control” placeholder=""> <label for=“inputAccountExample2” class=“input-control-label-left”>昵称:</label> </div> </div> </form> <a href="###" class=“avatar”><i class=“icon-user icon-2x”></i></a> <form class=“form”> <div class=“form-group”> <textarea id=“inputMsg” class=“form-control new-comment-text” rows=“2” value="" placeholder=“开始聊天… 输入enter 发送消息”></textarea> </div> </form> </div> </footer></div></div>引入依赖js <!– ZUI Javascript 依赖 jQuery –> <script src=“https://cdnjs.cloudflare.com/ajax/libs/zui/1.8.1/lib/jquery/jquery.js"></script> <!– ZUI 标准版压缩后的 JavaScript 文件 –> <script src=“https://cdnjs.cloudflare.com/ajax/libs/zui/1.8.1/js/zui.min.js"></script>websocket的js代码以及业务代码 <script type=“text/javascript”> window.CHAT = { me: “”, WS:{}, init: function () { if (window.WebSocket) { this.WS = new WebSocket(“ws://A156B7L58CCNY4B:8090/ws”); this.WS.onmessage = function(event) { var data = event.data; console.log(“收到数据:” + data); //显示其他人的聊天信息 console.log(CHAT.me); console.log(data.split(”:”)[0]); if(CHAT.me != data.split(":")[0]) { appendOtherchat(data); } }, this.WS.onclose = function(event) { console.log(“连接关闭”); }, this.WS.onopen = function(evt) { console.log(“Connection open …”); }, this.WS.onerror = function(event) { console.log(“连接失败….”); } } else { alert(“您的浏览器不支持聊天,请更换浏览器”); } }, chat:function (msg) { this.WS.send(msg); } } CHAT.init(); function Trim(str) { return str.replace(/(^\s*)|(\s*$)/g, “”); } function appendMy (msg) { //拼接自己的聊天内容 document.getElementById(‘chatlist’).innerHTML+="<div class=‘comment’><a class=‘avatar pull-right’><i class=‘icon-user icon-2x’></i></a><div class=‘content pull-right’><div><strong>我</strong></div><div class=‘text’>"+msg+"</div></div></div>"; } function appendOtherchat(msg) { //拼接别人的聊天信息到聊天室 var msgs = msg.split(":"); document.getElementById(‘chatlist’).innerHTML+="<div class=‘comment’><a class=‘avatar’><i class=‘icon-user icon-2x’></i></a><div class=‘content’><div><strong>"+msgs[0]+"</strong></div><div class=‘text’>"+msgs[1]+"</div></div></div>"; } document.getElementById(‘inputMsg’).addEventListener(‘keyup’, function(event) { if (event.keyCode == “13”) { //回车执行查询 var inputMsg = document.getElementById(‘inputMsg’).value; if (inputMsg == null || Trim(inputMsg) == "" ) { alert(“请输入聊天消息”); } else { var userName = document.getElementById(‘userName’).value; if (userName == null || userName == ‘’) { alert(“请输入聊天昵称”); } else { //发送消息 定义消息格式 用户名:[消息] CHAT.chat(userName+":"+inputMsg); //记录我的昵称 CHAT.me = userName; appendMy(inputMsg); //发送完清空输入 document.getElementById(‘inputMsg’).focus(); document.getElementById(‘inputMsg’).value=""; } } } }); </script>都有注释就不解释了自己看后端服务改造ChatHandler改造,判断websocket响应 /** * 读取客户端发送的消息,并将信息转发给其他客户端的 Channel。 / @Override protected void channelRead0(ChannelHandlerContext ctx, Object request) throws Exception { if (request instanceof FullHttpRequest) { //是http请求 FullHttpResponse response = new DefaultFullHttpResponse( HttpVersion.HTTP_1_1,HttpResponseStatus.OK , Unpooled.wrappedBuffer(“Hello netty” .getBytes())); response.headers().set(“Content-Type”, “text/plain”); response.headers().set(“Content-Length”, response.content().readableBytes()); response.headers().set(“connection”, HttpHeaderValues.KEEP_ALIVE); ctx.channel().writeAndFlush(response); } else if (request instanceof TextWebSocketFrame) { // websocket请求 String userId = ctx.channel().id().asLongText(); System.out.println(“收到客户端”+userId+":"+((TextWebSocketFrame)request).text()); //发送消息给所有客户端 channels.writeAndFlush(new TextWebSocketFrame(((TextWebSocketFrame)request).text())); //发送给单个客户端 //ctx.channel().writeAndFlush(new TextWebSocketFrame(((TextWebSocketFrame)request).text())); } } ChatServerInitializer改造,加入WebSocket @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); //websocket协议本身是基于http协议的,所以这边也要使用http解编码器 pipeline.addLast(new HttpServerCodec()); //以块的方式来写的处理器 pipeline.addLast(new ChunkedWriteHandler()); //netty是基于分段请求的,HttpObjectAggregator的作用是将请求分段再聚合,参数是聚合字节的最大长度 pipeline.addLast(new HttpObjectAggregator(102410241024)); //ws://server:port/context_path //ws://localhost:9999/ws //参数指的是contex_path pipeline.addLast(new WebSocketServerProtocolHandler("/ws",null,true,65535)); //自定义handler pipeline.addLast(new ChatHandler()); System.out.println(“ChatClient:"+ch.remoteAddress() +“连接上”); }改造完成启动后端服务,访问你的前端静态页面就可以和小伙伴聊天了。其实后端群聊很简单,就是把一个用户的输入消息,返回给所有在线客户端,前端去负责筛选显示。自己动手照着搞10分钟就能完成。实现功能输入聊天昵称开始聊天聊天消息不为空才能发送发送完自动清空输入,且聚焦输入框自己的消息显示在左侧,其他人的消息在右侧别忘了关注我 mike啥都想搞求关注啊。