欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Springboot+WebSocket+Netty實現(xiàn)在線聊天/群聊系統(tǒng)

 更新時間:2023年08月11日 09:19:10   作者:wml_JavaKill  
這篇文章主要實現(xiàn)在好友添加、建群、聊天對話、群聊功能,使用Java作為后端語言進(jìn)行支持,界面友好,開發(fā)簡單,文章中有詳細(xì)的代碼示例供大家參考,需要的朋友可以參考下

一、文章前言

此文主要實現(xiàn)在好友添加、建群、聊天對話、群聊功能,使用Java作為后端語言進(jìn)行支持,界面友好,開發(fā)簡單。

二、開發(fā)流程及工具準(zhǔn)備

2.1、下載安裝IntelliJ IDEA(后端語言開發(fā)工具),Mysql數(shù)據(jù)庫,微信Web開發(fā)者工具。

三、開發(fā)步驟

1.創(chuàng)建maven project

先創(chuàng)建一個名為SpringBootDemo的項目,選擇【New Project】

在這里插入圖片描述

然后在彈出的下圖窗口中,選擇左側(cè)菜單的【New Project】(注:和2022之前的idea版本不同,這里左側(cè)沒有【Maven】選項,不要選【Maven Archetype】!?。。?,輸入Name(項目名):SpringBootDemo,language選擇【java】,build system選擇【maven】,然后選擇jdk,我這里選的是jdk18.

在這里插入圖片描述

然后點擊【Create】

在這里插入圖片描述

2.在project下創(chuàng)建module

點擊右鍵選擇【new】—【Module…】

在這里插入圖片描述

左側(cè)選擇【Spring initializr】,通過idea中集成的Spring initializr工具進(jìn)行spring boot項目的快速創(chuàng)建。窗口右側(cè):name可根據(jù)自己喜好設(shè)置,group和artifact和上面一樣的規(guī)則,其他選項保持默認(rèn)值即可,【next】

在這里插入圖片描述

Developer Tools模塊勾選【Spring Boot DevTools】,web模塊勾選【Spring Web】

在這里插入圖片描述

此時,一個Springboot項目已經(jīng)搭建完成,可開發(fā)后續(xù)功能

3.編寫一個消息實體類、Mapper、service(三層架構(gòu))

@Data
public class Chat {
    @TableId(type = IdType.AUTO)
    private Long id;
    private Long userId;
    private Long targetUserId;
    private LocalDateTime createTime;
    private String userName;
    private String targetUserName;
    private String content;
}

由于我們使用mybatis-plus,所以簡單的增刪改查不用自己寫,框架自帶了,只需要實現(xiàn)或者繼承他的Mapper、Service

在這里插入圖片描述

在這里插入圖片描述

4.編寫WebSocket服務(wù)類

@ServerEndpoint("/imserver/{userId}")
@Component
public class WebSocketService {
    /**
     * concurrent包的線程安全Set,用來存放每個客戶端對應(yīng)的MyWebSocket對象。
     */
    private static ConcurrentHashMap<String, WebSocketService> webSocketMap = new ConcurrentHashMap<>();
    /**
     * 與某個客戶端的連接會話,需要通過它來給客戶端發(fā)送數(shù)據(jù)
     */
    private Session session;
    /**
     * 接收userId
     */
    private String userId = "";
    public static ChatMapper chatMapper = null;
    /**
     * 連接建立成功調(diào)用的方法
     * <p>
     * 1.用map存 每個客戶端對應(yīng)的MyWebSocket對象
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("userId") String userId) {
        this.session = session;
        this.userId = userId;
        if (webSocketMap.containsKey(userId)) {
            webSocketMap.remove(userId);
            webSocketMap.put(userId, this);
            //加入set中
        } else {
            webSocketMap.put(userId, this);
            //加入set中
        }
    }
    /**
     * 報錯
     *
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        error.printStackTrace();
    }
    /**
     * 實現(xiàn)服務(wù)器推送到對應(yīng)的客戶端
     */
    public void sendMessage(String message) {
        try {
            this.session.getBasicRemote().sendText(message);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /**
     * 自定義 指定的userId服務(wù)端向客戶端發(fā)送消息
     */
    public static void sendInfo(Chat chat) {
        QueryWrapper<Chat> queryWrapper = new QueryWrapper();
        List<Chat> chats=chatMapper.selectList(queryWrapper.lambda()
                .eq(Chat::getTargetUserId, chat.getTargetUserId()).or().eq(Chat::getUserId, chat.getTargetUserId()).or().eq(Chat::getTargetUserId, chat.getUserId()).or().eq(Chat::getUserId, chat.getUserId()));
        //log.info("發(fā)送消息到:"+userId+",報文:"+message);
        if (!StringUtils.isEmpty(chat.getTargetUserId().toString()) && webSocketMap.containsKey(chat.getTargetUserId().toString())) {
            webSocketMap.get(chat.getUserId().toString()).sendMessage(JSONObject.toJSONString(chats));
            webSocketMap.get(chat.getTargetUserId().toString()).sendMessage(JSONObject.toJSONString(chats));
        } else {
            webSocketMap.get(chat.getUserId().toString()).sendMessage(JSONObject.toJSONString(chats));
        }
    }
    /**
     * 自定義關(guān)閉
     *
     * @param userId
     */
    public static void close(String userId) {
        if (webSocketMap.containsKey(userId)) {
            webSocketMap.remove(userId);
        }
    }
    /**
     * 獲取在線用戶信息
     *
     * @return
     */
    public static Map getOnlineUser() {
        return webSocketMap;
    }

5.創(chuàng)建控制器Controller

先創(chuàng)建Controller Package

在這里插入圖片描述

創(chuàng)建一個Controller

在這里插入圖片描述

輸入類名,選在【Class】

在這里插入圖片描述

因為要編寫Rest風(fēng)格的Api,要在Controller上標(biāo)注@RestController注解

6.創(chuàng)建具體的Api接口

@RestController
public class DemoController {
    @Autowired
    private ChatService chatService;
    @PostMapping("/push")
    public ResponseEntity<String> pushToWeb(@RequestBody Chat chat) throws IOException {
        chat.setCreateTime(LocalDateTime.now());
        chatService.save(chat);
        WebSocketService.sendInfo(chat);
        return ResponseEntity.ok("MSG SEND SUCCESS");
    }
    @GetMapping("/close")
    public String close(String userId) {
        WebSocketService.close(userId);
        return "ok";
    }
    @GetMapping("/getOnlineUser")
    public Map getOnlineUser() {
        return WebSocketService.getOnlineUser();
    }
    @GetMapping("/getMessage")
    public ResponseEntity<List<Chat>> getMessage(String userId) {
        QueryWrapper<Chat> queryWrapper = new QueryWrapper();
        List<Chat> list = chatService.
                list(queryWrapper.lambda().eq(Chat::getTargetUserId, userId).or().eq(Chat::getUserId, userId));
        return ResponseEntity.ok(list);
    }
}

7.編寫netty配置

package com.example.demo.config;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
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;
import org.springframework.stereotype.Component;
public class NettyServer {
    public void start(){
        //創(chuàng)建兩個線程組boosGroup和workerGroup,含有的子線程NioEventLoop的個數(shù)默認(rèn)為cpu核數(shù)的兩倍
        //boosGroup只是處理鏈接請求,真正的和客戶端業(yè)務(wù)處理,會交給workerGroup完成
        EventLoopGroup boosGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            //創(chuàng)建服務(wù)器的啟動對象
            ServerBootstrap bootstrap = new ServerBootstrap();
            //使用鏈?zhǔn)骄幊虂砼渲脜?shù)
            //設(shè)置兩個線程組
            bootstrap.group(boosGroup,workerGroup)
                    //使用NioSctpServerChannel作為服務(wù)器的通道實現(xiàn)
                    .channel(NioServerSocketChannel.class)
                    //初始化服務(wù)器鏈接隊列大小,服務(wù)端處理客戶端鏈接請求是順序處理的,所以同一時間只能處理一個客戶端鏈接
                    //多個客戶端同時來的時候,服務(wù)端將不能處理的客戶端鏈接請求放在隊列中等待處理
                    .option(ChannelOption.SO_BACKLOG,1024)
                    //創(chuàng)建通道初始化對象,設(shè)置初始化參數(shù)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            System.out.println("收到到新的鏈接");
                            //websocket協(xié)議本身是基于http協(xié)議的,所以這邊也要使用http解編碼器
                            ch.pipeline().addLast(new HttpServerCodec());
                            //以塊的方式來寫的處理器
                            ch.pipeline().addLast(new ChunkedWriteHandler());
                            ch.pipeline().addLast(new HttpObjectAggregator(8192));
                            ch.pipeline().addLast(new MessageHandler());//添加測試的聊天消息處理類
                            ch.pipeline().addLast(new WebSocketServerProtocolHandler("/ws", null, true, 65536 * 10));
                        }
                    });
            System.out.println("netty server start..");
            //綁定一個端口并且同步,生成一個ChannelFuture異步對象,通過isDone()等方法判斷異步事件的執(zhí)行情況
            //啟動服務(wù)器(并綁定端口),bind是異步操作,sync方法是等待異步操作執(zhí)行完畢
            ChannelFuture cf = bootstrap.bind(1245).sync();
            //給cf注冊監(jiān)聽器,監(jiān)聽我們關(guān)心的事件
            cf.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture channelFuture) throws Exception {
                    if (cf.isSuccess()){
                        System.out.println("監(jiān)聽端口成功");
                    }else {
                        System.out.println("監(jiān)聽端口失敗");
                    }
                }
            });
            //對通道關(guān)閉進(jìn)行監(jiān)聽,closeFuture是異步操作,監(jiān)聽通道關(guān)閉
            //通過sync方法同步等待通道關(guān)閉處理完畢,這里會阻塞等待通道關(guān)閉
            cf.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            boosGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

8.前端代碼Websocket聊天功能

if (!window.WebSocket) {
							window.WebSocket = window.MozWebSocket;
						}
						if (window.WebSocket) {
							me.websocket = new WebSocket(me.ws + me.info.id);
							me.websocket.onmessage = function(event) {
								var json = JSON.parse(event.data);
								me.msgListMethod();
								console.log(json);
							};
							console.log(me.websocket)
							me.websocket.onopen = function(event) {
								console.log("Netty-WebSocket服務(wù)器。。。。。。連接");
							};
							me.websocket.onerror = function(evt) {
								console.log('發(fā)生錯誤..., evt');
							};
							me.websocket.CONNECTING = function(evt) {
								console.log('正在鏈接中');
							};
						} else {
							alert("您的瀏覽器不支持WebSocket協(xié)議!");
						}
						//監(jiān)聽窗口關(guān)閉事件,當(dāng)窗口關(guān)閉時,主動去關(guān)閉websocket連接,防止連接還沒斷開就關(guān)閉窗口,server端會拋異常。
						window.onbeforeunload = function() {
							if (me.websocket != null) {
								me.websocket.close();
							}
						};

這里用到了很多消息發(fā)送功能,比如文件、圖片。群聊還可查看群成員功能

以上就是Springboot+WebSocket+Netty實現(xiàn)在線聊天/群聊系統(tǒng)的詳細(xì)內(nèi)容,更多關(guān)于Springboot實現(xiàn)在線聊天的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • SpringBoot集成Shiro進(jìn)行權(quán)限控制和管理的示例

    SpringBoot集成Shiro進(jìn)行權(quán)限控制和管理的示例

    這篇文章主要介紹了SpringBoot集成Shiro進(jìn)行權(quán)限控制和管理的示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-03-03
  • SpringCloud服務(wù)的發(fā)現(xiàn)與調(diào)用詳解

    SpringCloud服務(wù)的發(fā)現(xiàn)與調(diào)用詳解

    在Java微服務(wù)越來越火的今天。幾乎什么公司都在搞微服務(wù)。而使用的比較多的就是Spring?Cloud技術(shù)棧。今天就來研究一下Spring?Cloud中服務(wù)發(fā)現(xiàn)與調(diào)用的基本原理
    2022-07-07
  • Java包含抽象方法的枚舉類示例

    Java包含抽象方法的枚舉類示例

    這篇文章主要介紹了Java包含抽象方法的枚舉類,結(jié)合實例形式分析了Java包含抽象方法的枚舉類實現(xiàn)方法與相關(guān)操作技巧,需要的朋友可以參考下
    2019-08-08
  • springboot依賴沖突問題及解決過程

    springboot依賴沖突問題及解決過程

    新搭了一個springboot 2.3.7.RELASE的框架,在集成mysql,tkMapper,mybatis的過程中,啟動報錯,怎么解決這個問題呢,下面小編給大家?guī)砹藄pringboot依賴沖突問題及解決過程,一起看看吧
    2021-09-09
  • java中的內(nèi)部類詳細(xì)總結(jié)

    java中的內(nèi)部類詳細(xì)總結(jié)

    內(nèi)部類不是很好理解,但說白了其實也就是一個類中還包含著另外一個類。如同一個人是由大腦、肢體、器官等身體結(jié)果組成,而內(nèi)部類相當(dāng)于其中的某個器官之一,例如心臟:它也有自己的屬性和行為(血液、跳動)
    2013-10-10
  • Mybatis?sql與xml文件讀取方法詳細(xì)分析

    Mybatis?sql與xml文件讀取方法詳細(xì)分析

    這篇文章主要介紹了Mybatis?sql與xml文件讀取方法,在執(zhí)行一個自定義sql語句時,dao對應(yīng)的代理對象時如何找到sql,也就是dao的代理對象和sql之間的關(guān)聯(lián)關(guān)系是如何建立的
    2023-01-01
  • SpringAop中的Advice通知實例

    SpringAop中的Advice通知實例

    這篇文章主要介紹了SpringAop中的Advice通知詳解,Spring的AOP功能中一個關(guān)鍵概念是通知Advice與切點Pointcut表達(dá)式相關(guān)聯(lián)在特定節(jié)點織入一些邏輯,Spring提供了五種類型的通知,需要的朋友可以參考下
    2023-09-09
  • Java垃圾回收之分代收集算法詳解

    Java垃圾回收之分代收集算法詳解

    今天小編就為大家分享一篇關(guān)于Java垃圾回收之分代收集算法詳解,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2018-10-10
  • Spring AbstractRoutingDatasource 動態(tài)數(shù)據(jù)源的實例講解

    Spring AbstractRoutingDatasource 動態(tài)數(shù)據(jù)源的實例講解

    本文介紹如何使用 Spring AbstractRoutingDatasource 基于上下文動態(tài)切換數(shù)據(jù)源,因此我們會讓查找數(shù)據(jù)源邏輯獨立于數(shù)據(jù)訪問之外
    2021-07-07
  • Java實戰(zhàn)在線選課系統(tǒng)的實現(xiàn)流程

    Java實戰(zhàn)在線選課系統(tǒng)的實現(xiàn)流程

    讀萬卷書不如行萬里路,只學(xué)書上的理論是遠(yuǎn)遠(yuǎn)不夠的,只有在實戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用java+SSM+jsp+mysql+maven實現(xiàn)一個在線選課系統(tǒng),大家可以在過程中查缺補漏,提升水平
    2021-11-11

最新評論