Netty Websocket多人多房间聊天室Demo
Netty Websocket多人多房間聊天室Demo
描述:可任意輸入自己的昵稱和要加入的聊天室名,即可加入聊天室進行聊天,在同一個聊天室內的所有人的發言每個人都會立即收到
項目使用springboot,源碼https://download.csdn.net/download/HumorChen99/15843430
https://gitee.com/HumorChen/netty_websocket_chatroom_demo
主要是這么些步驟:
- 處理器里分為兩個自定義的,一個是處理http的一個是處理ws消息的。
因為創建ws的時候第一次是發的一個http請求,所以我們利用這次http請求來完成加入聊天室的操作,讓發起ws的時候url里帶入昵稱和聊天室名字的參數,然后握手之前先得到這些參數用來初始化,然后繼續握手創建管道,然后把管道放入到這個聊天室的管道組,如果某個管道來了消息,那么要群發給管道里的每個人(要從管道知道是哪個聊天室的,以及這個用戶名稱,那么我們在初始化的時候將信息存入了管道的屬性里去了attr)。
處理消息的時候是責任鏈模式,每個handler依次執行,直到某個handler處理掉這個消息
-
依賴
<!--netty的依賴集合,都整合在一個依賴里面了--> <dependency><groupId>io.netty</groupId><artifactId>netty-all</artifactId><version>4.1.25.Final</version> </dependency> -
NettyWebServer 創建Netty服務器
package com.example.netty_websocket_chatroom_demo.server;import com.example.netty_websocket_chatroom_demo.handler.ChatHandler; import com.example.netty_websocket_chatroom_demo.handler.HttpHandler; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.http.HttpObjectAggregator; import io.netty.handler.codec.http.HttpServerCodec; import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler; import io.netty.handler.stream.ChunkedWriteHandler;public class NettyWebServer {public static void run(){EventLoopGroup boss=new NioEventLoopGroup();EventLoopGroup worker=new NioEventLoopGroup();try {ServerBootstrap bootstrap=new ServerBootstrap();bootstrap.group(boss,worker).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {ChannelPipeline pipeline=ch.pipeline();//http編碼解碼器pipeline.addLast(new HttpServerCodec());//分塊傳輸pipeline.addLast(new ChunkedWriteHandler());//塊聚合pipeline.addLast(new HttpObjectAggregator(1024*1024));//進入聊天室的http處理器pipeline.addLast(new HttpHandler());//協議處理器 // pipeline.addLast(new WebSocketServerProtocolHandler("/ws"));//自定義的處理器pipeline.addLast(new ChatHandler());}});ChannelFuture channelFuture=bootstrap.bind(8081).sync();channelFuture.channel().closeFuture().sync();}catch (Exception e){e.printStackTrace();}finally {boss.shutdownGracefully();worker.shutdownGracefully();}} } -
HttpHandler用來處理升級協議的http請求并同時處理加入聊天室的操作
package com.example.netty_websocket_chatroom_demo.handler;import com.example.netty_websocket_chatroom_demo.constant.ChatroomConst; import com.example.netty_websocket_chatroom_demo.data.ChatRoom; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.*; import io.netty.channel.group.ChannelGroup; import io.netty.channel.group.DefaultChannelGroup; import io.netty.handler.codec.http.*; import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; import io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker; import io.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory; import io.netty.util.AttributeKey; import io.netty.util.CharsetUtil; import io.netty.util.concurrent.ImmediateEventExecutor;import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.util.Map;public class HttpHandler extends SimpleChannelInboundHandler<FullHttpRequest> {private WebSocketServerHandshaker handshaker;static {AttributeKey.newInstance(ChatroomConst.IP);AttributeKey.newInstance(ChatroomConst.USERNAME);AttributeKey.newInstance(ChatroomConst.CHATROOM);}@Overrideprotected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest req) throws Exception {QueryStringDecoder decoder=new QueryStringDecoder(req.uri());String chatroomName=decoder.parameters().get("chatroom").get(0);String nickName=decoder.parameters().get("nickname").get(0);if(chatroomName==null || "".equals(chatroomName) || nickName==null || "".equals(nickName) || !req.decoderResult().isSuccess()||!"websocket".equals(req.headers().get("Upgrade"))){FullHttpResponse response=new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST);ChannelFuture future=ctx.channel().writeAndFlush(response);future.addListener(ChannelFutureListener.CLOSE);}else{//獲取IPString ip=((InetSocketAddress)ctx.channel().remoteAddress()).getAddress().getHostAddress();if("0:0:0:0:0:0:0:1".equals(ip)){ip="127.0.0.1";}String username=nickName+"("+ip+")";ctx.channel().attr(AttributeKey.valueOf(ChatroomConst.IP)).set(ip);ctx.channel().attr(AttributeKey.valueOf(ChatroomConst.USERNAME)).set(username);ctx.channel().attr(AttributeKey.valueOf(ChatroomConst.CHATROOM)).set(chatroomName);WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(req.uri(), null, false);handshaker = wsFactory.newHandshaker(req);if (handshaker == null) {WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());} else {handshaker.handshake(ctx.channel(), req).sync();ChannelGroup channelGroup=ChatRoom.map.get(chatroomName);if(channelGroup==null){channelGroup=new DefaultChannelGroup(ImmediateEventExecutor.INSTANCE);channelGroup.add(ctx.channel());ChatRoom.map.put(chatroomName,channelGroup);}else{TextWebSocketFrame textWebSocketFrame=new TextWebSocketFrame(username+" 加入了聊天室");for(Channel channel:channelGroup){channel.writeAndFlush(textWebSocketFrame);}channelGroup.add(ctx.channel());}}}}@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {System.out.println("通道建立");super.channelActive(ctx);}@Overridepublic void channelInactive(ChannelHandlerContext ctx) throws Exception {System.out.println("通道關閉");String chatroom=(String) ctx.channel().attr(AttributeKey.valueOf(ChatroomConst.CHATROOM)).get();if(chatroom!=null){ChannelGroup channelGroup=ChatRoom.map.get(chatroom);channelGroup.remove(ctx.channel());}super.channelInactive(ctx);} } -
ChatHandler 聊天信息的處理器
package com.example.netty_websocket_chatroom_demo.handler;import com.example.netty_websocket_chatroom_demo.constant.ChatroomConst; import com.example.netty_websocket_chatroom_demo.data.ChatRoom; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.group.ChannelGroup; import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; import io.netty.util.AttributeKey;import java.util.Date;public class ChatHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {@Overrideprotected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {String text=msg.text();String username=(String) ctx.channel().attr(AttributeKey.valueOf(ChatroomConst.USERNAME)).get();String chatroom=(String) ctx.channel().attr(AttributeKey.valueOf(ChatroomConst.CHATROOM)).get();String id=ctx.channel().id().asLongText();if(chatroom!=null){ChannelGroup channelGroup= ChatRoom.map.get(chatroom);if(channelGroup!=null){String time=new Date().toLocaleString();TextWebSocketFrame other=new TextWebSocketFrame(time+" "+username+": "+text);TextWebSocketFrame me=new TextWebSocketFrame(time+" "+"我: "+text);for(Channel channel:channelGroup){String channelId=channel.id().asLongText();if(id.equals(channelId)){channel.writeAndFlush(me);}else {channel.writeAndFlush(other);}}}}} } -
ChatRoom 用來存放聊天室和每個聊天室里的channel
package com.example.netty_websocket_chatroom_demo.data;import io.netty.channel.group.ChannelGroup;import java.util.HashMap; import java.util.Map;/*** 存放每個聊天室和每個聊天室里的數據*/ public class ChatRoom {public static Map<String, ChannelGroup> map=new HashMap<>(); } -
ChatroomConst 常量類
package com.example.netty_websocket_chatroom_demo.constant;/*** 常量*/ public interface ChatroomConst {String IP="IP";String USERNAME="USERNAME";String CHATROOM="CHATROOM"; } -
測試頁面
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>WebSocketTest</title><script>socket=undefined/*** 往屏幕里輸出信息*/function addMsg(msg) {let content=document.getElementById('content')content.innerHTML=content.innerHTML+msg+"<br>"}function send() {let msg=document.getElementById('input').valueif(msg){if (!window.WebSocket) {return}if (socket.readyState == WebSocket.OPEN) {socket.send(msg)document.getElementById('input').value=''} else {alert('連接沒有開啟.')}}else{alert('不允許空消息')}}/*** 加入聊天室*/function enter() {nickname=document.getElementById('nickname').valuechatroom=document.getElementById('chatroom').valueurl='ws://192.168.0.140:8081/ws?nickname='+nickname+'&chatroom='+chatroomif (!window.WebSocket) {window.WebSocket = window.MozWebSocket}if (window.WebSocket) {socket = new WebSocket(url)socket.onopen = function(event) {addMsg('聊天室'+chatroom+' 連接成功')console.log(event)}socket.onclose = function(event) {addMsg('連接被關閉')}socket.onmessage = function(event) {addMsg(event.data)}} else {alert('你的瀏覽器不支持 WebSocket!')}}/*** 退出聊天室*/async function exit() {socket.close()addMsg("退出聊天室")}</script> </head> <body> <div id="content" style="width: 400px;height: 300px;"></div> <div style="width: 400px;height: 200px"><input type="text" id="nickname" placeholder="請輸入要使用的昵稱"><input type="text" id="chatroom" placeholder="請輸入要加入的聊天室名"><button onclick="enter()">進入聊天室</button><textarea id="input" placeholder="請輸入短信內容" style="width: 100%;height: 80px"></textarea><button onclick="send()">發送</button><button onclick="exit()">退出聊天室</button> </div> </body> </html> -
application.yml 項目配置
server:port: 80 -
完整maven pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.4.0</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.example</groupId><artifactId>netty_websocket_chatroom_demo</artifactId><version>1.0.1-SNAPSHOT</version><name>netty_websocket_chatroom_demo</name><description>Demo project for Spring Boot</description><properties><java.version>1.8</java.version></properties><dependencies><!--netty的依賴集合,都整合在一個依賴里面了--><dependency><groupId>io.netty</groupId><artifactId>netty-all</artifactId><version>4.1.25.Final</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build> </project>
總結
以上是生活随笔為你收集整理的Netty Websocket多人多房间聊天室Demo的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Fuse.js模糊搜索引擎
- 下一篇: [渝粤教育] 浙江大学 概率论与数理统计