即时通讯WebSocket 和Socket.IO

WebSocket

HTML5定义了WebSocket协议,能更好的节省服务器资源和带宽,而且可以更实时地进行通信。html

在2008年诞生,2011年成为国际标准。python

如今基本全部浏览器都已经支持了。程序员

WebSocket是一种在单个TCP链接上进行全双工通讯的协议。在WebSocket API中,浏览器和服务器只须要完成一次握手(不是指创建TCP链接的那个三次握手,是指在创建TCP链接后传输一次握手数据),二者之间就直接能够建立持久性的链接,并进行双向数据传输。web

 

 

Websocket使用ws或wss的统一资源标志符,相似于HTTPS,其中wss表示在TLS之上的Websocket。如:后端

ws://example.com/wsapi wss://secure.example.com/

Websocket使用和 HTTP 相同的 TCP 端口,能够绕过大多数防火墙的限制。默认状况下,Websocket协议使用80端口;运行在TLS之上时,默认使用443端口。api

 

握手协议

WebSocket 是独立的、建立在 TCP 上的协议。 报文浏览器

Websocket 经过 HTTP/1.1 协议的101状态码进行握手。服务器

为了建立Websocket链接,须要经过浏览器发出请求,以后服务器进行回应,这个过程一般称为“握手”(handshaking)。websocket

一个典型的Websocket握手请求以下:多线程

客户端请求

 
GET / HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Host: example.com
Origin: http://example.com
Sec-WebSocket-Key: sN9cRrP/n9NdMgdcy2VJFQ==
Sec-WebSocket-Version: 13

服务器回应

 
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: fFBooB7FAkLlXgRSz0BT3v4hq5s=
Sec-WebSocket-Location: ws://example.com/
  • Connection必须设置Upgrade,表示客户端但愿链接升级。
  • Upgrade字段必须设置Websocket,表示但愿升级到Websocket协议。
  • Sec-WebSocket-Key是随机的字符串,服务器端会用这些数据来构造出一个SHA-1的信息摘要。把“Sec-WebSocket-Key”加上一个特殊字符串“258EAFA5-E914-47DA-95CA-C5AB0DC85B11”,而后计算SHA-1摘要,以后进行BASE-64编码,将结果作为“Sec-WebSocket-Accept”头的值,返回给客户端。如此操做,能够尽可能避免普通HTTP请求被误认为Websocket协议。
  • Sec-WebSocket-Version 表示支持的Websocket版本。RFC6455要求使用的版本是13,以前草案的版本均应当弃用。
  • Origin字段是可选的,一般用来表示在浏览器中发起此Websocket链接所在的页面,相似于Referer。可是,与Referer不一样的是,Origin只包含了协议和主机名称。
  • 其余一些定义在HTTP协议中的字段,如Cookie等,也能够在Websocket中使用。

优势

  • 较少的控制开销。在链接建立后,服务器和客户端之间交换数据时,用于协议控制的数据包头部相对较小。在不包含扩展的状况下,对于服务器到客户端的内容,此头部大小只有2至10字节(和数据包长度有关);对于客户端到服务器的内容,此头部还须要加上额外的4字节的掩码。相对于HTTP请求每次都要携带完整的头部,此项开销显著减小了。
  • 更强的实时性。因为协议是全双工的,因此服务器能够随时主动给客户端下发数据。相对于HTTP请求须要等待客户端发起请求服务端才能响应,延迟明显更少;即便是和Comet等相似的长轮询比较,其也能在短期内更屡次地传递数据。
  • 保持链接状态。与HTTP不一样的是,Websocket须要先建立链接,这就使得其成为一种有状态的协议,以后通讯时能够省略部分状态信息。而HTTP请求可能须要在每一个请求都携带状态信息(如身份认证等)。 更好的二进制支持。Websocket定义了二进制帧,相对HTTP,能够更轻松地处理二进制内容。
  • 能够支持扩展。Websocket定义了扩展,用户能够扩展协议、实现部分自定义的子协议。如部分浏览器支持压缩等。
  • 更好的压缩效果。相对于HTTP压缩,Websocket在适当的扩展支持下,能够沿用以前内容的上下文,在传递相似的数据时,能够显著地提升压缩率。
  • 没有同源限制,客户端能够与任意服务器通讯。
  • 能够发送文本,也能够发送二进制数据。
 

 

 

Socket.IO

1 简介

Socket.IO 本是一个面向实时 web 应用的 JavaScript 库,如今已成为拥有众多语言支持的Web即时通信应用的框架。

Socket.IO 主要使用WebSocket协议。可是若是须要的话,Socket.io能够回退到几种其它方法,例如Adobe Flash Sockets,JSONP拉取,或是传统的AJAX拉取,而且在同时提供彻底相同的接口。尽管它能够被用做WebSocket的包装库,它仍是提供了许多其它功能,好比广播至多个套接字,存储与不一样客户有关的数据,和异步IO操做。

Socket.IO 不等价于 WebSocket,WebSocket只是Socket.IO实现即时通信的其中一种技术依赖,并且Socket.IO还在实现WebSocket协议时作了一些调整。

优势:

Socket.IO 会自动选择合适双向通讯协议,仅仅须要程序员对套接字的概念有所了解。

有Python库的实现,能够在Python实现的Web应用中去实现IM后台服务。

缺点:

Socket.io并非一个基本的、独立的、可以回退到其它实时协议的WebSocket库,它其实是一个依赖于其它实时传输协议的自定义实时传输协议的实现。该协议的协商部分使得支持标准WebSocket的客户端不能直接链接到Socket.io服务器,而且支持Socket.io的客户端也不能与非Socket.io框架的WebSocket或Comet服务器通讯。于是,Socket.io要求客户端与服务器端均须使用该框架。

2 Python服务器端开发

安装

 
 
pip install python-socketio
 

建立服务器

  • 方式1

    使用多进程多线程模式的WSGI服务器对接(如uWSGI、gunicorn)

     
       
      import socketio  
    
      # create a Socket.IO servers
      sio = socketio.Server()
    
      # 打包成WSGI应用,可使用WSGI服务器托管运行
      app = socketio.WSGIApp(sio)  # Flask  Django
     

    建立好app对象后,使用uWSGI、或gunicorn服务器运行此对象。

  • 方式2

    做为Flask、Django 应用中的一部分

     
       
      from wsgi import app  # a Flask, Django, etc. application
      import socketio
    
      # create a Socket.IO server
      sio = socketio.Server()
    
      app = socketio.WSGIApp(sio, app)
     

    建立好app对象后,使用uWSGI、或gunicorn服务器运行此对象。

  • 方式3

    使用协程的方式运行 (推荐)

     
       
      import eventlet
      eventlet.monkey_patch()
    
      import socketio
      import eventlet.wsgi
    
      sio = socketio.Server(async_mode='eventlet')  # 指明在evenlet模式下
      app = socketio.Middleware(sio)
      eventlet.wsgi.server(eventlet.listen(('', 8000)), app)
     
说明

由于服务器与客户端进行即时通讯时,会尽量的使用长链接,因此若服务器采用多进程或多线程方式运行,受限于服务器能建立的进程或线程数,可以支持的并发链接客户端不会很高,也就是服务器性能有限。采用协程方式运行服务器,能够提高即时通讯服务器的性能。

事件处理

不一样于HTTP服务的编写方式,SocketIO服务编写再也不以请求Request和响应Response来处理,而是对收发的数据以消息(message)来对待,收发的不一样类别的消息数据又以事件(event)来区分。

本来HTTP服务编写中处理请求、构造响应的视图处理函数在SocketIO服务中改成编写收发不一样事件的事件处理函数。

1)事件处理方法

编写事件处理方法,能够接收指定的事件消息数据,并在处理方法中对消息数据进行处理。

 
 
@sio.on('connect')
def on_connect(sid, environ):
    """
    与客户端创建好链接后被执行
    :param sid: string sid是socketio为当前链接客户端生成的识别id
    :param environ: dict 在链接握手时客户端发送的握手数据(HTTP报文解析以后的字典)
    """
    pass

@sio.on('disconnect')
def on_disconnect(sid):
    """
    与客户端断开链接后被执行
    :param sid: string sid是断开链接的客户端id
    """
    pass

# 以字符串的形式表示一个自定义事件,事件的定义由先后端约定
@sio.on('my custom event')  
def my_custom_event(sid, data):
    """
    自定义事件消息的处理方法
    :param sid: string sid是发送此事件消息的客户端id
    :param data: data是客户端发送的消息数据
    """
    pass
 
注意
  • connect 为特殊事件,当客户端链接后自动执行
  • disconnect 为特殊事件,当客户端断开链接后自动执行

  • connect、disconnect与自定义事件处理方法的函数传入参数不一样

2)发送事件消息

  • 群发

     
       
    sio.emit('my event', {'data': 'foobar'})
     
  • 给指定用户发送

     
       
    sio.emit('my event', {'data': 'foobar'}, room=user_sid)
     
  • 给一组用户发送

    SocketIO提供了房间(room)来为客户端分组

    • sio.enter_room(sid, room_name)

      将链接的客户端添加到一个room

       
           
        @sio.on('chat')
        def begin_chat(sid):
            sio.enter_room(sid, 'chat_users')
       

      注意:当客户端链接后,socketio会自动将客户端添加到以此客户端sid为名的room中

    • sio.leave_room(sid, room_name)

      将客户端从一个room中移除

       
           
        @sio.on('exit_chat')
        def exit_chat(sid):
            sio.leave_room(sid, 'chat_users')
       
    • sio.rooms(sid)

      查询sid客户端所在的全部房间

      给一组用户发送消息的示例

       
           
      @sio.on('my message')
      def message(sid, data):
        sio.emit('my reply', data, room='chat_users')
       

      也可在群组发消息时跳过指定客户端

       
           
      @sio.on('my message')
      def message(sid, data):
        sio.emit('my reply', data, room='chat_users', skip_sid=sid)
       
  • 使用send发送message事件消息

    对于'message'事件,可使用send方法

     
       
      sio.send({'data': 'foobar'})
      sio.send({'data': 'foobar'}, room=user_sid)

3 Python客户端

 
import socketio

sio = socketio.Client()

@sio.on('connect')
def on_connect():
    pass

@sio.on('event')
def on_event(data):
    pass

sio.connect('http://10.211.55.7:8000')
sio.wait()
相关文章
相关标签/搜索