IM多客户端

1、场景描述:

业务系统中对于用户信息都有一个唯一标识userId,当我们在业务系统中实现单聊、群聊或者消息推送的时候,同一个用户会有多个客户端登录,例如我们分别用谷歌、360和火狐打开业务系统,此时同一用户登录,打开了三个socket连接,socket.io是使用sessionId来区分客户端的。此时如果要想谷歌、360和火狐浏览器都要接收到服务器推送的消息,就要把userId和sessionId进行绑定,也就是说同一个userId会对应多个sessionId。

2、代码实现

pom文件

    <dependency>
                <groupId>com.corundumstudio.socketio</groupId>
                <artifactId>netty-socketio</artifactId>
                <version>1.7.19</version>
     </dependency>

客户端连接成功、客户端失去连接、以及服务端收到ping消息的处理类如下

package com.pojo.prj.handle;

import com.corundumstudio.socketio.SocketIOClient;
import com.corundumstudio.socketio.SocketIOServer;
import com.corundumstudio.socketio.annotation.OnConnect;
import com.corundumstudio.socketio.annotation.OnDisconnect;
import com.corundumstudio.socketio.annotation.OnEvent;
import com.pojo.common.core.utils.LocalDateUtil;
import org.redisson.api.RBucket;
import org.redisson.api.RList;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;


@Component
public class NoticeEventHandler {

    private Logger logger = LoggerFactory.getLogger(NoticeEventHandler.class);

    @Autowired
    SocketIOServer socketIOServer;

    @Autowired
    RedissonClient redissonClient;

    @OnConnect
    @SuppressWarnings(value = {"unchecked"})
    public void onConnect(SocketIOClient client) {
        //用户连接
        logger.info("客户端加入");
        //用户连接
        //id为连用户id 
        String userId = client.getHandshakeData().getSingleUrlParam("userId");
        String clientType = client.getHandshakeData().getSingleUrlParam("clientType");
        //保存sessionId  key 用户id  value SocketIOClient的sessionId  clientType = APP PC
        String uuid = client.getSessionId().toString();
        //记录同一个userId 同一个客户端的 sessionId
        RList<String> uuidBucket = redissonClient.getList(ImConstant.USERID_SESSION_ID_KEY + clientType + ":" + userId);
        uuidBucket.add(uuid);
        //保存 sessionId 和用户id的映射 key为sessionId  value 为userId
        RBucket<String> userIdBucket = redissonClient.getBucket(ImConstant.SEESION_ID_USERID_KEY + uuid);
        userIdBucket.set(userId);

        RBucket<String> rBucket = redissonClient.getBucket("wssNotice:heartBeat:clentType:" + clientType + ":uuid:" + uuid);
        String time = LocalDateUtil.getCurrentTimeStr();
        rBucket.set(time, 90, TimeUnit.SECONDS);

    }


    @OnDisconnect
    @SuppressWarnings(value = {"unchecked"})
    public void onDisconnect(SocketIOClient client) {
        //用户失去连接
        logger.info("用户失去连接");
        //用户断开连接连接 清除缓存保存的用户信息
        //用户连接
        //id为连用户id
        String userId = client.getHandshakeData().getSingleUrlParam("userId");
        String clientType = client.getHandshakeData().getSingleUrlParam("clientType");
        String uuid = client.getSessionId().toString();

        RList<String> uuidBucket = redissonClient.getList(ImConstant.USERID_SESSION_ID_KEY + clientType + ":" + userId);
        uuidBucket.remove(uuid);
        
        RBucket<String> userIdBucket = redissonClient.getBucket(ImConstant.SEESION_ID_USERID_KEY + uuid);
        userIdBucket.delete();

        RBucket<String> rBucket = redissonClient.getBucket("wssNotice:heartBeat:clentType:" + clientType + ":uuid:" + uuid);
        rBucket.delete();

    }

    /**
     * 心跳 每30秒发送心跳一次 4次心跳未收到默认离线
     *
     * @param client
     */
    @OnEvent("jywsping")
    public void ping(SocketIOClient client) {
        String clientType = client.getHandshakeData().getSingleUrlParam("clientType");
        String uuid = client.getSessionId().toString();
        RBucket<String> rBucket = redissonClient.getBucket("wssNotice:heartBeat:clentType:" + clientType + ":uuid:" + uuid);
        String time = LocalDateUtil.getCurrentTimeStr();
        rBucket.set(time, 120, TimeUnit.SECONDS);
    }

}

4、当同一个用户userId,打开谷歌、360、和火狐浏览器后,用户id和sessionId的保存如下

客户端代码

<!DOCTYPE html>
<html>
<head>

        <meta charset="utf-8" />

        <title>Demo Chat</title>

        <link href="bootstrap.css" rel="stylesheet">

	<style>
		body {
			padding:20px;
		}
		#console {
			height: 400px;
			overflow: auto;
		}
		.username-msg {color:orange;}
		.connect-msg {color:green;}
		.disconnect-msg {color:red;}
		.send-msg {color:#888}
	</style>


	<script src="js/socket.io/socket.io2.js"></script>
        <script src="js/moment.min.js"></script>
        <script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>

	<script>

                var url = 'http://192.168.110.163:28889';
               
                

// clientType pc监管端传值 pc  app端传值 app      userId  用户id
		var socket =  io.connect(url,{ transports: ["websocket"],query: {userId: 61,clientType:"app"}});

		socket.on('connect', function() {
			output('<span class="connect-msg">Client has connected to the server!</span>');
		});


		socket.on('disconnect', function() {
			output('<span class="disconnect-msg">The client has disconnected!</span>');
		});
		
		
				socket.on('notice', function(data, ackServerCallback) {
				//事件发生 接收到通知消息  
			   //项目id     projectId  
               //标段id     sectionId
               //项目名称    projectName
               //标段名称    sectionName
               //通知类型   1 交通事件审核  2拥堵事件审核  3摘帽事件提醒  4 交通事件提醒  5拥堵事件提醒   noticeType
               // 通知内容   content
               // 事件 id   waringId
               // 通知id   id

			output('<span class="username-msg">' + data.id + ':</span> ' + data.projectName);
			//回调函数消息确认
                        if (ackServerCallback) {
                           ackServerCallback(data);
                        }
		});


                function sendDisconnect() {
                        socket.disconnect();
                }

		function ping() {
                     //ping 消息 每隔30秒发送一次
                     socket.emit('jywsping');
		}

		function output(message) {
                        var currentTime = "<span class='time'>" +  moment().format('HH:mm:ss.SSS') + "</span>";
                        var element = $("<div>" + currentTime + " " + message + "</div>");
			$('#console').prepend(element);
		}

        $(document).keydown(function(e){
            if(e.keyCode == 13) {
                $('#send').click();
            }
        });
	</script>
</head>

<body>

	<h1>Netty-socketio Demo Chat</h1>

	<br/>

	<div id="console" class="well">
	</div>

        <form class="well form-inline" onsubmit="return false;">
           <input id="msg" class="input-xlarge" type="text" placeholder="Type something..."/>
           <button type="button" onClick="ping()" class="btn" id="send">ping</button>
           <button type="button" onClick="sendDisconnect()" class="btn">Disconnect</button>
        </form>



</body>

</html>

用户id对应的sessionid

 

sessionId对应的用户id

 

5、当服务器主动发消息给相关用户(此处使用userId=61 打开了三个页面),可以看到三个客户端都收到了消息

360浏览器

 

火狐浏览器

谷歌浏览器

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

非ban必选

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值