分享web开发知识

注册/登录|最近发布|今日推荐

主页 IT知识网页技术软件开发前端开发代码编程运营维护技术分享教程案例
当前位置:首页 > 教程案例

websocket

发布时间:2023-09-06 02:13责任编辑:董明明关键词:websocket

WebSocket、HTTP 与 TCP

HTTP、WebSocket 等应用层协议,都是基于 TCP 协议来传输数据的。我们可以把这些高级协议理解成对 TCP 的封装。
既然大家都使用 TCP 协议,那么大家的连接和断开,都要遵循 TCP 协议中的三次握手和四次挥手 ,只是在连接之后发送的内容不同,或者是断开的时间不同.
我们知道http是基于tcp实现的,浏览器可以看做是客户端socket,这个socket在开发的时候遵循http规范,数据在发送并接收成功的时候就断开socket了,那么如果我们的socket一直连接不断开,并且发送数据的时候都把数据进行加密,这样是不是也是可以的呢?这种想法就可以引出websocket了,值得一提的是,websocket的兼容性不是所有版本的浏览器都支持(取决于是否能够new WebSocket), 但是随着技术的发展,浏览器对websocket的支持肯定会越来越好。
对于 WebSocket 来说,它必须依赖 HTTP 协议进行一次握手 ,握手成功后,数据就直接从 TCP 通道传输,与 HTTP 无关了。
Websocket和socket不同,Websocket工作在应用层,而socket是基于门面模式的一种封装,可以让程序员简便地写网络通信程序

flask实现websocket

安装gevent-websocket模块

from flask import Flask,render_template,requestfrom geventwebsocket.handler import WebSocketHandlerfrom gevent.pywsgi import WSGIServerimport jsonapp = Flask(__name__)USERS = { ???'1':{'name':'jack','count':0}, ???'2':{'name':'tuple','count':0}, ???'3':{'name':'lily','count':100},}@app.route('/index')def index(): ???return render_template('index.html',users=USERS)# http://127.0.0.1:5000/message ?既可以接受http,也可以接受websocketWEBSOCKET_LIST = []@app.route('/message')def message(): ???ws = request.environ.get('wsgi.websocket') ???if not ws: ???????return '您使用的是Http协议' ???WEBSOCKET_LIST.append(ws) ???while True: ???????# 当关闭webscoekt的时候cid 得到None ???????cid = ws.receive() ???????if not cid: ???????????WEBSOCKET_LIST.remove(ws) ???????????ws.close() ???????????break ???????old_count = USERS[cid]['count'] ???????new_count = old_count + 1 ???????USERS[cid]['count'] = new_count ???????for client in WEBSOCKET_LIST: ???????????client.send(json.dumps({'cid':cid,'count':new_count})) ???return ''if __name__ == '__main__': ???# 当请求是websocket的时候,那么就用WebSocketHandler进行处理 ???http_server = WSGIServer(('0.0.0.0', 5000), app, handler_class=WebSocketHandler) ???http_server.serve_forever()

index.html

<!DOCTYPE html><html lang="zh-CN"><head> ???<meta charset="UTF-8"> ???<title>Title</title> ???<meta name="viewport" content="width=device-width, initial-scale=1"></head><body> ???<h1>丑男投票系统</h1> ???<ul> ???????{% for k,v in users.items() %} ???????????<li onclick="vote({{k}})" id="id_{{k}}">{{v.name}}<span>{{v.count}}</span></li> ???????{% endfor %} ???</ul> ???<script src="{{ url_for('static',filename='jquery-3.3.1.min.js')}}"></script> ???<script> ???????var ws = new WebSocket('ws://127.0.0.1:5000/message'); ???????// 事件监听绑定回调函数,一旦有消息发送过来就会调用函数 ???????ws.onmessage = function (event) { ???????????/* 服务器端向客户端发送数据时,自动执行 */ ???????????// {'cid':cid,'count':new} ???????????var response = JSON.parse(event.data); ???????????$('#id_'+response.cid).find('span').text(response.count); ???????}; ???????function vote(cid) { ???????????ws.send(cid) ???????} ???</script></body></html>

webscoket 原理

websocket的握手需要借助http, new WebSocket的时候,服务端接收到的的请求头信息有有三个比较特殊:

Sec-WebSocket-Key:FUH2Nige4Npq/InFS0OoJQ==Upgrade:websocketConnection: Upgrade

表示这次请求是用于升级http为websocket,并把一个随机字符串发送过去,此时服务端应该拿到这个字符串进行加密然后发送过去,浏览器对这个字符串也加密,比较加密后的字符串和服务端发送过来的字符串是否一致,一致就表名握手成功,此时response header为

Connection:UpgradeSec-WebSocket-Accept:+lZCKceYfJsmNj6q0GYPa9r9LXE=Upgrade:websocket

状态码为:101 Switching Protocols,表示协议切换成功,此时,后面的通信就和http没有半毛钱关系了。

自定义实现websocket服务端

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_dictdef send_msg(conn, msg_bytes): ???""" ???WebSocket服务端向客户端发送消息 ???:param conn: 客户端连接到服务器端的socket对象,即: conn,address = socket.accept() ???:param msg_bytes: 向客户端发送的字节 ???:return: ???""" ???import struct ???token = b"\x81" ???length = len(msg_bytes) ???if length < 126: ???????token += struct.pack("B", length) ???elif length <= 0xFFFF: ???????token += struct.pack("!BH", 126, length) ???else: ???????token += struct.pack("!BQ", 127, length) ???msg = token + msg_bytes ???conn.send(msg)import base64import hashlibimport socketsock = 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)while 1: ???# 1. 等待用户连接 ???conn, address = sock.accept() ???# 2. 接收验证消息 ???msg = conn.recv(8096) ???msg_dict = get_headers(msg) ???# 3. 对数据加密, 其中魔法字符串'258EAFA5-E914-47DA-95CA-C5AB0DC85B11' 是固定的值 ???value = msg_dict['Sec-WebSocket-Key'] + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11' ???ac = base64.b64encode(hashlib.sha1(value.encode('utf-8')).digest()) ???# 4. 将加密后的结果返回给浏览器,发送的时候是用http通信的,回消息的时候也要封装成http格式 ???response_tpl = "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_tpl %(ac.decode('utf-8'),) ???conn.send(response.encode('utf-8')) ???# 5. 接受浏览器发送过来的加密数据,并进行解密,发送数据也需要进行加密,加密和解密规则可以参考官方 ???while True: ???????info = conn.recv(8096) ???????# 10111111 去掉第一位,和 01111111 相与就行 ???????payload_len = info[1] & 127 ???????if payload_len == 127: ???????????# 如果payload_len是127,那么需要往后面取8个字节作为头信息,头部总共10个字节 ???????????extend_payload_len = info[2:10] ???????????mask = info[10:14] ???????????decoded = info[14:] ???????elif payload_len == 126: ???????????# 如果payload_len是126,那么需要往后面取2个字节作为头信息,头部总共4个字节 ???????????extend_payload_len = info[2:4] ???????????mask = info[4:8] ???????????decoded = info[8:] ???????else: ???????????# 如果payload_len小于126,那么头部总共2个字节 ???????????extend_payload_len = None ???????????mask = info[2:6] ???????????decoded = info[6:] ???????bytes_list = bytearray() ???????# decoded 是真实的数据 ???????for i in range(len(decoded)): ???????????chunk = decoded[i] ^ mask[i % 4] ???????????bytes_list.append(chunk) ???????body = str(bytes_list, encoding='utf-8') ???????print('浏览器发送过来的数据',body) ???????# ???????# import ?time ???????# time.sleep(2) ???????send_msg(conn,body.encode('utf8'))

websocket

原文地址:https://www.cnblogs.com/longyunfeigu/p/9574067.html

知识推荐

我的编程学习网——分享web前端后端开发技术知识。 垃圾信息处理邮箱 tousu563@163.com 网站地图
icp备案号 闽ICP备2023006418号-8 不良信息举报平台 互联网安全管理备案 Copyright 2023 www.wodecom.cn All Rights Reserved