编程知识

当前位置:mg游戏平台手机版 > 编程知识 > 长连接调用方式,它底层基于engine.io

长连接调用方式,它底层基于engine.io

来源:http://www.alcandaskincare.com 作者:mg游戏平台手机版 时间:2019-10-20 20:02

python websocket

在等级次序中用到socket.io抓牢时推送,遂花了点时间看了socket.io完成,做个简易解析,如有错漏,招待指正。

安装

1 概述

socket.io是多个依据WebSocket的CS的实时通讯库,它底层基于engine.io。engine.io使用WebSocket和xhr-polling(或jsonp)封装了活龙活现套自个儿的情商,在不补助WebSocket的低版本浏览器中(支持websocket的浏览器版本见这里)使用了长轮询(long polling)来顶替。socket.io在engine.io的底子上平添了namespace,room,自动重连等特征。

正文接下去会先简介websocket协议,然后在这里基础上上课下engine.io和socket.io协议以致源码解析,后续再通过例子表明socket.io的劳作流程。

pip install websocket-client

2 WebSocket协议

我们掌握,在HTTP 合同开采的时候,并非为了双向通讯程序希图的,开端的 web 应用程序只必要 “乞请-响应” 就够了。由于历史由来,在创立具有双向通讯机制的 web 应用程序时,就不得不动用 HTTP 轮询的方式,因此爆发了 “短轮询” 和 “长轮询”(注意区分短连接和长连接)。

短轮询通过顾客端定期轮询来掌握服务端是或不是有新的新闻产生,劣点也是鲜明,轮询间距大了则音信非常不足实时,轮询间距过小又会损耗过多的流量,扩充服务器的承受。长轮询是对短轮询的优化,须求服务端做相应的修改来扶持。顾客端向服务端发送诉求时,若是那时候服务端未有新的消息发生,并不立时回去,而是Hang住活龙活现段时间等有新的新闻依然逾期再重返,顾客端收到服务器的答问后继续轮询。能够看看长轮询比短轮询能够减掉大气没用的呼吁,並且顾客端接收取新消息也会实时不菲。

尽管如此长轮询比短轮询优化了无数,但是每一次央浼如故都要带上HTTP央浼尾部,並且在长轮询的连续几天完结未来,服务器端储存的新新闻要等到后一次客商端连接时技能传递。更加好的秘诀是只用多少个TCP连接来贯彻顾客端和服务端的双向通信,WebSocket商量便是为此而生。WebSocket是依据TCP的多少个独自的合计,它与HTTP契约的唯大器晚成涉及就是它的拉手供给能够当作三个Upgrade request行经HTTP服务器剖析,且与HTTP使用同样的端口。WebSocket暗中认可对常见乞请使用80端口,合同为ws://,对TLS加密央求使用443端口,左券为wss://

拉手是经过一个HTTP Upgrade request以前的,二个伸手和响应底部示比如下(去掉了非亲非故的底部)。WebSocket握手诉求尾部与HTTP诉求尾部是优异的(见大切诺基FC2616)。

## Request Headers ##
Connection: Upgrade
Host: socket.io.demo.com
Origin: http://socket.io.demo.com
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
Sec-WebSocket-Key: mupA9l2rXciZKoMNQ9LphA==
Sec-WebSocket-Version: 13
Upgrade: websocket

## Response Headers ##
101 Web Socket Protocol Handshake
Connection: upgrade
Sec-WebSocket-Accept: s4VAqh7eedG0a11ziQlwTzJUY3s=
Sec-WebSocket-Origin: http://socket.io.demo.com
Server: nginx/1.6.2
Upgrade: WebSocket
  • Upgrade 是HTTP/1.第11中学分明的用来转移当前三番五次的应用层左券的头顶,表示顾客端希望用现存的接连转变来新的应用层公约WebSocket合同。

  • Origin 用于幸免跨站攻击,浏览器日常会利用那个来标志原始域,对于非浏览器的顾客端应用能够根据要求使用。

  • 要求头中的 Sec-WebSocket-Version 是WebSocket版本号,Sec-WebSocket-Key 是用于握手的密钥。Sec-WebSocket-Extensions 和 Sec-WebSocket-Protocol 是可选用,暂不钻探。

  • 响应头中的 Sec-WebSocket-Accept 是将伏乞头中的 Sec-WebSocket-Key 的值加上多少个定点魔数258EAFA5-E914-47DA-95CA-C5AB0DC85B11经SHA1+base64编码后获取。总结进度的python代码示例(uwsgi中的完结见 core/websockets.c的 uwsgi_websocket_handshake函数):

    magic_number = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
    key = 'mupA9l2rXciZKoMNQ9LphA=='
    accept = base64.b64encode(hashlib.sha1(key + magic_number).digest())
    assert(accept == 's4VAqh7eedG0a11ziQlwTzJUY3s=')
    
  • 顾客端会检查响应头中的status code 和 Sec-WebSocket-Accept 值是不是是期望的值,假如开掘Accept的值不得法也许状态码不是101,则不会确立WebSocket连接,也不会发送WebSocket数据帧。

WebSocket切磋使用帧(Frame)收发数据,帧格式如下。基于安全考虑衡量,顾客端发送给服务端的帧必需经过4字节的掩码(Masking-key)加密,服务端收到音信后,用掩码对数据帧的Payload Data进行异或运算解码得到数码(详见uwsgi的 core/websockets.c 中的uwsgi_websockets_parse函数),倘诺服务端收到未经掩码加密的数据帧,则应该及时关闭该WebSocket。而服务端发给客商端的数量则无需掩码加密,顾客端假若接到了服务端的掩码加密的多寡,则也必得关闭它。

 0                   1                   2                   3
      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
     +-+-+-+-+-------+-+-------------+-------------------------------+
     |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
     |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
     |N|V|V|V|       |S|             |   (if payload len==126/127)   |
     | |1|2|3|       |K|             |                               |
     +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
     |     Extended payload length continued, if payload len == 127  |
     + - - - - - - - - - - - - - - - +-------------------------------+
     |                               |Masking-key, if MASK set to 1  |
     +-------------------------------+-------------------------------+
     | Masking-key (continued)       |          Payload Data         |
     +-------------------------------- - - - - - - - - - - - - - - - +
     :                     Payload Data continued ...                :
     +---------------------------------------------------------------+

帧分为调节帧和数据帧,调节帧不可能分片,数据帧能够分片。首要字段表达如下:

  • FIN: 未有分片的帧的FIN为1,分片帧的第二个分片的FIN为0,最终贰个分片FIN为1。
  • opcode: 帧类型编号,此中央调控制帧:0x8 (Close), 0x9 (Ping), and 0xA (Pong),数据帧首要有:0x1 (Text), 0x2 (Binary)。
  • MASK:客商端发给服务端的帧MASK为1,Masking-key为加密掩码。服务端发往客商端的MASK为0,Masking-key为空。
  • Payload len和Payload Data分别是帧的数目长度和数据内容。

 

3 engine.io和socket.io

眼下提到socket.io是基于engine.io的包装,engine.io(协议版本3)有后生可畏套自个儿的商业事务,任何engine.io服务器都不可能不协助polling(满含jsonp和xhr)和websocket两种传输方式。engine.io使用websocket时有后生可畏套本身的ping/pong机制,使用的是opcode为0x1(Text)类型的数据帧,不是websocket谈判规定的ping/pong类型的帧,标准的 ping/pong 帧被uwsgi使用

engine.io的数据编码分为Packet和Payload,个中 Packet是数据包,有6种档期的顺序:

  • 0 open:从服务端发出,标记多少个新的传输格局已经展开。
  • 1 close:要求关闭这条传输连接,不过它本身并不仅息这一个一连。
  • 2 ping:顾客端周期性发送ping,服务端响应pong。注意那么些与uwsgi自带的ping/pong不平等,uwsgi里面发送ping,而浏览器重返pong。
  • 3 pong:服务端发送。
  • 4 message:实际发送的音讯。
  • 5 upgrade:在调换transport前,engine.io会发送探测包测量检验新的transport(如websocket)是还是不是可用,假设OK,则客商端会发送贰个upgrade消息给服务端,服务端关闭老的transport然后切换来新的transport。
  • 6 noop:空操作数据包,顾客端收到noop音信会将事先等待暂停的轮询暂停,用于在抽取到八个新的websocket强制三个新的轮询周期。

而Payload是指活龙活现多种绑定到大器晚成块儿的编码后的Packet,它只用在poll中,websocket里面使用websocket帧里面包车型大巴Payload字段来传输数据。借使客商端不援助XH宝马7系2,则payload格式如下,此中length是多少包Packet的长短,而packet则是编码后的多寡包内容。

<length1>:<packet1>[<length2>:<packet2>[...]]

若帮助XH大切诺基2,则payload中剧情全方位以二进制编码,此中第一位0表示字符串,1表示二进制数据,而前边随着的数字则是象征packet长度,然后以xff结尾。借使三个长短为109的字符类型的数据包,则前边长度编码是 x00x01x00x09xff,然后后边接packet内容。

<0 for string data, 1 for binary data><Any number of numbers between 0 and 9><The number 255><packet1 (first type,
then data)>[...]

engine.io服务器维护了多少个socket的字典结构用于管理总是到该机的客商端,而客商端的标记正是sid。假诺有多少个worker,则必要确认保障同多少个顾客端的连接落在同样台worker上(能够配备nginx遵照sid分发)。因为每一个worker只保险了意气风发有个别客商端连接,若是要扶持广播,room等个性,则后端需求动用 redis 可能 RabbitMQ 新闻队列,使用redis的话则是经过redis的订阅发表机制落到实处多机多worker之间的音信推送。

socket.io是engine.io的卷入,在其基础上扩张了自动重连,多路复用,namespace,room等天性。socket.io本人也许有大器晚成套左券,它Packet类型分为(CONNECT 0, DISCONNECT 1, EVENT 2, ACK 3, ERROR 4, BINARY_EVENT 5, BINARY_ACK 6)。注意与engine.io的Packet类型有所差别,不过socket.io的packet实际是依附的engine.io的Message类型发送的,在末端实例中能够见到Packet的编码方式。当连接出错的时候,socket.io会通过活动重连机制再一次连接。

 

4 源码剖析

在营造连接后,种种socket会被自动踏入到贰个私下认可的命名空间/。在各个命名空间中,socket会被暗许参加五个名称叫Nonesid的屋家。None的房间用于广播,而sid是近些日子顾客端的session id,用于单播。除默许的房屋外,我们能够根据须要将对应socket加入动和自动定义房间,roomid唯朝气蓬勃就可以。socket.io基于engine.io,扶持websocket和long polling。倘使是long polling,会定期发送GET, POST央求,当未有数量时,GET需要在拉取队列音信时会hang住(超时时间为ping提姆eout),倘使hang住中间服务器平昔尚未数量发生,则必要等到客户端发送下一个POST必要时,此时服务器会往队列中存款和储蓄POST诉求中的音讯,那样上一个GET要求才会回到。纵然upgrade到了websocket连接,则会定时ping/pong来保活连接。

为方便描述,上边提到的engine.io服务器对应源文件是engineio/server.py,engine.io套接字对应源文件engineio/socket.py,而socket.io服务器则附和socketio/server.py。上面深入分析下socket.io连接创设、消息接收和出殡和安葬、连接关闭进程。socket.io版本为1.9.0,engine.io版本为2.0.4。

先来看一下,长连接调用格局:

连年构建

先是,顾客端会发送贰个polling诉求来树立连接。此时的央求参数没有sid,表示要创立连接。 engine.io服务器通过handle_get_request()handle_post_request()主意来分别处理开头化连接以至长轮询中的 GET 和 POST 伏乞。

socket.io在起头化时便登记了3个事件到engine.io的handlers中,分别是connect(处理函数_handle_eio_connect),message(_handle_eio_message),disconnect(_handle_eio_disconnect),在engine.io套接字接收到了上述多少个品种的音信后,在本人做了对应管理后都会触发socket.io中的对应的管理函数做进一步管理。

当接过到GET伏乞且未有sid参数时,则engine.io服务器会调用 _handle_connect()艺术来树立连接。那个方法主要办事是为近来顾客端生成sid,创造Socket对象并保存到engine.io服务器的sockets集结中。做了那些最早化职业后,engine.io服务器会发送二个OPEN类型的数目包给客商端,接着会触发socket.io服务器的connect事件。

顾客端第一回三回九转的时候,socket.io也要做一些最初化的行事,那是在socket.io服务器的_handle_eio_connect()管理的。这里做的事务要害有几点:

  • 初阶化manager,举个例子用的是redis做后端队列的话,则须求开始化redis_manager,包含安装redis连接配置,订阅频道,默许频道是"socket.io",假若利用flask_socketio则频道是"flask_socketio",即使用到gevent,则还要对redis模块的socket库打monkey-patch等。

  • 将该顾客端参与到暗中认可房间None,sid中。

  • 调用代码中对connect事件注册的函数。如下边那些,注意下,socket.io中也是有个用于事件管理的handlers,它保存的是在后端代码中对socket.io事件注册的函数(开辟者定义的),而engine.io的handlers中保存的函数是socket.io注册的那七个针对connect,message和disconnect事件的原则性的管理函数。

    socketio.on("connect")
    def test_connect():
        print "client connected"
    
  • 发送贰个sockeio的connect数据包给客商端。

末尾在响应中engine.io会为客商端设置一个名字为io值为sid的cookie,响应内容payload富含三个数据包,三个是engine.io的OPEN数据包,内容为sid,pingTimeout等布置和参数;另一个是socket.io的connect数据包,内容为40。个中4象征的是engine.io的message音讯,0则代表socket.io的connect音信,以字节流回到。这里的pingTimeout顾客端和服务端分享那个布局,用于检查测试对端是不是过期。

随之会发送贰个轮询恳求和websocket握手诉求,如若websocket握手成功后客商端会发送2 probe探测帧,服务端回应3 probe,然后客商端会发送内容为5的Upgrade帧,服务端回应内容为6的noop帧。探测帧检查通过后,客商端停止轮询央求,将传输通道转到websocket连接,转到websocket后,接下去就从头为期(默许是25秒)的 ping/pong(那是socket.io自定义的ping/pong,除此而外,uwsgi也会定时(暗中同意30秒)对顾客端ping,客户端回应pong,这几个在chrome的Frames里面是看不到的,供给信任wireshark大概用任何浏览器插件来考查)。

    ws = websocket.WebSocketApp("ws://echo.websocket.org/",
                              on_message = on_message,
                              on_error = on_error,
                              on_close = on_close)
    ws.on_open = on_open
    ws.run_forever()

服务端音讯接收流程

对收到新闻的则统一通过engine.io套接字的receive()函数管理:

  • 对于轮询,生机勃勃旦接收了polling的POST央求,则会调用receive往该socket的音信队列之中发送音信,进而释放在此之前hang住的GET央浼。
  • 对于websocket:
    • 接收了ping,则会立时响应三个pong。
    • 收纳到了upgrade消息,则即时发送二个noop新闻。
    • 吸收接纳到了message,则调用socket.io注册到engine.io的_handle_eio_message措施来管理socket.io自个儿定义的种种消息。

 

服务端音信发送流程

而服务端要给顾客端发送音讯,则供给经过socket.io服务器的emit方法,注意emit方法是对准room来发送新闻的,若是是context-aware的,则emit私下认可是对namespace为/且room名称为sid的房间发送,假若是context-free的,则默许是广播即对富有连接的客户端发送新闻(当然在context-free的场地下边,你也足以内定room来只给钦定room推送音信)。

socket.io要兑现多进度以致广播,房间等效果,势绝对要衔接叁个redis之类的音讯队列,进而socket.io的emit会调用对应队列管理器pubsub_manager的emit方法,例如用redis做新闻队列则最终调用 redis_manager中的_publish() 方法通过redis的订阅公布功能将新闻推送到flask_socketio频道。另方兴未艾方面,全部的socket在连年时都订阅了 flask_socketio频道,况兼都有二个体协会程(或线程)在监听频道中是不是有音信,黄金年代旦有新闻,就能够调用pubsub_manager._handle_emit()办法对本机对应的socket发送对应的音讯,最终是由此socket.io服务器的_emit_internal()办法完结对本机中room为sid的持有socket发送新闻的,即使room为None,则就是广播,即对负有连接到本机的装有顾客端推送音信。

socket.io服务器发送音信要基于engine.io音信包装,所以总结到底照旧调用的engine.io套接字中的send()方法。engine.io为各样顾客端都会维护多个音讯队列,发送数据都以先存到行列之中待拉取,websocket除了探测帧之外的别样数据帧也都是经过该消息队列发送。

 长连接,参数介绍:

闭馆连接(只剖析websocket)

websocket只怕特别关闭的事态多多。比方客商端发了ping后等待pong超时关闭,服务端接收到ping跟上三个ping之间超过了pingTimeout;用的uwsgi的话,uwsgi发送ping,要是在websockets-pong-tolerance(默许3秒)内收取不到pong回应,也会停业连接;还也许有要是nginx的proxy_read_timeout配置的比pingInterval小等。

设若不是客商端主动关闭连接,socket.io就能在接连出错后不停重试以创制连接。重试间距和重试次数由reconnectionDelayMax(默认5秒)reconnectionAttempts(默许一向重连)设定。下边钻探客商端平常关闭的气象,各样非常关闭状态请具体意况具体解析。

客商端主动关闭

若果客商端调用socket.close()再接再砺关闭websocket连接,则会头阵送一个音讯41(4:engine.io的message,1:socket.io的disconnect)再关闭连接。如前方提到,engine.io套接字接收到音讯后会交给socket.io服务器注册的 _handle_eio_message()管理。最终是调用的socket.io的_handle_disconnect(),该函数专门的学业满含调用socketio.on("disconnect")登记的函数,将该顾客端从加盟的房子中移除,清理蒙受变量等。

uwsgi而接受到顾客端关闭websocket连接音讯后会关闭服务端到顾客端的接二连三。engine.io服务器的websocket数据接收例程ws.wait()因为老是关闭报IOError,触发服务端循环收发数据经过结束,并从维护的sockets集合中移除这一个闭馆的sid。然后调用engine.io套接字的close(wait=True, abort=True)艺术,由于是顾客端主动关闭,这里就不会再给客户端发送二个CLOSE信息。而 engine.io服务器的close方法方兴日盛致会触发socket.io在此以前注册的disconnect事件管理函数,由于前边已经调用_handle_disconnect()管理了关门连接事件,所以那边_handle_eio_disconnect()无需再做任何操作(那个操作不是多余的,其职能见后意气风发节)。

浏览器关闭

直接关门浏览器发送的是websocket的标准CLOSE新闻,opcode为8。socket.io服务端管理格局基本后生可畏致,由于这种情形下并从未发送socket.io的闭馆音信41,socket.io的关门操作必要等到engine.io触发的_handle_eio_disconnect()中管理,那便是前黄金时代节中为何engine.io服务器前面还要多调用三次 _handle_eio_disconnect()的缘由所在。

(1)url: websocket的地址。

5 实例

商业事务表明轻松令人有些头晕,websocket,engine.io,socket.io,各自行车运动组织议是怎样专业的,看看实例大概会相比明晰,为了便利测验,小编写了个Dockerfile,安装了docker的童鞋能够拉替代码实行 bin/start.sh 就能够运行具备完整的 nginx+uwsgi+gevent+flask_socketio测验意况的器皿在这里从前测验,浏览器展开http://127.0.0.1就能够测验。async_mode用的是gevent_uwsgi,完整代码见 这里。

对此不扶助websocket的低版本浏览器,socket.io会退化为长轮询的章程,通过为期的出殡GET, POST伏乞来拉取数据。非常少时,会将诉求数据的GET央求hang住,直到服务端有数据发生可能客商端的POST央求将GET必要释放,释放之后会随着再次发送八个GET央浼,除外,左券解析和拍卖流程与websocket方式基本大器晚成致。实例只针对利用websocket的开展分析

为了考查socket.io顾客端的调用流程,能够安装localStorage.debug = '*';,测量试验的前段代码片段如下(完整代码见酒馆):

 <script type="text/javascript" charset="utf-8">
    var socket = io.connect('/', {
        "reconnectionDelayMax": 10000,
        "reconnectionAttempts": 10
    });
    socket.on('connect', function() {
        $('#log').append('<br>' + $('<div/>').text('connected').html());
    })

    $(document).ready(function() {

        socket.on('server_response', function(msg) {
            $('#log').append('<br>' + $('<div/>').text('Received from server: ' + ': ' + msg.data).html());
        });

        $('form#emit').submit(function(event) {
            socket.emit('client_event', {data: $('#emit_data').val()});
            return false;
        });
    });

 </script>

测验代码比较轻巧,引进socket.io的js库文件,然后在接二连三成功后在页面展现“connected”,在输入框输入文字,能够由此连续几日发送至服务器,然后服务器将浏览器发送的字符串加上server标志回显回来。

(2)header: 客商发送websocket握手央求的伏乞头,{'head1:value1','head2:value2'}。

构建连接

在chrome中开发页面可以看出发了3个央浼,分别是:

1 http://127.0.0.1/socket.io/?EIO=3&transport=polling&t=MAkXxBR
2 http://127.0.0.1/socket.io/? EIO=3&transport=polling&t=MAkXxEz&sid=9c54f9c1759c4dbab8f3ce20c1fe43a4
3 ws://127.0.0.1/socket.io/?EIO=3&transport=websocket&sid=9c54f9c1759c4dbab8f3ce20c1fe43a4

恳请默许路线是/socket.io,注意命名空间并不会在路径中,而是在参数中传送。第4个央求是polling,EIO是engine.io合同的版本号,t是叁个随意字符串,第五个必要时还还不曾生成sid。服务端接收到新闻后会调用engine.io/server.py_handle_connect()树立连接。

回去的结果是

## Response Headers: Content-Type: application/octet-stream ##
�ÿ0{"pingInterval":25000,"pingTimeout":60000,"upgrades":["websocket"],"sid":"9c54f9c1759c4dbab8f3ce20c1fe43a4"}�ÿ40

可以见到,这里重返的是字节流的payload,content-type为"application/octet-stream"。这些payload其实富含三个packet,第二个packet是engine.io的OPEN新闻,类型为0,它的从头到尾的经过为pingInterval,pingTimeout,sid等;第三个packet类型是4(message),而它的数目内容是0,表示socket.io的CONNECT。而内部的看起来乱码的局地其实是眼前提到的payload编码中的长度的编码x00x01x00x09xffx00x02xff

  • 第3个央浼是轮询乞请,若是websocket建设构造并测量试验成功(使用内容为probe的ping/pong帧)后,会搁浅轮询乞求。可以见到轮询央浼一向hang住到websocket营造并测量检验成功后才回来,响应结果是�ÿ6,前边乱码部分是payload长度编码x00x01xff,后边的数字6是engine.io的noop音信。

  • 第二个必要是websocket握手央求,握手成功后,可以在chrome的Frames里头来看websocket的多少帧交互流程,能够看来如前方分析,确实是首发的探测帧,然后是Upgrade帧,接着就是定时的ping/pong帧了。

    2probe
    3probe
    5
    2
    3
    ...
    

(3)on_open:在建构Websocket握手时调用的可调用对象,那些方法唯有三个参数,正是此类自己。

顾客端发送音讯给服务端

假诺要发送新闻给服务器,在浏览器输入框输入test,点击echo开关,能够观察websocket发送的帧的内容如下,当中4是engine.io的message类型标识,2是socket.io的EVENT类型标记,而背后则是事件名称和数目,数据能够是字符串,字典,列表等档案的次序。

42["client_event",{"data":"test"}]

(4)on_message:那个目的在接受到服务器重临的新闻时调用。有多少个参数,一个是此类本人,八个是我们从服务器获取的字符串(utf-8格式)。

服务端接收消息流程

而服务端接收音讯并赶回一个新的event为"server_response",数据为"TEST",代码如下,当中socketio是flask_socketio模块的SocketIO对象,它提供了装饰器方法 on将自定义的client_event和管理函数test_client_event注册到sockerio服务器的handlers中。

当收到到 client_event 消息时,会通过sockerio/server.py中的 _handle_eio_message()方式处理信息,对于socket.io的EVENT类型的音讯最终会经过_trigger_event()措施管理,该形式也正是从handlers中得到client_event对应的管理函数并调用之。

from flask_socketio import SocketIO, emit
socketio = SocketIO(...)

@socketio.on("client_event")
def test_client_event(msg):
    emit("server_response", {"data": msg["data"].upper()})

(5)on_error:这些目的在遭遇错误时调用,有三个参数,第1个是此类自己,第贰个是十一分对象。

服务端发送音讯到顾客端

服务端发送新闻通过 flask_socketio提供的emit方法实现,如前生机勃勃节剖析的,最后依然通过的engine.io包装成engine.io的新闻格式后发生。

42["server_response",{"data":"TEST"}]

(6)on_close:在遭受一而再关闭的动静时调用,参数独有一个,正是此类自身。

闭馆连接

客户端要再接再厉关闭连接,在JS中调用 socket.close() 就可以,此时出殡和安葬的多少包为 41,个中4意味着的是engine.io的新闻类型message,而数据1则是指的socket.io的音讯类型disconnect,关闭流程见上活龙活现章的表达。

(7)on_cont_message:那几个目的在接收到接二连三帧数据时被调用,有七个参数,分别是:类自身,从服务器接受的字符串(utf-8),一连标识。

6 总结

正文示例中,为了有助于深入分析,只用了暗许的namespace和room,而在骨子里项目中得以依赖业务须要选用namespace,room等高级天性。

nginx+uwsgi行使socket.io时,当用到websocket时,注意nginx的晚点配置proxy_read_timeout和uwsgi的websocket超时配置websocket-ping-freq和websockets-pong-tolerance,配置不当会促成socke.io因为websocket的ping/pong超时而持续重连。

(8)on_data:当从服务器收到到消息时被调用,有五个参数,分别是:该类本人,接收到的字符串(utf-8),数据类型,延续标识。

参谋资料

  • https://tools.ietf.org/html/rfc6455
  • https://www.nginx.com/blog/websocket-nginx/
  • https://security.stackexchange.com/questions/36930/how-does-websocket-frame-masking-protect-against-cache-poisoning
  • https://github.com/suexcxine/blog/blob/master/source/_posts/websocket.md
  • https://github.com/abbshr/abbshr.github.io/issues/47
  • https://socket.io/docs/logging-and-debugging/
  • http://uwsgi-docs.readthedocs.io/en/latest/WebSockets.html
  • https://flask-socketio.readthedocs.io/en/latest/

(9)keep_running:三个二进制的标识位,假如为True,那个app的主循环将随处运行,暗中同意值为True。

(10)get_mask_key:用于爆发一个掩码。

(11)subprotocols:意气风发组可用的子公约,默以为空。

 

长连接首要措施:ws.run_forever(ping_interval=60,ping_timeout=5)

 假如持续开关闭websocket连接,会平素不通下去。别的那几个函数带七个参数,假使传的话,运转心跳包发送。

 

ping_interval:自动发送“ping”命令,每一种钦赐的光阴(秒),借使设置为0,则不会活动发送。

ping_timeout:若无接到pong新闻,则为超时(秒)。

ws.run_forever(ping_interval=60,ping_timeout=5)

#ping_interval心跳发送间隔时间

#ping_timeout 设置,发送ping到收到pong的超时时间

 

大家看源代码,会意识这么生机勃勃断代码:

ping的过期时间,要超过ping距离时间

 

        if not ping_timeout or ping_timeout <= 0:
            ping_timeout = None
        if ping_timeout and ping_interval and ping_interval <= ping_timeout:
            raise WebSocketException("Ensure ping_interval > ping_timeout")

 

 

 

 

长连接:

示例1:

 

import websocket
try:
    import thread
except ImportError:
    import _thread as thread
import time

def on_message(ws, message):
    print(message)

def on_error(ws, error):
    print(error)

def on_close(ws):
    print("### closed ###")


def on_open(ws):
    def run(*args):
        ws.send("hello1")
        time.sleep(1)
        ws.close()
    thread.start_new_thread(run,())

if __name__ == "__main__":
    websocket.enableTrace(True)
    ws = websocket.WebSocketApp("ws://echo.websocket.org/",
                              on_message = on_message,
                              on_error = on_error,
                              on_close = on_close)
    ws.on_open = on_open
    ws.run_forever(ping_interval=60,ping_timeout=5)

 

示例2:

import websocket
from threading import Thread
import time
import sys


class MyApp(websocket.WebSocketApp):
    def on_message(self, message):
        print(message)

    def on_error(self, error):
        print(error)

    def on_close(self):
        print("### closed ###")

    def on_open(self):
        def run(*args):
            for i in range(3):
                # send the message, then wait
                # so thread doesn't exit and socket
                # isn't closed
                self.send("Hello %d" % i)
                time.sleep(1)

            time.sleep(1)
            self.close()
            print("Thread terminating...")

        Thread(target=run).start()


if __name__ == "__main__":
    websocket.enableTrace(True)
    if len(sys.argv) < 2:
        host = "ws://echo.websocket.org/"
    else:
        host = sys.argv[1]
    ws = MyApp(host)
    ws.run_forever()

 

 

短连接:

from websocket import create_connection
ws = create_connection("ws://echo.websocket.org/")
print("Sending 'Hello, World'...")
ws.send("Hello, World")
print("Sent")
print("Receiving...")
result =  ws.recv()
print("Received '%s'" % result)
ws.close()

 

——

本文由mg游戏平台手机版发布于编程知识,转载请注明出处:长连接调用方式,它底层基于engine.io

关键词: