springboot+maven+WebSocket-tio 简易聊天室后台代码

使用tio编写建议的websocket聊天室后台

[官方文档](https://www.tiocloud.com/doc/tio/?pageNumber=1)

maven引入

<!-- tio-websocket -->
<dependency>
    <groupId>org.t-io</groupId>
    <artifactId>tio-websocket-server</artifactId>
    <version>3.5.9.v20200214-RELEASE</version>
</dependency>

MyWsMsgHandler 处理类

package com.wlq.demo.conf;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONObject;
import com.wlq.demo.uitls.RedisHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.tio.core.ChannelContext;
import org.tio.core.Tio;
import org.tio.http.common.HttpRequest;
import org.tio.http.common.HttpResponse;
import org.tio.utils.lock.SetWithLock;
import org.tio.websocket.common.WsRequest;
import org.tio.websocket.common.WsResponse;
import org.tio.websocket.server.handler.IWsMsgHandler;

import java.util.ArrayList;
import java.util.HashMap;

/**
 * 消息处理类
 */
@Component
public class MyWsMsgHandler implements IWsMsgHandler {

    @Autowired
    private RedisHelper redisHelper;

    /**
     * <li>对httpResponse参数进行补充并返回,如果返回null表示不想和对方建立连接,框架会断开连接,如果返回非null,框架会把这个对象发送给对方</li>
     * <li>注:请不要在这个方法中向对方发送任何消息,因为这个时候握手还没完成,发消息会导致协议交互失败。</li>
     * <li>对于大部分业务,该方法只需要一行代码:return httpResponse;</li>
     *
     * @param httpRequest
     * @param httpResponse
     * @param channelContext
     * @return
     * @throws Exception
     */
    @Override
    public HttpResponse handshake(HttpRequest httpRequest, HttpResponse httpResponse, ChannelContext channelContext) throws Exception {
        // 可以在此做一些业务逻辑,返回null表示不想连接
        return httpResponse;
    }

    /**
     * 握手成功后触发该方法
     *
     * @param httpRequest
     * @param httpResponse
     * @param channelContext
     * @throws Exception
     */
    @Override
    public void onAfterHandshaked(HttpRequest httpRequest, HttpResponse httpResponse, ChannelContext channelContext) throws Exception {
        // 拿到用户id
        String id = httpRequest.getParam("userId");
        //接收人id
        String receiver = httpRequest.getParam("receiver");
        //1客服 0用户
        String chatType = httpRequest.getParam("chatType");

        channelContext.setAttribute("chatType",chatType);
        channelContext.setAttribute("receiver",receiver);
        // 绑定用户
        Tio.bindUser(channelContext, id);

        //获取某用户的ChannelContext集合
        SetWithLock<ChannelContext> lock = Tio.getChannelContextsByUserid(channelContext.tioConfig, id);

        // 给用户发送消息
        JSONObject message = new JSONObject();
        message.put("msg", "连接成功...");
        message.put("sendName", "系统提醒");

        WsResponse wsResponse = WsResponse.fromText(message.toString(), "UTF-8");
        Tio.sendToUser(channelContext.tioConfig, id, wsResponse);
    }

    /**
     * <li>当收到Opcode.BINARY消息时,执行该方法。也就是说如何你的ws是基于BINARY传输的,就会走到这个方法</li>
     *
     * @param wsRequest
     * @param bytes
     * @param channelContext
     * @return 可以是WsResponse、byte[]、ByteBuffer、String或null,如果是null,框架不会回消息
     * @throws Exception
     */
    @Override
    public Object onBytes(WsRequest wsRequest, byte[] bytes, ChannelContext channelContext) throws Exception {

        String sendNameId = channelContext.userid;
        String receiverId = String.valueOf(channelContext.getAttribute("receiver"));
        String chatType = String.valueOf(channelContext.getAttribute("chatType"));

        JSONObject message = new JSONObject();
        message.put("receiver",receiverId);
        // 发送消息者
        message.put("sendName",sendNameId);
        // 消息
        message.put("msg","这是图片");
        // 发送时间
        String time = DateUtil.format(DateTime.now(), DatePattern.NORM_DATETIME_PATTERN);
        message.put("time",time);
        // type
        message.put("chatType",chatType);

        // 保存聊天记录到DB等业务逻辑...
        JSONArray list = new JSONArray();
        Object o;
        if (Boolean.parseBoolean(chatType)){
            //客服
            o = redisHelper.get(receiverId);
            if (null == o) {
                list.add(message.toString());
                redisHelper.set(receiverId,list.toString());
            }else {
                JSONArray array = JSONUtil.parseArray(o);
                array.add(message.toString());
                redisHelper.set(receiverId, array.toString());
            }
        }else {
            //用户
            o = redisHelper.get(sendNameId);
            if (null == o) {
                list.add(message.toString());
                redisHelper.set(sendNameId,list.toString());
            }else {
                JSONArray array = JSONUtil.parseArray(o);
                array.add(message.toString());
                redisHelper.set(sendNameId, array.toString());
            }
        }
        System.out.println("我走了onBytes");
        return null;
    }

    /**
     * 当收到Opcode.CLOSE时,执行该方法,业务层在该方法中一般不需要写什么逻辑,空着就好
     *
     * @param wsRequest
     * @param bytes
     * @param channelContext
     * @return 可以是WsResponse、byte[]、ByteBuffer、String或null,如果是null,框架不会回消息
     * @throws Exception
     */
    @Override
    public Object onClose(WsRequest wsRequest, byte[] bytes, ChannelContext channelContext) throws Exception {
        // 关闭连接
        String chatType = String.valueOf(channelContext.getAttribute("chatType"));
        if (!Boolean.parseBoolean(chatType)){
            //用户断开链接
            //保存聊天记录
            Object chatLog = channelContext.getAttribute("chatLog");
            JSONArray array = JSONUtil.parseArray(chatLog);
        }
        Tio.remove(channelContext, "WebSocket Close");
        return null;
    }

    /**
     * <li>当收到Opcode.TEXT消息时,执行该方法。也就是说如何你的ws是基于TEXT传输的,就会走到这个方法</li>
     *
     * @param wsRequest
     * @param text
     * @param channelContext
     * @return 可以是WsResponse、byte[]、ByteBuffer、String或null,如果是null,框架不会回消息
     * @throws Exception
     */
    @Override
    public Object onText(WsRequest wsRequest, String text, ChannelContext channelContext) throws Exception {
        JSONObject message = new JSONObject();

        // 发送消息者
        String sendNameId = channelContext.userid;
        message.put("sendName",sendNameId);
        //接收消息者
        String receiverId = String.valueOf(channelContext.getAttribute("receiver"));
        message.put("receiver",receiverId);
        // 消息
        message.put("msg",text);
        // 发送时间
        String time = DateUtil.format(DateTime.now(), DatePattern.NORM_DATETIME_PATTERN);
        message.put("time",time);
        // 是否是客服
        String chatType = String.valueOf(channelContext.getAttribute("chatType"));
        message.put("chatType",chatType);

        // 保存聊天记录到DB等业务逻辑...
        //todo 方案一:说一句保存一句
        JSONArray list = new JSONArray();
        Object o;
        if (Boolean.parseBoolean(chatType)){
            //客服
            o = redisHelper.get(receiverId);
            if (null == o) {
                list.add(message.toString());
                redisHelper.set(receiverId,list.toString());
            }else {
                JSONArray array = JSONUtil.parseArray(o);
                array.add(message.toString());
                redisHelper.set(receiverId, array.toString());
            }
        }else {
            //用户
            o = redisHelper.get(sendNameId);
            if (null == o) {
                list.add(message.toString());
                redisHelper.set(sendNameId,list.toString());
            }else {
                JSONArray array = JSONUtil.parseArray(o);
                array.add(message.toString());
                redisHelper.set(sendNameId, array.toString());
            }
        }
        //todo 方案二:先保存到attribute,用户断开链接的时候再保存到数据库
        channelContext.setAttribute("chatLog",list);

        WsResponse wsResponse = WsResponse.fromText(message.toString(), "UTF-8");
        Tio.sendToUser(channelContext.tioConfig, receiverId, wsResponse);

        //返回值是要发送给客户端的内容,一般都是返回null
        JSONObject resp = new JSONObject();
        resp.put("sendName", "系统提醒");
        resp.put("msg", "发送成功");
        return resp.toString();
    }
}

WebSocketConfig 配置类

package com.wlq.demo.conf;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.tio.server.ServerTioConfig;
import org.tio.websocket.server.WsServerStarter;

import java.io.IOException;

/**
 * websocket 配置类
 */
@Configuration
public class WebSocketConfig {

    /**
     * 注入消息处理器
     */
    @Autowired
    private MyWsMsgHandler myWsMsgHandler;

    /**
     * 启动类配置
     *
     * @return
     * @throws IOException
     */
    @Bean
    public WsServerStarter wsServerStarter() throws IOException {
        // 设置处理器
        WsServerStarter wsServerStarter = new WsServerStarter(6789, myWsMsgHandler);
        // 获取到ServerTioConfig
        ServerTioConfig serverTioConfig = wsServerStarter.getServerTioConfig();
        // 设置心跳超时时间,默认:1000 * 120
        serverTioConfig.setHeartbeatTimeout(1000 * 120);
        // 启动
        wsServerStarter.start();
        return wsServerStarter;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值