websocket

1. websocket

```
基于http的一个协议。(握手环节发送和响应时,用http协议规定传递)

协议规定了浏览器和服务端创建连接之后,不断开,保持连接。相互之间可以基于连接进行收发消息。
websocket原理也了解过:
- 握手环节
- 加密解密环节
```

2. 原理

- 客户端向服务端发送随机字符串,在http的请求头中 Sec-WebSocket-Key

- 服务端接受到到随机字符串

- 随机字符串 + 魔法字符串
- sha1加密
- base64加密
- 放在响应头中给用户返回 Sec-WebSocket-Accept

- 客户端浏览器会进行校验,校验不通过:服务端不支持websocket协议。

- 客户端和服务端进行相互收发消息,数据加密和解密。

- 解密细节:

- 获取第二个字节的后7位,payload len

- 判断payload len的值

- = 127

2字节 + 8字节 + 4字节 + 数据

- = 126

2字节 + 2字节 + 4字节 + 数据

- <= 125

2字节 + 4字节 + 数据

- 使用masking key 对数据进行解密

3. websocket多对多,hello.py

import socket
import hashlib
import base64

def get_headers(data):
"""
将请求头格式化成字典
:param data:
:return:
"""
header_dict = {}
data = str(data, encoding='utf-8')
header, body = data.split('\r\n\r\n', 1)
header_list = header.split('\r\n')
for i in range(0, len(header_list)):
if i == 0:
if len(header_list[i].split(' ')) == 3:
header_dict['method'], header_dict['url'], header_dict['protocol'] = header_list[i].split(' ')
else:
k, v = header_list[i].split(':', 1)
header_dict[k] = v.strip()
return header_dict

def get_data(info):
payload_len = info[1] & 127
if payload_len == 126:
extend_payload_len = info[2:4]
mask = info[4:8]
decoded = info[8:]
elif payload_len == 127:
extend_payload_len = info[2:10]
mask = info[10:14]
decoded = info[14:]
else:
extend_payload_len = None
mask = info[2:6]
decoded = info[6:]

bytes_list = bytearray()
for i in range(len(decoded)):
chunk = decoded[i] ^ mask[i % 4]
bytes_list.append(chunk)
body = str(bytes_list, encoding='utf-8')
return body

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('127.0.0.1', 8002))
sock.listen(5)

等待用户连接

conn, address = sock.accept()
# 握手环节
header_dict = get_headers(conn.recv(1024))
magic_string = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
random_string = header_dict['Sec-WebSocket-Key']
value = random_string + magic_string
ac = base64.b64encode(hashlib.sha1(value.encode('utf-8')).digest())
response = "HTTP/1.1 101 Switching Protocols\r\n"
"Upgrade:websocket\r\n"
"Connection: Upgrade\r\n"
"Sec-WebSocket-Accept: %s\r\n"
"WebSocket-Location: ws://127.0.0.1:8002\r\n\r\n"

response = response %ac.decode('utf-8')
print(response)
conn.send(response.encode('utf-8'))

接受数据

while True:
data = conn.recv(1024)
msg = get_data(data)
print(msg)

world.html





Title




4. 框架应用

django和flask框架,内部基于wsgi做的socket,默认他俩都不支持websocket协议,
只支持http协议。

- flask中应用

pip3 install gevent-websocket

- django中应用

pip3 install channels

5. Django自己的websocket:channels.layers方式

pip3 install channels
webchat

settings.py

ASGI_APPLICATION = "wechat.routing.application"

CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels.layers.InMemoryChannelLayer",
}
}

routing.py

from channels.routing import ProtocolTypeRouter, URLRouter
from django.conf.urls import url
from app01 import consumers

application = ProtocolTypeRouter({
'websocket': URLRouter([
url(r'^x1/$', consumers.ChatConsumer),
])
})

routing.py >>>>>>>>>>>>>session 版

from channels.routing import ProtocolTypeRouter, URLRouter
from django.conf.urls import url
from channels.sessions import CookieMiddleware,SessionMiddlewareStack
from apps.web import consumers

application = ProtocolTypeRouter({
'websocket': SessionMiddlewareStack(URLRouter([
url(r'^deploy/online/(?P<task_id>\d+)/$', consumers.DeployConsumer),
]))
})

获取session的方式

class DeployConsumer(WebsocketConsumer):

def websocket_connect(self, message):
self.accept()

获取session

username = self.scope['session']['username']
print(f'username>>>>>>>>>{username}')

默认不会帮你保存session

self.scope['session']['x1'] = 123

保存session需要执行save方法

self.scope['session'].save()

urls.py

from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^index/', views.index),
]

views.py

from django.shortcuts import render
def index(request):
return render(request,'index.html')

consumers.py:根据用户浏览器上的操作自动触发响应的方法

第一种方法:自己应用底层代码

from channels.exceptions import StopConsumer
from channels.generic.websocket import WebsocketConsumer
from asgiref.sync import async_to_sync

class ChatConsumer(WebsocketConsumer):

项目启动,输入网址回车自动触发:websocket_connect

def websocket_connect(self, message):
""" websocket连接到来时,自动执行 """
print('有人来了')
async_to_sync(self.channel_layer.group_add)('22922192', self.channel_name)

self.accept()

websocket浏览器给发消息时,自动触发此方法

def websocket_receive(self, message):
""" websocket浏览器给发消息时,自动触发此方法 """
print('接收到消息', message)

async_to_sync(self.channel_layer.group_send)('22922192', {
'type': 'xxx.ooo',
'message': message['text']
})

def xxx_ooo(self, event):
message = event['message']
self.send(message)

def websocket_disconnect(self, message):
""" 断开连接 """
print('客户端主动断开连接了')
async_to_sync(self.channel_layer.group_discard)('22922192', self.channel_name)
raise StopConsumer()

第二种:自己定义成自己看的懂的方式

class NewChatConsumer(WebsocketConsumer):
def connect(self):
print('有人来了')
async_to_sync(self.channel_layer.group_add)('22922192', self.channel_name)
self.accept()

def receive(self, text_data=None, bytes_data=None):
print('接收到消息', text_data)

async_to_sync(self.channel_layer.group_send)('22922192', {
'type': 'xxx.ooo',
'message': text_data
})

def xxx_ooo(self, event):
message = event['message']
self.send(message)

def disconnect(self, code):
print('客户端主动断开连接了')
async_to_sync(self.channel_layer.group_discard)('22922192', self.channel_name)

templates/index.html





Title


Web聊天室:









6. websocket 单对单(consumers.py)

一对一收发消息:

from channels.exceptions import StopConsumer
from channels.generic.websocket import WebsocketConsumer

class ChatConsumer(WebsocketConsumer):
def websocket_connect(self, message):
""" websocket连接到来时,自动执行 """
print('有人来了')
self.accept()

def websocket_receive(self, message):
""" websocket浏览器给发消息时,自动触发此方法 """
print('接收到消息', message)

self.send(text_data='收到了')

self.close()

def websocket_disconnect(self, message):
print('客户端主动断开连接了')
raise StopConsumer()

settings.py

ASGI_APPLICATION = "wechat.routing.application"

posted @ 2019-11-29 15:04  阿浪阿浪  阅读(287)  评论(0)    收藏  举报