1. 程式人生 > >spring整合socketio一對一實時聊天

spring整合socketio一對一實時聊天

第一步:pom.xml新增socketio依賴

<dependency>
   <groupId>com.corundumstudio.socketio</groupId>
   <artifactId>netty-socketio</artifactId>
   <version>1.7.11</version>
</dependency>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

值得提醒的是,當專案啟動netty類錯誤時,有可能是引入的版本問題,最好是最新版本

第二步:新增host和埠配置檔案(application.properties)

socketio.host=10.101.67.26
socketio.port=5380

值得說明的是,host也可以是localhost或者127.0.0.1,但是當在伺服器部署時,host不能是公網ip,必須是該部署的伺服器內網ip,否則socketio服務會繫結失敗

第三步:專案啟動類新增socketio服務啟動

package com.web;

import com.corundumstudio.socketio.SocketIOServer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application implements CommandLineRunner {
   @Autowired
   private SocketIOServer socketIOServer;

   public static void main(String[] args) {
      System.out.println("進入了Application方法");
      SpringApplication.run(Application.class, args);
   }

   @Override
   public void run(String... args) throws Exception {
      socketIOServer.start();//啟動
   }
}

第四步:新建連線、埠、傳送訊息處理類

package com.web.service.socket;

import com.corundumstudio.socketio.SocketIOClient;
import com.corundumstudio.socketio.annotation.OnConnect;
import com.corundumstudio.socketio.annotation.OnDisconnect;
import com.web.entity.cus.CusMessageDetail;
import com.web.service.cus.CusMessageDetailService;
import com.web.utils.ApplicationContextProvider;
import com.web.utils.DateUtils;
import com.web.utils.MyUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

@Component
public class LoginHandler {
    private static Map<String, SocketIOClient> clientMap = new ConcurrentHashMap<>();

    //客戶端連上socket伺服器時執行此事件
    @OnConnect
    public void onConnect(SocketIOClient client) {
        String messagerelateid = client.getHandshakeData().getSingleUrlParam("messagerelateid");
        String fromuserid = client.getHandshakeData().getSingleUrlParam("fromuserid");
        String touserid = client.getHandshakeData().getSingleUrlParam("touserid");
        if (messagerelateid != null) {
            clientMap.put(messagerelateid+"&"+fromuserid, client);//這裡使用messagerelateid和fromuserid進行拼湊
        }
        //TODO 根據messagerelateid、fromuserid、touserid查出聊天記錄(可能需要弄成介面形式)
    }

    //客戶端斷開socket伺服器時執行此事件
    @OnDisconnect
    public void onDisconnect(SocketIOClient client) {
        String messagerelateid = client.getHandshakeData().getSingleUrlParam("messagerelateid");
        String fromuserid = client.getHandshakeData().getSingleUrlParam("fromuserid");
        String touserid = client.getHandshakeData().getSingleUrlParam("touserid");
        if (messagerelateid != null) {
            clientMap.remove(messagerelateid+"&"+fromuserid, client);
            client.disconnect();
        }
    }

    //伺服器向客戶端傳送事件
    public CusMessageDetail pushMessage(String messagerelateid,String fromuserid,String touserid,String content) {
        CusMessageDetailService cusMessageDetailService = (CusMessageDetailService) ApplicationContextProvider.getBean("cusMessageDetailService");
        if (!StringUtils.isEmpty(messagerelateid)) {
            SocketIOClient client = clientMap.get(messagerelateid+"&"+touserid);//因為是發給對方,所以這裡使用messagerelateid和touserid進行拼湊,拿到對方的socket
            CusMessageDetail cusMessageDetail = new CusMessageDetail();
            cusMessageDetail.setContent(content);
            cusMessageDetail.setCreateby(fromuserid);
            cusMessageDetail.setCreatetime(DateUtils.strDate2());
            cusMessageDetail.setFromuserid(fromuserid);
            cusMessageDetail.setId(MyUtils.subUUID(UUID.randomUUID().toString()));
            cusMessageDetail.setIsdelete(0);
            cusMessageDetail.setMessagerelateid(messagerelateid);
            cusMessageDetail.setTouserid(touserid);
            cusMessageDetail.setUpdateby(fromuserid);
            cusMessageDetail.setUpdatetime(DateUtils.strDate2());
            cusMessageDetailService.insert(cusMessageDetail);//插入
            if (client != null) {
                //如果線上,則直接傳送
                client.sendEvent("pushMsg", cusMessageDetail.getContent());//傳送訊息
                return cusMessageDetail;
            }else{
                //對方離線,那麼也進行返回
                return cusMessageDetail;
            }
        }
        return null;
    }
}

值得說明的是,伺服器向客戶端傳送事件方法有我本人自己的一些邏輯處理,大家可以自行處理即可。

第五步:建立前端頁面進行測試

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title>NETTY SOCKET.IO DEMO</title>
    <base>
    <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
    <script src="https://cdn.bootcss.com/socket.io/2.1.1/socket.io.js"></script>
    <style>
        body {
            padding: 20px;
        }
        #console {
            height: 450px;
            overflow: auto;
        }
        .username-msg {
            color: orange;
        }
        .connect-msg {
            color: green;
        }
        .disconnect-msg {
            color: red;
        }
    </style>
</head>
<body>
<div id="console" class="well"></div>
</body>
<script type="text/javascript">
    var socket;
    connect();
    function connect() {
        var opts = {
            query: 'messagerelateid=a387b209c37948fbb47f58a9dcd53881&fromuserid=d138b75e81e74c4082295175ad605744&touserid=a138b75e81e74c4082295175ad605744'
        };
        socket = io.connect('http://10.101.67.26:5380', opts);
        socket.on('connect', function () {
            console.log("連線成功");
            serverOutput('<span class="connect-msg">連線成功</span>');
        });
        socket.on('pushMsg', function (data) {
            console.info(data);
            output('<span class="username-msg">' + data + ' </span>');
        });
        socket.on('disconnect', function () {
            serverOutput('<span class="disconnect-msg">' + '已下線! </span>');
        });
    }
    function output(message) {
        var element = $("<div>" + " " + message + "</div>");
        $('#console').prepend(element);
    }
    function serverOutput(message) {
        var element = $("<div>" + message + "</div>");
        $('#console').prepend(element);
    }
</script>
</html>

說明:socket = io.connect('http://10.101.67.26:5380', opts); 這裡的ip即為伺服器配置檔案的host,但是如果伺服器繫結的是內網的ip,那麼頁面這裡必須是公網的ip方可連線上

一對一發送訊息時,傳送的是對方的client中,自己這邊是收不到的,所以傳送成功後需要把傳送的訊息內容追加的訊息列表後面即可。

 

擴充套件:socketio如何實現群聊?

提示:可以建立一個群組,群組繫結多個人該群的一位client值,當群發時,迴圈發給該群所掛載的所有使用者client,如果有使用者線上,則是立即可以收到訊息,不線上,下次連線時通過獲取歷史訊息