王卫文 發表於 2021-5-6 12:28:00

解决sockjs、stomp在uni-app端使用的坑

<p><span style="font-size: 16px">  我们项目之前前后端使用的是Stomp + SockJs实现的在线直播的实时聊天,现在需要搬到app上,所以要在uni-app上面也实现一次,结果就很自然的出问题了。下面整理一下在uniapp整合WebSocket中遇到的bug。</span></p>
<p><span style="font-size: 16px">1、第一次尝试</span></p>
<p><span style="font-size: 16px">  先像web开发一样去写:先引入stomjs和sockjs包,然后用 new SockJS(url) 的方式去实例化SockJs,然后通过创建StompClient去连接订阅。</span></p>
<p><span style="font-size: 16px">  H5时连接没有任何问题,但换到uni-app的时候问题就来了,sockjs.js里面用了一点操作DOM元素的方法,比如<code>document.getElementsByTagName</code>,uni-app是解析不了的,原生的微信小程序好像也解析不了。</span></p>
<p><span style="font-size: 16px">2、第二次尝试</span></p>
<p><span style="font-size: 16px">  这次放弃了使用外部的js,采用原生的uni.connectSocket()的方式创建连接。</span></p>
<div class="cnblogs_code">
<pre><span style="font-size: 16px">SocketTask =<span style="color: rgba(0, 0, 0, 1)"> uni.connectSocket({</span>
  url: <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">http://***:8081/socket</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(0, 128, 0, 1)">  //</span><span style="color: rgba(0, 128, 0, 1)">url: 'ws:</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">localhost:8081/socket',</span>
  data: <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">data</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">,
  header: {
</span><span style="color: rgba(128, 0, 0, 1)">    '</span><span style="color: rgba(128, 0, 0, 1)">content-type</span><span style="color: rgba(128, 0, 0, 1)">'</span>: <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">application/json</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">
  },
  method: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">post</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">,
  success: function(res) {
    console.log(</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">WebSocket连接创建</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">, res)
  },
  fail: function(err) {
    uni.showToast({
      title: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">网络异常!</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">,
    })
    console.log(err)
  },
})</span></span></pre>
</div>
<p><span style="font-size: 16px">  代码中显示的就很清楚了,uni-app连接websocket的时候要是用ws或者wss,相当于http和https,不然就会报无效的url这种异常,但是这个时候还是会抛异常:Error during WebSocket handshake: Unexpected response code: 200</span></p>
<p><span style="font-size: 16px">  其中遇到的问题就是使用原生websocket怎么也连不上的问题,后来就查看服务端是如何处理sockjs的。然后我就拉取了后端代码,去后端代码里最好找到了问题的关键,先贴一下服务端配置:</span></p>
<div class="cnblogs_code">
<pre><span style="font-size: 16px"><span style="color: rgba(0, 0, 0, 1)">    @Override
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">用来注册Endpoint,“/event-websocket”即为客户端尝试建立连接的地址。</span>
    <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> registerStompEndpoints(StompEndpointRegistry registry) {
      registry
            .addEndpoint(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">/event-websocket</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
            .setAllowedOrigins(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">*</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
            .withSockJS();
    }</span></span></pre>
</div>
<p><span style="font-size: 16px">  可以看到服务端是开启了sockjs模式的,将此处的withSockJS()去除,就可以通过原生方式连接了。因为sockjs在浏览器不支持wobsoket请求是会自动切换为http请求轮训方式。</span></p>
<p><span style="font-size: 16px">  但是去除withSockJS(),那么web就无法连接了,怎么办呢?可以注册建立2个链接地址,一个给web使用sockjs模式,另一个给app使用,不使用sockjs模式。</span></p>
<div class="cnblogs_code">
<pre><span style="font-size: 16px"><span style="color: rgba(0, 0, 0, 1)">    @Override
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">用来注册Endpoint,“/event-websocket”即为客户端尝试建立连接的地址。</span>
    <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> registerStompEndpoints(StompEndpointRegistry registry) {
      registry
            .addEndpoint(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">/event-websocket</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
            .setAllowedOrigins(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">*</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
            .withSockJS();

      registry
            .addEndpoint(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">/event-websocket-app</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
            .setAllowedOrigins(</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">*</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
    }</span></span></pre>
</div>
<p><span style="font-size: 16px">3、第3次尝试</span></p>
<p><span style="font-size: 16px">  经过上述处理,以为万事大吉了,但是结果在安卓app上是可以正常建立链接的,但是在ios app上却不行,百思不得解,最后解决方式是将域名模式改成ip模式就可以了</span></p>
<div class="cnblogs_code">
<pre><span style="font-size: 16px"><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 连接后台</span>
<span style="color: rgba(0, 0, 0, 1)">initSocket () {
  let that </span>= <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">
  let _unitoken </span>= uni.getStorageSync(<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">token</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">)
  let _token </span>= _unitoken ? _unitoken.substring(<span style="color: rgba(128, 0, 128, 1)">7</span>) : <span style="color: rgba(128, 0, 0, 1)">''</span>
<span style="color: rgba(0, 128, 0, 1)">  //</span><span style="color: rgba(0, 128, 0, 1)"> 当未登录或登录失效时后台根据_randomUserId模拟匿名用户</span>
  let _randomUserId = (-generateRandom(<span style="color: rgba(128, 0, 128, 1)">100000</span><span style="color: rgba(0, 0, 0, 1)">))
  let _eventId </span>= <span style="color: rgba(0, 0, 255, 1)">this</span>.<span style="color: rgba(0, 0, 255, 1)">params</span><span style="color: rgba(0, 0, 0, 1)">.eventId
  let sockUrl </span>= wsdomain + `<span style="color: rgba(0, 0, 255, 1)">event</span>-websocket-app?eventId=${_eventId}&amp;token=${_token}&amp;userId=<span style="color: rgba(0, 0, 0, 1)">${_randomUserId}`
  let socket </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> UniWebSocket(sockUrl)
</span><span style="color: rgba(0, 0, 255, 1)">  this</span>.stompClient =<span style="color: rgba(0, 0, 0, 1)"> Stomp.over(socket)
  </span><span style="color: rgba(0, 0, 255, 1)">this</span>.stompClient.connect({}, (res) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 订阅服务端提供的某个topic</span>
    <span style="color: rgba(0, 0, 255, 1)">this</span>.stompClient.subscribe(`/topic/<span style="color: rgba(0, 0, 255, 1)">event</span>/${_eventId}`, (frame) =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
      that.addBarage(JSON.parse(frame.body))
    })
    that.sentFirst()
  }, (err) </span>=&gt;<span style="color: rgba(0, 0, 0, 1)"> {
    console.log(</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">失败:</span><span style="color: rgba(128, 0, 0, 1)">'</span> +<span style="color: rgba(0, 0, 0, 1)"> err)
</span><span style="color: rgba(0, 0, 255, 1)">    if</span> (<span style="color: rgba(0, 0, 255, 1)">this</span>.infoCount &gt; <span style="color: rgba(128, 0, 128, 1)">5</span><span style="color: rgba(0, 0, 0, 1)">) {
      let _speaker </span>= <span style="color: rgba(0, 0, 255, 1)">this</span>.<span style="color: rgba(0, 0, 255, 1)">event</span>.currentAgendaSpeaker ||<span style="color: rgba(0, 0, 0, 1)"> {}
      let _tip </span>=<span style="color: rgba(0, 0, 0, 1)"> {
        headimgurl: _speaker.icon,
        name: _speaker.name,
        text: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">温馨提示:您的连接已断开,将无法收到聊天信息,请退出直播间重新进入。</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">
      }
      </span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.infoFlow.push(_tip)
      </span><span style="color: rgba(0, 0, 255, 1)">this</span>.infoCount = <span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)">
    }
</span><span style="color: rgba(0, 0, 255, 1)">    if</span> (!<span style="color: rgba(0, 0, 255, 1)">this</span>.timer) <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.resetWebSocket()
    </span><span style="color: rgba(0, 0, 255, 1)">else</span> clearInterval(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.timer)
  })
  </span><span style="color: rgba(0, 0, 255, 1)">this</span>.stompClient.debug = <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">
},</span></span></pre>
</div>
<p><span style="font-size: 16px">  wsdomain由原域名形式换成ip形式就可以了。</span></p>
<div class="cnblogs_code">
<pre><span style="font-size: 16px">wss:<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">dev.modb.pro/ws/</span>
<span style="color: rgba(0, 0, 0, 1)">
ws:</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">服务器ip:8080/</span></span></pre>
</div>
<p>&nbsp;</p><br><br>
来源:https://www.cnblogs.com/goloving/p/14734630.html
頁: [1]
查看完整版本: 解决sockjs、stomp在uni-app端使用的坑