中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

怎么用SpringBoot+Netty實現簡單聊天室

發布時間:2022-02-07 10:42:15 來源:億速云 閱讀:130 作者:iii 欄目:開發技術

本篇內容主要講解“怎么用SpringBoot+Netty實現簡單聊天室”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“怎么用SpringBoot+Netty實現簡單聊天室”吧!

    一、實現

    1.User類

    import java.util.Objects;
    
    public class User {
    
        public String id;
        public String nickname;
    
        public User(String id, String nickname) {
            super();
            this.id = id;
            this.nickname = nickname;
        }
    
        public String getId() {
            return id;
        }
    
        public void setId(String id) {
            this.id = id;
        }
    
        public String getNickname() {
            return nickname;
        }
    
        public void setNickname(String nickname) {
            this.nickname = nickname;
        }
    
        @Override
        public boolean equals(Object o) {
            if (this == o)
                return true;
            if (o == null || getClass() != o.getClass())
                return false;
            User user = (User) o;
            return id.equals(user.getId());
        }
    
        @Override
        public int hashCode() {
    
            return Objects.hash(id);
        }
    
        public String getUid() {
    
            return id;
        }
    }

    2.SocketSession類

    import io.netty.channel.Channel;
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.util.AttributeKey;
    
    import java.util.HashMap;
    import java.util.Map;
    import java.util.UUID;
    
    public class SocketSession {
    
        public static final AttributeKey<SocketSession> SESSION_KEY = AttributeKey.valueOf("SESSION_KEY");
    
        /**
         * 用戶實現服務端會話管理的核心
         */
    // 通道
        private Channel channel;
        // 用戶
        private User user;
    
        // session唯一標示
        private final String sessionId;
    
        private String group;
    
        /**
         * session中存儲的session 變量屬性值
         */
        private Map<String, Object> map = new HashMap<String, Object>();
    
        public SocketSession(Channel channel) {//注意傳入參數channel。不同客戶端會有不同channel
            this.channel = channel;
            this.sessionId = buildNewSessionId();
            channel.attr(SocketSession.SESSION_KEY).set(this);
        }
    
        // 反向導航
        public static SocketSession getSession(ChannelHandlerContext ctx) {//注意ctx,不同的客戶端會有不同ctx
            Channel channel = ctx.channel();
            return channel.attr(SocketSession.SESSION_KEY).get();
        }
    
        // 反向導航
        public static SocketSession getSession(Channel channel) {
            return channel.attr(SocketSession.SESSION_KEY).get();
        }
    
        public String getId() {
            return sessionId;
        }
    
        private static String buildNewSessionId() {
            String uuid = UUID.randomUUID().toString();
            return uuid.replaceAll("-", "");
        }
    
        public synchronized void set(String key, Object value) {
            map.put(key, value);
        }
    
        public synchronized <T> T get(String key) {
            return (T) map.get(key);
        }
    
        public boolean isValid() {
            return getUser() != null ? true : false;
        }
    
        public User getUser() {
            return user;
        }
    
        public void setUser(User user) {
            this.user = user;
        }
    
        public String getGroup() {
            return group;
        }
    
        public void setGroup(String group) {
            this.group = group;
        }
    
        public Channel getChannel() {
            return channel;
        }
    }

    3.SessionGroup

    import com.google.gson.Gson;
    import io.netty.channel.ChannelFuture;
    import io.netty.channel.ChannelFutureListener;
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.channel.group.ChannelGroup;
    import io.netty.channel.group.ChannelGroupFuture;
    import io.netty.channel.group.DefaultChannelGroup;
    import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
    import io.netty.util.concurrent.ImmediateEventExecutor;
    import org.springframework.util.StringUtils;
    
    import java.util.Iterator;
    import java.util.Map;
    import java.util.concurrent.ConcurrentHashMap;
    
    public final class SessionGroup {
    
        private static SessionGroup singleInstance = new SessionGroup();
    
        // 組的映射
        private ConcurrentHashMap<String, ChannelGroup> groupMap = new ConcurrentHashMap<>();
    
        public static SessionGroup inst() {
            return singleInstance;
        }
    
        public void shutdownGracefully() {
    
            Iterator<ChannelGroup> groupIterator = groupMap.values().iterator();
            while (groupIterator.hasNext()) {
                ChannelGroup group = groupIterator.next();
                group.close();
            }
        }
    
        public void sendToOthers(Map<String, String> result, SocketSession s) {
            // 獲取組
            ChannelGroup group = groupMap.get(s.getGroup());
            if (null == group) {
                return;
            }
            Gson gson=new Gson();
            String json = gson.toJson(result);
            // 自己發送的消息不返回給自己
    //      Channel channel = s.getChannel();
            // 從組中移除通道
    //      group.remove(channel);
            ChannelGroupFuture future = group.writeAndFlush(new TextWebSocketFrame(json));
            future.addListener(f -> {
                System.out.println("完成發送:"+json);
    //          group.add(channel);//發送消息完畢重新添加。
    
            });
        }
    
        public void addSession(SocketSession session) {
    
            String groupName = session.getGroup();
            if (StringUtils.isEmpty(groupName)) {
                // 組為空,直接返回
                return;
            }
            ChannelGroup group = groupMap.get(groupName);
            if (null == group) {
                group = new DefaultChannelGroup(ImmediateEventExecutor.INSTANCE);
                groupMap.put(groupName, group);
            }
            group.add(session.getChannel());
        }
    
        /**
         * 關閉連接, 關閉前發送一條通知消息
         */
        public void closeSession(SocketSession session, String echo) {
            ChannelFuture sendFuture = session.getChannel().writeAndFlush(new TextWebSocketFrame(echo));
            sendFuture.addListener(new ChannelFutureListener() {
                public void operationComplete(ChannelFuture future) {
                    System.out.println("關閉連接:"+echo);
                    future.channel().close();
                }
            });
        }
    
        /**
         * 關閉連接
         */
        public void closeSession(SocketSession session) {
    
            ChannelFuture sendFuture = session.getChannel().close();
            sendFuture.addListener(new ChannelFutureListener() {
                public void operationComplete(ChannelFuture future) {
                    System.out.println("發送所有完成:"+session.getUser().getNickname());
                }
            });
    
        }
    
        /**
         * 發送消息
         * @param ctx 上下文
         * @param msg 待發送的消息
         */
        public void sendMsg(ChannelHandlerContext ctx, String msg) {
            ChannelFuture sendFuture = ctx.writeAndFlush(new TextWebSocketFrame(msg));
            sendFuture.addListener(f -> {//發送監聽
                System.out.println("對所有發送完成:"+msg);
            });
        }
    }

    4.WebSocketTextHandler類

    import com.google.gson.Gson;
    import com.google.gson.reflect.TypeToken;
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.channel.SimpleChannelInboundHandler;
    import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
    import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
    import io.netty.handler.timeout.IdleState;
    import io.netty.handler.timeout.IdleStateEvent;
    
    import java.util.HashMap;
    import java.util.Map;
    
    public class WebSocketTextHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
    
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
            SocketSession session = SocketSession.getSession(ctx);
            TypeToken<HashMap<String, String>> typeToken = new TypeToken<HashMap<String, String>>() {
            };
    
            Gson gson=new Gson();
            java.util.Map<String,String> map = gson.fromJson(msg.text(), typeToken.getType());
            User user = null;
            switch (map.get("type")) {
                case "msg":
                    Map<String, String> result = new HashMap<>();
                    user = session.getUser();
                    result.put("type", "msg");
                    result.put("msg", map.get("msg"));
                    result.put("sendUser", user.getNickname());
                    SessionGroup.inst().sendToOthers(result, session);
                    break;
                case "init":
                    String room = map.get("room");
                    session.setGroup(room);
                    String nick = map.get("nick");
                    user = new User(session.getId(), nick);
                    session.setUser(user);
                    SessionGroup.inst().addSession(session);
                    break;
            }
        }
    
        @Override
        public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
    
            // 是否握手成功,升級為 Websocket 協議
            if (evt == WebSocketServerProtocolHandler.ServerHandshakeStateEvent.HANDSHAKE_COMPLETE) {
                // 握手成功,移除 HttpRequestHandler,因此將不會接收到任何消息
                // 并把握手成功的 Channel 加入到 ChannelGroup 中
                new SocketSession(ctx.channel());
            } else if (evt instanceof IdleStateEvent) {
                IdleStateEvent stateEvent = (IdleStateEvent) evt;
                if (stateEvent.state() == IdleState.READER_IDLE) {
                    System.out.println("bb22");
                }
            } else {
                super.userEventTriggered(ctx, evt);
            }
        }
    }

    5.WebSocketServer類

    import io.netty.bootstrap.ServerBootstrap;
    import io.netty.channel.Channel;
    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.codec.http.websocketx.extensions.compression.WebSocketServerCompressionHandler;
    import io.netty.handler.stream.ChunkedWriteHandler;
    import io.netty.handler.timeout.IdleStateHandler;
    
    import java.util.concurrent.TimeUnit;
    
    public class WebSocketServer {
    
        private static WebSocketServer wbss;
    
        private static final int READ_IDLE_TIME_OUT = 60; // 讀超時
        private static final int WRITE_IDLE_TIME_OUT = 0;// 寫超時
        private static final int ALL_IDLE_TIME_OUT = 0; // 所有超時
    
        public static WebSocketServer inst() {
            return wbss = new WebSocketServer();
        }
    
        public void run(int port) {
            EventLoopGroup bossGroup = new NioEventLoopGroup();
            EventLoopGroup workerGroup = new NioEventLoopGroup();
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer <SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            // Netty自己的http解碼器和編碼器,報文級別 HTTP請求的解碼和編碼
                            pipeline.addLast(new HttpServerCodec());
                            // ChunkedWriteHandler 是用于大數據的分區傳輸
                            // 主要用于處理大數據流,比如一個1G大小的文件如果你直接傳輸肯定會撐暴jvm內存的;
                            // 增加之后就不用考慮這個問題了
                            pipeline.addLast(new ChunkedWriteHandler());
                            // HttpObjectAggregator 是完全的解析Http消息體請求用的
                            // 把多個消息轉換為一個單一的完全FullHttpRequest或是FullHttpResponse,
                            // 原因是HTTP解碼器會在每個HTTP消息中生成多個消息對象HttpRequest/HttpResponse,HttpContent,LastHttpContent
                            pipeline.addLast(new HttpObjectAggregator(64 * 1024));
                            // WebSocket數據壓縮
                            pipeline.addLast(new WebSocketServerCompressionHandler());
                            // WebSocketServerProtocolHandler是配置websocket的監聽地址/協議包長度限制
                            pipeline.addLast(new WebSocketServerProtocolHandler("/ws", null, true, 10 * 1024));
    
                            // 當連接在60秒內沒有接收到消息時,就會觸發一個 IdleStateEvent 事件,
                            // 此事件被 HeartbeatHandler 的 userEventTriggered 方法處理到
                            pipeline.addLast(
                                    new IdleStateHandler(READ_IDLE_TIME_OUT, WRITE_IDLE_TIME_OUT, ALL_IDLE_TIME_OUT, TimeUnit.SECONDS));
    
                            // WebSocketServerHandler、TextWebSocketFrameHandler 是自定義邏輯處理器,
                            pipeline.addLast(new WebSocketTextHandler());
                        }
                    });
            Channel ch = b.bind(port).syncUninterruptibly().channel();
            ch.closeFuture().syncUninterruptibly();
    
            // 返回與當前Java應用程序關聯的運行時對象
            Runtime.getRuntime().addShutdownHook(new Thread() {
                @Override
                public void run() {
                    SessionGroup.inst().shutdownGracefully();
                    bossGroup.shutdownGracefully();
                    workerGroup.shutdownGracefully();
                }
            });
        }
    }

    6.index.html

    <!DOCTYPE HTML>
    <html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <title>群聊天室</title>
        <style type="text/css">
            body {
                margin-right:50px;
                margin-left:50px;
            }
            .ddois {
                position: fixed;
                left: 120px;
                bottom: 30px;
            }
        </style>
    </head>
    <body>
    群名:<input type="text" id="room" name="group" placeholder="請輸入群">
    <br /><br />
    昵稱:<input type="text" id="nick" name="name" placeholder="請輸入昵稱">
    <br /><br />
    <button type="button" onclick="enter()">進入聊天群</button>
    <br /><br />
    <div id="message"></div>
    <br /><br />
    <div class="ddois">
        <textarea name="send" id="text" rows="10" cols="30" placeholder="輸入發送消息"></textarea>
        <br /><br />
        <button type="button" onclick="send()">發送</button>
    </div>
    <script type="text/javascript">
        var webSocket;
    
        if (window.WebSocket) {
            webSocket = new WebSocket("ws://localhost:8088/ws");
        } else {
            alert("抱歉,您的瀏覽器不支持WebSocket協議!");
        }
    
        //連通之后的回調事件
        webSocket.onopen = function() {
            console.log("已經連通了websocket");
    //                setMessageInnerHTML("已經連通了websocket");
        };
        //連接發生錯誤的回調方法
        webSocket.onerror = function(event){
            console.log("出錯了");
    //              setMessageInnerHTML("連接失敗");
        };
    
        //連接關閉的回調方法
        webSocket.onclose = function(){
            console.log("連接已關閉...");
    
        }
    
        //接收到消息的回調方法
        webSocket.onmessage = function(event){
            console.log("bbdds");
            var data = JSON.parse(event.data)
            var msg = data.msg;
            var nick = data.sendUser;
            switch(data.type){
                case 'init':
                    console.log("mmll");
                    break;
                case 'msg':
                    console.log("bblld");
                    setMessageInnerHTML(nick+":  "+msg);
                    break;
                default:
                    break;
            }
        }
        function enter(){
            var map = new Map();
            var nick=document.getElementById('nick').value;
            var room=document.getElementById('room').value;
            map.set("type","init");
            map.set("nick",nick);
            console.log(room);
            map.set("room",room);
            var message = Map2Json(map);
            webSocket.send(message);
        }
    
        function send() {
            var msg = document.getElementById('text').value;
            var nick = document.getElementById('nick').value;
            console.log("1:"+msg);
            if (msg != null && msg != ""){
                var map = new Map();
                map.set("type","msg");
                map.set("msg",msg);
                var map2json=Map2Json(map);
                if (map2json.length < 8000){
                    console.log("4:"+map2json);
                    webSocket.send(map2json);
                }else {
                    console.log("文本太長了,少寫一點吧????");
                }
            }
        }
    
        //將消息顯示在網頁上
        function setMessageInnerHTML(innerHTML) {
            document.getElementById("message").innerHTML += innerHTML + "
    ";
        }
    
        function Map2Json(map) {
            var str = "{";
            map.forEach(function (value, key) {
                str += '"'+key+'"'+':'+ '"'+value+'",';
            })
            str = str.substring(0,str.length-1)
            str +="}";
            return str;
        }
    
    </script>
    
    </body>
    </html>

    二、效果

    怎么用SpringBoot+Netty實現簡單聊天室

    到此,相信大家對“怎么用SpringBoot+Netty實現簡單聊天室”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    AI

    大同市| 蓝山县| 灵川县| 永年县| 云霄县| 利川市| 静乐县| 深水埗区| 津南区| 定日县| 汕尾市| 丰镇市| 天津市| 邹城市| 大洼县| 海原县| 靖安县| 库尔勒市| 福鼎市| 湛江市| 卓尼县| 当涂县| 揭阳市| 武胜县| 广安市| 凭祥市| 龙门县| 兴业县| 普宁市| 泸州市| 衡阳市| 许昌县| 阳曲县| 乌鲁木齐县| 扎赉特旗| 沽源县| 眉山市| 虎林市| 贡山| 漳浦县| 澄江县|