websocket 使用介绍

websocket介绍

websocket最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种

websocket 使用

websocket 连接创建分为前端和服务量部分 ,中间通过nginx转发,废话不多说,直接上代码

1、前端部分

// # js websocket 创建
    /**
     *
     * @param url
     * @param onmessageFn
     * @param onopenFn
     */
    createWebsocket: function (url, onmessageFn, onopenFn) {
      const self = this
      if(!url) {
        console.log(`url不能为空`)
        return
      }
      let websocket = null
      const getWsUrl = (url) => {
        const protocol = document.location.protocol
        const domain = document.domain
        const ws = protocol === 'https:' ? 'wss' : 'ws'
        const wsUrl = `${ws}://${domain}/${url}`
        return wsUrl
      }
      const wsUrl = getWsUrl(url)
      // 判断当前浏览器是否支持WebSocket
      if ('WebSocket' in window) {
        websocket = new ReconnectingWebSocket(wsUrl, null, { reconnectInterval: 10 })
      } else {
        console.log('当前浏览器 Not support websocket')
      }
      // 连接发生错误的回调方法
      websocket.onerror = function () {
        console.log(`${wsUrl} 连接发生错误`)
      }
      // 连接成功建立的回调方法
      websocket.onopen = function () {
        console.log(`${wsUrl} 连接成功`)
        onopenFn && onopenFn()
      }

      // 接收到消息的回调方法
      websocket.onmessage = function (event) {
        console.log(`${wsUrl} 接受消息:${event.data}`)
        onmessageFn && onmessageFn(event.data)
      }

      // 连接关闭的回调方法
      websocket.onclose = function (e) {
        console.log(`${wsUrl} 连接关闭`)
      }

      // 监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
      window.onbeforeunload = function () {
        websocket && websocket.close &&  websocket.close()
      }
      return websocket
    },
    // 关闭websocket
    closeWebSocket: function (websocket) {
      websocket && websocket.close &&  websocket.close()
    }

2、服务部分

package com.kuaisu.xqt.websocket;

import com.alibaba.fastjson.JSON;
import com.github.dapeng.core.SoaException;
import com.google.gson.Gson;
import com.isuwang.soa.im.message.domain.TSendMessageBodyRequest;
import com.isuwang.soa.im.message.domain.TSendMessageRequest;
import com.kuaisu.platform.helper.AttachmentHelper;
import com.kuaisu.platform.oauth.domain.OauthUserDetails;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPubSub;

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.security.Principal;
import java.util.List;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

/**
 * 个人工作平台企点qq
 */
@ServerEndpoint(value = "/websocket/personalPlatformQqMsg")
public class PersonalPlatformQqMsgWebsocket {

    private static final Logger logger = LoggerFactory.getLogger(PersonalPlatformQqMsgWebsocket.class);

    private static final String INFO_PLATFORM = "personalPlatformQqMsg";

    //concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。若要实现服务端与单一客户端通信的话,可以使用Map来存放,其中Key可以为用户标识
    private static final CopyOnWriteArraySet<PersonalPlatformQqMsgWebsocket> qqMsgWebsocketSet = new CopyOnWriteArraySet<PersonalPlatformQqMsgWebsocket>();

    //与某个客户端的连接会话,需要通过它来给客户端发送数据
    private Session session;

    private static Executor singleThreadExecutor = Executors.newSingleThreadExecutor();

    private byte[] acceptBytes = null;
    @Autowired
    protected JedisPool jedisPool;
    private Gson gson = new Gson();

    public void init() throws Exception {
        logger.info("init 开始执行");
        singleThreadExecutor.execute(() -> {
            try (Jedis jedis = jedisPool.getResource()) {
                jedis.subscribe(new JedisPubSub() {
                    @Override
                    public void onMessage(String channel, String message) {
                        // TODO 监听推动消息
                        logger.info(INFO_PLATFORM + "监听到消息推送");
//                        TUserBehaviorStatisticsDetail ubs = gson.fromJson(message, TUserBehaviorStatisticsDetail.class);
//                        try {
//                            UserBehaviorWebsocket.sendMessage(ubs.allocStaffId.get(), message);
//                        } catch (IOException e) {
//                            e.printStackTrace();
//                        }
                    }
                }, "user_behavior_statistics_topic");
            }
        });
    }


    /**
     * 连接建立成功调用的方法
     *
     * @param session 可选的参数。session为与某个客户端的连接会话,需要通过它来给客户端发送数据
     */
    @OnOpen
    public void onOpen(Session session, EndpointConfig config) {
        //获取用户信息
        Principal principal = session.getUserPrincipal();
        OauthUserDetails oauthUserDetails = (OauthUserDetails) ((OAuth2Authentication) principal).getUserAuthentication().getPrincipal();
        com.kuaisu.platform.oauth.domain.User user = oauthUserDetails.user();
        this.user = user;
        this.session = session;
        qqMsgWebsocketSet.add(this);     //加入set中
        logger.info(INFO_PLATFORM + "有新连接加入!当前在线人数为:" + getOnlineCount());
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        qqMsgWebsocketSet.remove(this);  //从set中删除
        logger.info(INFO_PLATFORM + "有一连接关闭!当前在线人数为:" + getOnlineCount());
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息
     * @param session 可选的参数
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        logger.info(INFO_PLATFORM + "来自客户端的消息:" + message);
        // TODO 客服端发送qq消息
        TSendMessageRequest sendMessageRequest = parseMsg(message);
        toSendMsg2Server(sendMessageRequest);

//
    }

    @OnMessage
    public void onMessageB(byte[] b, boolean last, Session session) {
        // process partial data here, which check on last to see if these is more on the way
        if (this.acceptBytes == null) {
            this.acceptBytes = new byte[0];
        }
        this.acceptBytes = addBytes(this.acceptBytes, b);
        if (last) { // 接收完毕
            // TODO 客服端发送qq消息
            try {
                String msg = new String(this.acceptBytes, "utf-8");
                logger.info(INFO_PLATFORM + "来自客户端的blob消息length:" + msg.length());
                this.acceptBytes = null;
               
            } catch (IOException e) {
                logger.error(e.getMessage(), e);
            }
        }
    }

    /**
     * 发生错误时调用
     *
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        qqMsgWebsocketSet.remove(this);  //从set中删除
        logger.info(INFO_PLATFORM + "连接发生错误:" + error.getMessage(), error);
    }

    /**
     * 这个方法与上面几个方法不一样。没有用注解,是根据自己需要添加的方法。
     * 给websocket发送信息
     * @param msg
     * @throws IOException
     */
    public void sendMessage(String msg) throws IOException {
        this.session.getBasicRemote().sendText(msg);
    }

    /**
     * 给websocket发送信息
     * @param object
     * @throws IOException
     * @throws EncodeException
     */
    public void sendMessage(Object object) throws IOException {
        String msg = JSON.toJSONString(object);
        sendMessage(msg);
    }

    /**
     * @param data1
     * @param data2
     * @return data1 与 data2拼接的结果
     */
    private static byte[] addBytes(byte[] data1, byte[] data2) {
        byte[] data3 = new byte[data1.length + data2.length];
        System.arraycopy(data1, 0, data3, 0, data1.length);
        System.arraycopy(data2, 0, data3, data1.length, data2.length);
        return data3;
    }

    /**
     * 统计连接数
     *
     * @return
     */
    public static int getOnlineCount() {
        return qqMsgWebsocketSet.size();
    }

    /**
     * 获取当前的websocket集合
     *
     * @return
     */
    public static CopyOnWriteArraySet<PersonalPlatformQqMsgWebsocket> getWebsocketSet() {
        return qqMsgWebsocketSet;
    }
    
}

3、nginx中间层

    # WebSocket support (nginx 1.4)
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";

重点:发送大文本

通过websocket 直接发送大文本,比如base64的附件等,直接通过websocket.send是不行的,经测试服务端会报异常。那么该如何处理呢?好在websocket是支持二进制、Blod大本文的。在发送之前,将内容用Blod分装一层即可。代码如下:

// websocket 发送大文本
sendMsgByBlob: function (websocket, msg) {
      const blob = new Blob([msg], {
        type: 'text/plain'
      })
      websocket.send( blob)
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值