zoukankan      html  css  js  c++  java
  • 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('
    
    ', 1)
    	header_list = header.split('
    ')
    	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
    " 
    		"Upgrade:websocket
    " 
    		"Connection: Upgrade
    " 
    		"Sec-WebSocket-Accept: %s
    " 
    		"WebSocket-Location: ws://127.0.0.1:8002
    
    "
      
      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

    <!DOCTYPE html>
    <html lang="en">
    <head>
    	<meta charset="UTF-8">
    	<title>Title</title>
    </head>
    <body>
        <input type="button" value="开始" onclick="startConnect();">
    
    <script>
        var ws = null;
        function startConnect() {
    		// 1. 内部会先发送随机字符串
    		// 2. 内部会校验加密字符串
    		ws = new WebSocket('ws://127.0.0.1:8002')
      }
    </script>
    </body>
    </html>
    

    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

    <!DOCTYPE html>
    <html lang="en">
    <head>
    	<meta charset="UTF-8">
    	<title>Title</title>
    </head>
    <body>
    	<h1>Web聊天室:<span id="tips"></span></h1>
    	<div class="form">
    		<input id="txt" type="text" placeholder="请输入文字">
    		<input id="btn" type="button" value="发送" onclick="sendMessage();">
    	</div>
    	<div id="content">
    
    	</div>
    
    	<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
    	<script>
    		var ws;
    
    		$(function () {
    			initWebSocket();
    		});
    
    		function initWebSocket() {
    			ws = new WebSocket("ws://127.0.0.1:8000/x1/");
    			ws.onopen = function(){
    				$('#tips').text('连接成功');
    			};
    			ws.onmessage = function (arg) {
    				var tag = document.createElement('div');
    				tag.innerHTML = arg.data;
    				$('#content').append(tag);
    			};
    			ws.onclose = function () {
    				ws.close();
    			}
    		}
    
    		function sendMessage() {
    			ws.send($('#txt').val());
    		}
    	</script>
    </body>
    </html>
    

    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"
    希望你眼眸有星辰,心中有山海,从此以梦为马,不负韶华
  • 相关阅读:
    中国石油大学天梯赛真题模拟第三场
    中国石油大学天梯赛真题模拟第一场
    PTA 水果忍者
    演练3-1:留言管理系统的制作
    知识点4-1:哪些应该放在动作方法中?
    Week5(10月11日):国庆后补课的复杂心情
    知识点3-5:使用强类型模板
    Week5(10月10日):国庆之后,让我们整装期待元旦吧
    知识点3-6:HTML辅助方法
    知识点3-4:给视图传递数据
  • 原文地址:https://www.cnblogs.com/daviddd/p/11957836.html
Copyright © 2011-2022 走看看