websocket:后台flaks socketio以及socket-client前端的配合

1,023 阅读4分钟

前后端的交互问题

http的单项传递

​ 最基本的协议,通常前段向后台发送,有get,post等等方法,现在前端上也有很多别的包可以用,axios等都可。但缺点在于它只能是前端向后端发起请求。

双向信息传递

要用到这个是因为近期项目中需要进行后台到前端的消息通知,也就是一个前后端的交互过程,在以往的过程中我们主要是利用http来进行前端到后端的请求,这样的交互方式是单项的,当我们需要后台传递消息给前端的时候,可以使用websocket。当然,暴力破解是永远可行的,所以技术上通常有三种方式:短轮询 长轮询 websocket

暴力破解法:长短轮询

暴力破解永远是最熟悉的方法——客户端向服务端隔一段时间发送http请求回后台,间隔时间长短决定了轮询的次数,不过由于每次发送http请求的时候报文头可能都会比内容多,所以轮询会有浪费带宽的坏处,但简单的点在于它实现起来没有任何技术上的压力。

websocket:真正的双工实现

比起暴力破解,websocket更加智能和优雅🤯(当然,是在做出来的前提下,没做出来,各种奇怪的错误更让人自闭

websocket的机制

websocket首先会用http来进行连接,连接完成之后此项服务就会交给websocket来管理,之后就会建立长久的连接。

websocket的js原生实现

websocket的原生实现比较简单,这里用python来写后端代码,js来写前段代码(js用的es6的写法,这个是基于react框架,从中间截取出来)。

import asyncio
import websockets

# 接收客户端消息并处理,这里只是简单把客户端发来的返回回去
async def recv_msg(websocket):
    while True:
        recv_text = await websocket.recv()
        response_text = f"your submit context: {recv_text}"
        await websocket.send(response_text)

# 服务器端主逻辑
# websocket和path是该函数被回调时自动传过来的,不需要自己传
async def main_logic(websocket, path):
    await recv_msg(websocket)

# 把ip换成自己本地的ip
start_server = websockets.serve(main_logic, 'localhost', 5678)

asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()
// 打开一个 web socket
const ws = new WebSocket("ws://localhost:5678");

// 连接建立后
ws.onopen=()=>
{
	console.log("成功连接")
}

// 接收到服务器消息后,建立连接后会一直监听
ws.onmessage=(e)=> 
{ 
    const received_msg = e.data;
    console.log("收到消息:"+received_msg);
}

//传递数据到服务器
ws.send("msg")

// 连接关闭后
ws.onclose =()=>
{ 
    console.log("连接已关闭..."); 
}

这样的实现很简洁也很简单,对于前端来说,所有使用都在这上面做组合就可以了。

websocket的实现:基于socket.io

socket.io的官网地址:socket.io/

为什么要用socket.io的原因,孩子没娘说来话长,因为本身后端项目是基于python flask框架的,所以最初就定下要在flask框架下使用,而查询到flask框架包装好的websocket有:flask sockets flask socketio

flask sockets:对于websocket的简单包装,只能用在websocket阶段,如果不能使用websocket的情况就无法使用

flask socketio:这个是更完善的一个包括,在可以使用websocket的时候会使用websocket,不能使用的时候会采用长轮询。

所以最终决定用flaks socketio,然后前段采用socketio-client,而后台的使用可以参照网上的写法

下载的包是npm install react-socket-io,别问为什么,问就是不知道.

//使用方法

//导入,采用es6的方法
import io from 'socket.io-client'

//连接后台 http://ip:port/namespace
const socket = io("http://192.168.6.140:6888/test_conn")

//监听事件,这个server_response并不是固定的,他是和后端成对的
//如果前端监听 'server_response',他对应监听的消息就是后端socketio.emit('server_response','msg')
socket.on('server_response',(data)=>{
    console.log(data)
    //传送标记为event的消息,后端监听socketio.on('event',namespace='/test_conn')
    socket.emit('event',"message")
})

踩坑记录----错误记录

  • Cannot read property 'sid' of undefined

    • 前段可以接受到后台发送的sid信息,但是在socktio解析过程中报错无法得到sid的信息

    • //network中获取的第一个消息
      0{"sid":"NWUwZWY4NzBiYjk1OQ==","upgrades":[],"pingInterval":44000,"pingTimeout":36000}
      
      
      //network的信息流
      server----------------------------client
      ----0{sid,upgrades,pinginterval...}---->
      <------------------{40}-----------------
      ------------------>{40}---------------->
      ------------------>{40}---------------->
      

      前段连接代码采用上面的引入import io from 'socket.io-client'不要用require('socket.io-client')

  • 后台报错http1.1 400或者http1.1 404

    • 后台socketio应该有

      socketio = SocketIO(app, async_mode=async_mode,cors_allowed_origins='*')