你真的会websocket吗

摘要:
WebsocketWebSocket协议是基于TCP的一种新的网络协议。WebSocket通信协议于2011年被IETF定为标准RFC6455,并被RFC7936所补充规范。这个类提供了和已连接的客户端通信的WebSocket事件和方法的钩子。当一个新的WebSocket连接打开时,open方法被调用,而on_message和on_close方法分别在连接接收到新的消息和客户端关闭时被调用。')defon_message:self.write_message正如你在我们的EchoHandler实现中所看到的,open方法只是使用WebSocketHandler基类提供的write_message方法向客户端发送字符串"connected!
Websocket
WebSocket协议是基于TCP的一种新的网络协议。它实现了浏览器与服务器全双工(full-duplex)通信——允许服务器主动发送信息给客户端。
WebSocket通信协议于2011年被IETF定为标准RFC 6455,并被RFC7936所补充规范。
WebSocket协议支持(在受控环境中运行不受信任的代码的)客户端与(选择加入该代码的通信的)远程主机之间进行全双工通信。用于此的安全模型是Web浏览器常用的基于原始的安全模式。 协议包括一个开放的握手以及随后的TCP层上的消息帧。 该技术的目标是为基于浏览器的、需要和服务器进行双向通信的(服务器不能依赖于打开多个HTTP连接(例如,使用XMLHttpRequest或<iframe>和长轮询))应用程序提供一种通信机制。
这个协议目前仍是草案,只有最新的一些浏览器可以支持它。但是,它的好处是显而易见的,随着支持它的浏览器越来越多,我们将看到它越来越流行。(和以往的Web开发一样,必须谨慎地坚持依赖可用的新功能并能在必要时回滚到旧技术的务实策略。)
Django用法

在1.9版本之后,Django实现了对Channels的支持,他所使用的是WebSocket通信,解决了实时通信的问题,而且在使用WebSocket进行通信的同时依旧能够支持HTTP通信。

1.1目录结构

在此结构中必须有硬性要求,具体如下:

新的目录如下:
|--channels_example
|    |--channels_example
|        |-- __init__.py
|        |--settings.py
|        |--urls.py
|        |--wsgi.py
|        |-- routing.py   #必须
|        |-- consumer.py  #必须
|        |--asgi.py
|    |-- manage.py

1.2配置settings.py文件

1.2.1将其添加到APP列表里

INSTALLED_APPS =[
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'channels',
]

1.2.2然后,添加新的参数CHANNEL_LAYERS,如下:

CHANNEL_LAYERS ={
    "default": {
        "BACKEND": "asgiref.inmemory.ChannelLayer",
        "ROUTING": "channels_example.routing.channel_routing",
    },
}

需要注意的是 ROUTING 参数,他是用来指定WebSocket表单的位置,当有WebSocket请求访问时,就会根据这个路径找到相应表单,调用相应的函数进行处理。
channels_example.routing 就是我们刚才建好的routing,py文件,里面的channel_routing我们下面会进行填充。

1.3填写路由映射地址

from channels.routing importroute
importconsumers
channel_routing =[
    route('websocket.connect', consumers.ws_connect),        
    route('websocket.disconnect', consumers.ws_disconnect),        
    #route('websocket.receive', consumers.ws_message),        
    route('websocket.receive', consumers.ws_message_uuid),        
]

1.4路由映射到相对应的函数

from django.http importHttpResponse
from channels.handler importAsgiHandler
#message.reply_channel    一个客户端通道的对象
#message.reply_channel.send(chunk)  用来唯一返回这个客户端
#一个管道大概会持续30s
defws_connect(message):
    auth =True
    if notauth:
        reply = json.dumps({'error': error})
        message.reply_channel.send({'text': reply, 'close': True})
    else:
        reply = "{}"
        message.reply_channel.send({'text': reply})
        print(">>> %s connected" %str(message))
defws_disconnect(message):
    print("<<< %s disconnected" %str(message))
    #with message_queue.mutex:
    #message_queue.queue.clear()
    while notmessage_queue.empty():
        try:
            message_queue.get(False)
        exceptEmpty:
            continue
        message_queue.task_done()
defws_message_uuid(message):
    task =Task.create(message)
    iftask:
        message_queue.put(task)
tornado用法

1.1Tornado的WebSocket模块

Tornado在websocket模块中提供了一个WebSocketHandler类。这个类提供了和已连接的客户端通信的WebSocket事件和方法的钩子。当一个新的WebSocket连接打开时,open方法被调用,而on_messageon_close方法分别在连接接收到新的消息和客户端关闭时被调用。

此外,WebSocketHandler类还提供了write_message方法用于向客户端发送消息,close方法用于关闭连接。

classEchoHandler(tornado.websocket.WebSocketHandler):
    defopen(self):
        self.write_message('connected!')
    defon_message(self, message):
        self.write_message(message)

正如你在我们的EchoHandler实现中所看到的,open方法只是使用WebSocketHandler基类提供的write_message方法向客户端发送字符串"connected!"。每次处理程序从客户端接收到一个新的消息时调用on_message方法,我们的实现中将客户端提供的消息原样返回给客户端。这就是全部!让我们通过一个完整的例子看看实现这个协议是如何简单的吧。

WebSocketHandler.open()

当一个WebSocket连接建立后被调用。

WebSocketHandler.on_message(message)

当客户端发送消息message过来时被调用,注意此方法必须被重写。

WebSocketHandler.on_close()

当WebSocket连接关闭后被调用。

WebSocketHandler.write_message(message, binary=False)

向客户端发送消息messagea,message可以是字符串或字典(字典会被转为json字符串)。若binary为False,则message以utf8编码发送;二进制模式(binary=True)时,可发送任何字节码。

WebSocketHandler.close()

关闭WebSocket连接。

WebSocketHandler.check_origin(origin)

判断源origin,对于符合条件(返回判断结果为True)的请求源origin允许其连接,否则返回403。可以重写此方法来解决WebSocket的跨域请求(如始终return True)。

1.2实例--工作websocket实际应用

#coding=utf-8
importuuid
importos
from works.actions importwork
importhashlib
importjson
importQueue
from threading importThread
importnumpy as np
importcv2
importbase64
importjwt
importtornado.gen
from handlers.base_handler importBaseWebSocket
from config importMEDIA_ROOT
importtime
message_queue =Queue.PriorityQueue()
defwork_loop():
    whileTrue:
        task =message_queue.get()
        iuuid =task.uuid
        offset_top =task.offset_top
        image_data =task.image_data
        channel =task.channel
        zoom =task.zoom
        rType =task.rType
        responseType =task.responseType
        print(">>> len: %d | current offset: %d" %(message_queue.qsize(), offset_top))
        filename = str(uuid.uuid1()) + '.jpg'
        filepath =os.path.join(MEDIA_ROOT, filename)
        with open(filepath, 'wb') as f:
            f.write(image_data.decode("base64"))
        if zoom != 1.0:
            im =cv2.imread(filepath)
            if im isNone:
                continue
            osize = im.shape[1], im.shape[0]
            size = int(im.shape[1] * zoom), int(im.shape[0] *zoom)
            im =cv2.resize(im, size)
            cv2.imwrite(filepath, im)
        try:
            reply = work(filepath, use_crop=False, result=rType,responseType=responseType)
        exceptException as e:
            print("!!!!!! %s -> %s caused error" %(iuuid, filename))
            print(e)
            cmd = u"cp %s %s" % (filepath, os.path.join(MEDIA_ROOT, 'rb_' +filename))
            os.system(cmd.encode('utf-8'))
            continue
        if responseType == 'url':
            #rtn_url = 'http://101.236.17.104:3389/upload/' + 'rb_' + filename
            rtn_url = 'http://192.168.0.254:8000/upload/' + 'rb_' +filename
            reply = {'url': rtn_url, 'uuid': iuuid}
        reply['uuid'] =iuuid
        channel.write_message({'text': json.dumps(reply)})
        print '%s end time:' %channel, time.time()
classBrowserWebSocket(BaseWebSocket):
    '''浏览器websocket服务器'''
    defopen(self):
        '''新的WebSocket连接打开时被调用'''
        #message = {}
        #remote_ip = self.request.remote_ip
        #message['query_string']=self.get_argument('query_string')
        #message['remote_ip']=remote_ip
        #auth, error = verify_auth_token(message)
        auth =True
        error = 'error'
        if notauth:
            reply = json.dumps({'error': error})
            self.write_message({'text': reply, 'close': True})
        else:
            reply = "{}"
            self.write_message({'text': reply})
            print(">>> %s connected" %self.request.remote_ip)
    defon_message(self, message):
        '''连接收到新消息时被调用'''
        print '%s start time:'%self,time.time()
        task =Task.create(message,self)
        iftask:
            message_queue.put(task)
    @tornado.gen.coroutine
    defon_messages(self, message):
        '''连接收到新消息时被调用'''
        task =Task.create(message,self)
        iftask:
            message_queue.put(task)
    defon_close(self):
        '''客户端关闭时被调用'''
        print("<<< %s disconnected" %str(self.request.remote_ip))
        #with message_queue.mutex:
        #message_queue.queue.clear()
        while notmessage_queue.empty():
            try:
                message_queue.get(False)
            exceptQueue.Empty:
                continue
            message_queue.task_done()
    defcheck_origin(self, origin):
        '''允许WebSocket的跨域请求'''
        returnTrue
classTask(object):
    def __init__(self, uuid, offset_top, image_data, channel, zoom, rType, responseType, *args):
        self.uuid =uuid
        self.offset_top =int(float(offset_top))
        self.image_data =image_data
        self.channel =channel
        self.zoom =zoom
        self.rType =rType
        self.responseType =responseType
    @classmethod
    defcreate(clz, message,sel):
        #data = message.get('text')
        data =message
        try:
            params = json.loads(data[:150])
            image_data = data[150:]
            image_data = image_data.replace(" ", "+")
            params['image_data'] =image_data
            params['channel'] =sel
            #add Type
            if params.get('responseType') isNone:
                params['responseType'] = 'url'
            #request type
            if params.get('rType') isNone:
                params['rType'] = 'rl'
            task = Task(**params)
        exceptValueError as e:
            task =None
            print(">>>message data error!")
            print(e)
        returntask
    def __cmp__(self, other):
        returncmp(self.offset_top, other.offset_top)
defverify_auth_token(message):
    '''token 验证'''
    token = message.get('query_string')
    secret_key = 'aoiakai'
    try:
        payload = jwt.decode(token, secret_key, algorithms=['HS256'])
        if payload.get('ip') != message.get('remote_ip'):
            return False, 'ip mismatch'
    exceptjwt.ExpiredSignatureError as e:
        print(e)
        return False, 'token expired'
    exceptException as e:
        print(e)
        return False, 'enter correct token'
    return True, ''
work_thread = Thread(target=work_loop)
work_thread.daemon =True
work_thread.start()

免责声明:文章转载自《你真的会websocket吗》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Centos7内核升级CentOS系统中基于Apache+php+mysql的许愿墙网站的搭建下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

C#实现WebSocket协议客户端和服务器websocket sharp组件实例解析

看到这篇文章的题目,估计很多人都会问,这个组件是不是有些显的无聊了,说到web通信,很多人都会想到ASP.NET SignalR,或者Nodejs等等,实现web的网络实时通讯。有关于web实时通信的相关概念问题,在这里就不再做具体的介绍了,有兴趣的可以自行百度。   下面我们介绍一款WebSocket组件websocket-sharp的相关内容。 一....

springboot 使用webflux响应式开发教程(二)

本篇是对springboot 使用webflux响应式开发教程(一)的进一步学习。 分三个部分: 数据库操作webservicewebsocket 创建项目,artifactId = trading-service,groupId=io.spring.workshop。选择Reactive Web , Devtools, Thymeleaf , React...

webSocket+jwt实现方式

背景: 原项目是通过前端定时器获取消息,存在消息滞后、空刷服务器、浪费带宽和资源的问题,在springboot项目集成websocket可以实现实时点对点消息推送。 原项目是在header添加jwt令牌实现认证,由于websocket不支持在头部添加信息(或许是我打开的方式不对?),最终只能采用在url添加令牌参数实现认证,感觉不够优雅,后续再想办法重构改...

使用daphne部署django channles websocket 项目

前言:最近写了一个项目,有部分功能使用django channles websocket写的,使用的链接是wss:// 或者 ws:// ,到真实在uwsgi+nginx部署时,发现wss:// 或者 ws://不可用了,后来查了比较多时间,尝试过修改nginx配置文件,尝试过修改uwsgi配置文件,尝试过使用gunicorn部署,都没有解决此问题。最终发...

WebSocket传输图片

本文之初是想做简易直播系统,然而利用Html5+WebSocket还是有难度啊。本文只是打通了图像采集->服务器,再由服务器推送到客户端的过程。中间全程使用Image Base64字符串传输(效率不高) 以下是实现步骤 1.实现服务端:有socket和WebSocket两大块构成。socket作为主线程,用于处理图像源上传请求以及管理WebSocke...

WebSocket 实现长连接演示代码

服务端代码(node) const WebSocket = require('ws') const ws = new WebSocket.Server({ port: 8080 },()=>{ console.log('socket start') }) // 创建服务器 // 连接监听 clinet表示已经连接的客户端对象有多个 let...