layim框架+websocket即时通讯功能

文章目录
一、layim即时通讯的认识
1.1、layim基础配置
1.1.1、初始化(init)
1.1.2、群员接口(members)
1.1.3、layim基础配置代码
二、WebSocket搭建
2.1、创建消息处理类
2.2、webSocket配置类
2.3、前台jsp页面建立websocket连接
四、附上完整jsp代码
1.4、效果图
那么上篇讲到spring集成websocket的简单使用,这篇则将使用layim框架实现websocket即时通讯功能
一、layim即时通讯的认识

官方文档 https://www.layui.com/doc/modules/layim.html
由于layim是官方收费的,这里不便附上layim源码

准备阶段
集成ssm框架,spring集成websocket
引入layim
1.1、layim基础配置
1.1.1、初始化(init)

需要从后台去找好友分组数据以及群的信息
需要返回的json数据格式为:

{
  "code": 0
  ,"msg": ""
  ,"data": {
    "mine": {
      "username": "纸飞机"
      ,"id": "100000"
      ,"status": "online"
      ,"sign": "在深邃的编码世界,做一枚轻盈的纸飞机"
      ,"avatar": "http://cdn.firstlinkapp.com/upload/2016_6/1465575923433_33812.jpg"
    }
    ,"friend": [{
      "groupname": "我心中的女神"
      ,"id": 3
      ,"online": 1
      ,"list": [{
        "username": "林心如"
        ,"id": "76543"
        ,"avatar": "http://tp3.sinaimg.cn/1223762662/180/5741707953/0"
        ,"sign": "我爱贤心"
      }]
  }
}

创建ChatInfo、FriendInfo、GroupInfo类封装需要的json数据

public class ChatInfo {
    /**
     * 响应码
     */
    private Integer code;

    /**
     * 响应消息
     */
    private String msg;
    /**
     * 数据集合:接收mine(我的信息),friend(好友的信息)
     */
    private Map data = new HashMap();
    ....
    getter/setter
}

/**
 *      好友列表信息
 */
public class FriendInfo {
	//群名
    private String groupname;
    //群id
    private Integer id;
    //list集合接收多个群
    private List<EmpInfo> list;
	...
	getter/setter
}

/**
 *  /群组列表信息
 */
public class GroupInfo {
    //群id(部门id)
    private Integer id;
    //群名
    private String groupname;
    //群头像
    private String avatar;
    ...
    getter/setter
 }

controller层返回json数据

@RequestMapping("/findChatInfo")
    @ResponseBody
    public ChatInfo findChatInfo(Integer currentUserId) {
        List<FriendInfo> friendInfos = friendInfoService.findFriendInfos();
        EmpInfo mine = empInfoService.findOne(currentUserId);
        ChatInfo chatInfo = new ChatInfo();
        chatInfo.setCode(0);
        chatInfo.setMsg("ok");
        Map map = chatInfo.getData();
        map.put("mine", mine);
        map.put("friend", friendInfos);
        List<GroupInfo> list = Arrays.asList(groupInfoService.findGroupInfo(currentUserId));
        map.put("group", list);
        return chatInfo;
    }

显示结果为
在这里插入图片描述
1.1.2、群员接口(members)

需要从后台根据群id来查找群成员
返回的json数据格式如下

{
  "code": 0
  ,"msg": ""
  ,"data": {
    "owner": {
      "username": "贤心"
      ,"id": "100001"
      ,"avatar": "http://tp1.sinaimg.cn/1571889140/180/40030060651/1"
      ,"sign": "这些都是测试数据,实际使用请严格按照该格式返回"
    }
    ,"members": 12
    ,"list": [{
      "username": "贤心"
      ,"id": "100001"
      ,"avatar": "http://tp1.sinaimg.cn/1571889140/180/40030060651/1"
      ,"sign": "这些都是测试数据,实际使用请严格按照该格式返回"
    }]
  }
}

创建EmpInfo类封装群成员信息封装需要的json数据

/**
 *    成员信息
 */
public class EmpInfo{
    //消息的来源id
    private Integer id;
    //消息接收方的id
    private Integer toId;
    private String username;

    private String status;
    //签名
    private String sign;
    //头像
    private String avatar;
    //登陆状态
    private Integer isLogin;

    //消息内容
    private String content;

    //消息的来源类型
    private String type;

    //是否是本人
    private Boolean mine = false;
    //发送消息的时间
    private Date timestamp = new Date();

    //历史消息时间
    private Date historyTime = new Date();
    ...
    getter/setter
}

controller层返回json数据

 @RequestMapping("/getMembers")
    @ResponseBody
    public String getMembers(Integer id) {
        PageTable<Map<String, Object>> pageTable = new PageTable<Map<String, Object>>();
        //查询当前用户所在群的所有人员
        Map groupEmps = groupInfoService.findGroupEmps(id);
        //设置code
        pageTable.setCode(WebConstant.PAGESUCCESSCODE);
        //设置msg
        pageTable.setMsg("获取成功");
        //设置data
        pageTable.setData(groupEmps);
        //转成字符串形式返回给前台
        return responseAPI.getJsonString(pageTable);
    }

findGroupEmps方法代码

   @Override
    public Map findGroupEmps(Integer groupId) {
        Map map = new HashMap();
        map.put("owner", "");
        //查询当前用户所在群成员的人数--value类型 Integer
        map.put("members", groupInfoDao.findGroupEmpsNum(groupId));
        //查询当前用户所在群的所有人员--value类型 List<EmpInfo>
        map.put("list", groupInfoDao.findGroupEmps(groupId));
        return map;
    }

效果图如下
在这里插入图片描述
1.1.3、layim基础配置代码

 layim.config({

                //初始化接口
                init: {
                    url: '/chatController/findChatInfo?currentUserId=' +${currentUserId} //从后台去找好友分组数据
                    , data: {}
                }
                , min: true



                //查看群员接口
                , members: {
                    url: '/chatController/getMembers'
                    , data: {}
                }

                //上传图片接口
                , uploadImage: {
                    url: '/upload/image' //(返回的数据格式见下文)
                    , type: '' //默认post
                }

                //上传文件接口
                , uploadFile: {
                    url: '/upload/file' //(返回的数据格式见下文)
                    , type: '' //默认post
                }

                , isAudio: true //开启聊天工具栏音频
                , isVideo: true //开启聊天工具栏视频

                //扩展工具栏
                , tool: [{
                    alias: 'code'
                    , title: '代码'
                    , icon: '&#xe64e;'
                }]

                //,brief: true //是否简约模式(若开启则不显示主面板)

                , title: xxx //自定义主面板最小化时的标题
                //,right: '100px' //主面板相对浏览器右侧距离
                //,minRight: '90px' //聊天面板最小化时相对浏览器右侧距离
                , initSkin: '5.jpg' //1-5 设置初始背景
                //,skin: ['aaa.jpg'] //新增皮肤
                // ,isfriend: false //是否开启好友
                //,isgroup: false //是否开启群组
                //,min: true //是否始终最小化主面板,默认false
                , notice: true //是否开启桌面消息提醒,默认false
                //,voice: false //声音提醒,默认开启,声音文件为:default.mp3

                , msgbox: layui.cache.dir + 'css/modules/layim/html/msgbox.html' //消息盒子页面地址,若不开启,剔除该项即可
                , find: layui.cache.dir + 'css/modules/layim/html/find.html' //发现页面地址,若不开启,剔除该项即可
                , chatLog: "/msg/showHistoryMsg" //聊天记录页面地址,若不开启,剔除该项即可

            });

二、WebSocket搭建
与前篇基本相同

2.1、创建消息处理类

//消息处理类
@Component
public class MyHandler extends TextWebSocketHandler {
    // 在线用户列表
    public static final Map<Integer, WebSocketSession> userSocketSessionMap = new HashMap<Integer, WebSocketSession>();

    // 用户连接成功 就被调用
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        //获取传来的登录用户的id
        String idstr = session.getUri().toString().split("=")[1];
        int id = Integer.parseInt(idstr);
        System.err.println("用户连接成功");
        //保存对应的WebSocketSession
        userSocketSessionMap.put(id, session);
    }

    // 消息处理方法
    @Override
    public void handleTextMessage(WebSocketSession session, TextMessage message)  {
        try {
            ObjectMapper objectMapper = new ObjectMapper();
            EmpInfo friendInfo = objectMapper.readValue(message.getPayload(), EmpInfo.class);
            //session.sendMessage(message);//发送给当前人
            if(friendInfo.getType().equals("friend")) {
                sendMessageToUser(message,friendInfo );
            }else if(friendInfo.getType().equals("group")) {
                sendMessagesToUsers(message,friendInfo);//给所有的用户发送消息
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 给所有的用户发送消息
     */
    public void sendMessagesToUsers(TextMessage message,EmpInfo friendInfo) {

        Set<Integer> clientIds  = userSocketSessionMap.keySet();
        WebSocketSession session = null;
        for (Integer clientId : clientIds) {
            session = userSocketSessionMap.get(clientId);
            System.out.println(clientId);
            try {
                if(session.isOpen()&&!friendInfo.getId().equals(clientId)){
                    session.sendMessage(message);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    /**
     * 给所有的用户发送消息
     */
    public void sendMessageToUser(TextMessage message,EmpInfo friendInfo) throws IOException {

        Set<Integer> clientIds  = userSocketSessionMap.keySet();
        WebSocketSession session = null;
        for (Integer clientId : clientIds) {
            session = userSocketSessionMap.get(clientId);
            System.out.println(session);
            try {
                if(session.isOpen()&&friendInfo.getToId().equals(clientId)){

                    System.out.println("回复消息");
                    session.sendMessage(message);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }



    //用户退出后的处理,退出之后,要将用户信息从websocket的session中remove掉,这样用户就处于离线状态了,也不会占用系统资源
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus)  {
        try {
            if(session.isOpen()){
                session.close();
            }
            userSocketSessionMap.remove(session.getId());
            System.out.println("退出系统");
        }catch (Exception e){
            System.out.println("用户非正常关闭");
        }


    }

}

2.2、webSocket配置类

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(myHandler(), "/myHandler");
    }

    @Bean
    public WebSocketHandler myHandler() {
        return new MyHandler();
    }
}

2.3、前台jsp页面建立websocket连接

    var socket = null;
        //连接websocket的ip地址
        var ip = "输入你的ip地址";//比如localhost
        //动态修改查
        var im = {
            init: function () {
                if ('WebSocket' in window) {
                    //var socketUrl = 'ws://localhost:80/myHandler?myid=' +${currentUserId};
                    var socketUrl = 'ws://'+ip+'/myHandler?myid='+${currentUserId};
                    socket = new WebSocket(socketUrl);
                    im.startListener();
                } else {
                    alert('当前浏览器不支持WebSocket功能,请更换浏览器访问。');
                }
            },
            startListener: function () {
                if (socket) {
                    // 连接发生错误的回调方法
                    socket.onerror = function () {
                        console.log("通讯连接失败!");
                    };
                    // 连接成功建立的回调方法
                    socket.onopen = function (event) {
                        console.log("通讯连接成功");
                    }
                    // 接收到消息的回调方法
                    socket.onmessage = function (event) {
                        console.log("通讯接收到消息");
                        im.handleMessage(event.data);
                    }
                    // 连接关闭的回调方法
                    socket.onclose = function () {
                        console.log("通讯关闭连接!!");
                    }
                }
            },
            handleMessage: function (msg) {

                msg = JSON.parse(msg)
                //如果是群消息,转换一下id,返回群id的的字段必须是id
                if (msg.type == "group") {
                    var temId = msg.id;
                    msg.id = msg.toId;
                    msg.toId = temId;
                }
                //console.log(msg)
                layim.getMessage(msg);
            }
        };
        im.init();

四、附上完整jsp代码
<%@ page contentType=“text/html;charset=UTF-8” language=“java” %>

LayIM 3.x PC版本地演示
<link rel="stylesheet" href="/lib/layMI/css/layui.css">

<style>
</style>

注意:必须在登录的时候传一个登录用户的id到消息处理类中作为用户列表的key

MyHandler类

    // 用户连接成功 就被调用
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        //获取传来的登录用户的id
        String idstr = session.getUri().toString().split("=")[1];
        int id = Integer.parseInt(idstr);
        System.err.println("用户连接成功");
        //保存对应的WebSocketSession
        userSocketSessionMap.put(id, session);
    }

1.4、效果图
谷歌浏览
在这里插入图片描述
火狐浏览器
在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值