在很多的网站中都嵌入有聊天功能,最理想的方式就是使用WebSocket来开发,屏幕面前的你如果不清楚WebSocket的作用可以自己去百度一下,Netty提供了WebSocket支持,这篇文章将使用Netty作为服务器,使用WebSocket开发一个简易的聊天室系统。
服务端
导入依赖
<dependencies><dependency><groupId>io.netty</groupId><artifactId>netty-all</artifactId><version>4.1.42.Final</version></dependency></dependencies>
Netty提供了 WebSocketServerProtocolHandler ,它能够把平台的Http协议升级为WebSocket的WS协议,服务端代码如下
publicclassNettySocketServer { publicstaticvoidmain(String[] args) { NioEventLoopGroupbossGroup=newNioEventLoopGroup(); NioEventLoopGroupworkGroup=newNioEventLoopGroup(); ServerBootstrapbootstrap=newServerBootstrap(); try { bootstrap.group(bossGroup, workGroup); bootstrap.channel(NioServerSocketChannel.class); bootstrap.childHandler(newChannelInitializer<SocketChannel>() { protectedvoidinitChannel(SocketChannelchannel) throwsException { ChannelPipelinepipeline=channel.pipeline(); //设置http编码器pipeline.addLast(newHttpServerCodec()); //支持以块的方式写数据,添加块处理器pipeline.addLast(newChunkedWriteHandler()); //http请求是分段的,通过该处理器聚合分段pipeline.addLast(newHttpObjectAggregator(8192)); //支持websocket长连接,添加SocketServer处理器,把http升级为ws协议//客户端请求方式为:ws://localhost:8000/hellopipeline.addLast(newWebSocketServerProtocolHandler("/hello")); //处理请求,执行业务的Hnadlerpipeline.addLast(newMyWebSorcketServerHandler()); } }); //启动服务器ChannelFuturesync=bootstrap.bind(8000).sync(); sync.channel().closeFuture().sync(); }catch (Exceptione){ e.printStackTrace(); }finally { bossGroup.shutdownGracefully(); workGroup.shutdownGracefully(); } } }
- 因为使用http,所以需要通过pipeline添加http编码器 HttpServerCodec
- WebSocketServerProtocolHandler : WebSocket支持,把http升级为ws协议
- MyWebSorcketServerHandler :该处理器是服务器处理请求的处理器
编写请求处理器
//消息进站处理 ,TextWebSocketFrame:WebSokcet消息是以“帧 Frame”的方式参数,该类是针对Text的数据publicclassMyWebSorcketServerHandlerextendsSimpleChannelInboundHandler<TextWebSocketFrame> { //保存所有客户端privatestaticChannelGroupchannels=newDefaultChannelGroup(GlobalEventExecutor.INSTANCE); privateSimpleDateFormatdateFormat=newSimpleDateFormat("mm:ss"); //读取消息protectedvoidchannelRead0(ChannelHandlerContextctx, TextWebSocketFramemsg) throwsException { Stringmessage=dateFormat.format(newDate())+":%s:"+msg.text(); System.out.println("收到消息: "+ctx.channel().remoteAddress()+":"+message); StringsendMsg=String.format(message, ctx.channel().remoteAddress()); //把消息广播给所有的客户端channels.writeAndFlush(newTextWebSocketFrame(sendMsg)); } publicvoidhandlerAdded(ChannelHandlerContextctx) throwsException { Stringmessage=dateFormat.format(newDate())+":"+ctx.channel().remoteAddress()+" 加入聊天室"; //添加客户端的集合channels.add(ctx.channel()); //自动把消息广播给其客户端channels.writeAndFlush(newTextWebSocketFrame(message)); System.out.println("服务器收到链接 ID="+ctx.channel().id().asLongText()); } publicvoidhandlerRemoved(ChannelHandlerContextctx) throwsException { Stringmessage=dateFormat.format(newDate())+":"+ctx.channel().remoteAddress()+" 断开连接"; channels.writeAndFlush(newTextWebSocketFrame(message)); } //客户端退出publicvoidchannelInactive(ChannelHandlerContextctx) throwsException { Stringmessage=dateFormat.format(newDate())+":"+ctx.channel().remoteAddress()+" 退出聊天室"; channels.writeAndFlush(newTextWebSocketFrame(message)); System.out.println(message); } publicvoidexceptionCaught(ChannelHandlerContextctx, Throwablecause) throwsException { System.out.println("发生异常 ID="+ctx.channel().id().asLongText()+";msg="+cause.getMessage()); ctx.channel().close(); } }
- DefaultChannelGroup : 该group用来存储所有的客户端的channel
- channelRead0 : 当接收到请求拿到数据的方法,需要把数据广播给所有客户端
客户端编写
<htmllang="en"><head><metacharset="UTF-8"><title>WebSocket测试</title></head><body><textareaid="viewBody"rows="10"style="width: 500px"></textarea><br/><inputid="sendInput"type="text"style="width: 450px"><buttonid="sendBtn"onclick="send()">发送</button></body><script>varsocket; if(!window.WebSocket){ alert("浏览器不支持"); }else{ //创建客户端socket=newwindow.WebSocket("ws://localhost:8000/hello"); //当收到消息socket.onmessage=function (event) { varinput=document.getElementById("viewBody"); input.value=input.value+"\n"+event.data; } //打开链接socket.onopen=function (event) { varinput=document.getElementById("viewBody"); input.value="成功加入聊天室"+"\n"; } //关闭链接socket.onclose=function (event) { varinput=document.getElementById("viewBody"); input.value=input.value+"\n"+"退出聊天室"; } functionsend(){ if(socket&&socket.readyState==WebSocket.OPEN){ letsendInput=document.getElementById("sendInput"); letvalue=sendInput.value; socket.send(value); sendInput.value=""; } } } </script></html>
测试效果