一、websocket

WebSocket协议是基于TCP的一种新的协议。WebSocket最初在HTML5规范中被引用为TCP连接,作为基于TCP的套接字API的占位符。它实现了浏览器与服务器全双工(full-duplex)通信。其本质是保持TCP连接,在浏览器和服务端通过Socket进行通信。

本文将使用Python编写Socket服务端,一步一步分析请求过程!!!

1. 启动服务端

import socket
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()
...
...
...

启动Socket服务器后,等待用户【连接】,然后进行收发数据。

2. 客户端连接

<script type="text/javascript">var socket = new WebSocket("ws://127.0.0.1:8002/xxoo");...
</script>

当客户端向服务端发送连接请求时,不仅连接还会发送【握手】信息,并等待服务端响应,至此连接才创建成功!

3. 建立连接【握手】

import 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)
# 获取客户端socket对象
conn, address = sock.accept()
# 获取客户端的【握手】信息
data = conn.recv(1024)
...
...
...
conn.send('响应【握手】信息')

请求和响应的【握手】信息需要遵循规则:

  • 从请求【握手】信息中提取 Sec-WebSocket-Key
  • 利用magic_string 和 Sec-WebSocket-Key 进行hmac1加密,再进行base64加密
  • 将加密结果响应给客户端

注:magic string为:258EAFA5-E914-47DA-95CA-C5AB0DC85B11

请求【握手】信息为:

GET /chatsocket HTTP/1.1
Host: 127.0.0.1:8002
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket
Origin: http://localhost:63342
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: mnwFxiOlctXFN/DeMt1Amg==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
...
...

提取Sec-WebSocket-Key值并加密:

import socket
import base64
import hashlibdef get_headers(data):"""将请求头格式化成字典:param data::return:"""header_dict = {}data = str(data, encoding='utf-8')for i in data.split('\r\n'):print(i)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_dictsock = 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()
data = conn.recv(1024)
headers = get_headers(data) # 提取请求头信息
# 对请求头中的sec-websocket-key进行加密
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://%s%s\r\n\r\n"
magic_string = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
value = headers['Sec-WebSocket-Key'] + magic_string
ac = base64.b64encode(hashlib.sha1(value.encode('utf-8')).digest())
response_str = response_tpl % (ac.decode('utf-8'), headers['Host'], headers['url'])
# 响应【握手】信息
conn.send(bytes(response_str, encoding='utf-8'))
...
...
...

View Code

4.客户端和服务端收发数据

客户端和服务端传输数据时,需要对数据进行【封包】和【解包】。客户端的JavaScript类库已经封装【封包】和【解包】过程,但Socket服务端需要手动实现。

第一步:获取客户端发送的数据【解包】

info = conn.recv(8096)payload_len = info[1] & 127if 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 = Nonemask = 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')print(body)

基于Python实现解包过程(未实现长内容)

解包详细过程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len |    Extended payload length    |
|I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
|N|V|V|V|       |S|             |   (if payload len==126/127)   |
| |1|2|3|       |K|             |                               |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
|     Extended payload length continued, if payload len == 127  |
+ - - - - - - - - - - - - - - - +-------------------------------+
|                               |Masking-key, if MASK set to 1  |
+-------------------------------+-------------------------------+
| Masking-key (continued)       |          Payload Data         |
+-------------------------------- - - - - - - - - - - - - - - - +
:                     Payload Data continued ...                :
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
|                     Payload Data continued ...                |
+---------------------------------------------------------------+

第二步:向客户端发送数据【封包】

def send_msg(conn, msg_bytes):"""WebSocket服务端向客户端发送消息:param conn: 客户端连接到服务器端的socket对象,即: conn,address = socket.accept():param msg_bytes: 向客户端发送的字节:return: """import structtoken = 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_bytesconn.send(msg)return True

5. 基于Python实现简单示例

a. 基于Python socket实现的WebSocket服务端:

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import socket
import base64
import hashlibdef 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 structtoken = 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_bytesconn.send(msg)return Truedef run():sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)sock.bind(('127.0.0.1', 8003))sock.listen(5)conn, address = sock.accept()data = conn.recv(1024)headers = get_headers(data)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://%s%s\r\n\r\n"value = headers['Sec-WebSocket-Key'] + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'ac = base64.b64encode(hashlib.sha1(value.encode('utf-8')).digest())response_str = response_tpl % (ac.decode('utf-8'), headers['Host'], headers['url'])conn.send(bytes(response_str, encoding='utf-8'))while True:try:info = conn.recv(8096)except Exception as e:info = Noneif not info:breakpayload_len = info[1] & 127if 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 = Nonemask = 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')send_msg(conn,body.encode('utf-8'))sock.close()if __name__ == '__main__':run()

View Code

b. 利用JavaScript类库实现客户端

<!DOCTYPE html>
<html>
<head lang="en"><meta charset="UTF-8"><title></title>
</head>
<body><div><input type="text" id="txt"/><input type="button" id="btn" value="提交" οnclick="sendMsg();"/><input type="button" id="close" value="关闭连接" οnclick="closeConn();"/></div><div id="content"></div><script type="text/javascript">var socket = new WebSocket("ws://127.0.0.1:8003/chatsocket");socket.onopen = function () {/* 与服务器端连接成功后,自动执行 */var newTag = document.createElement('div');newTag.innerHTML = "【连接成功】";document.getElementById('content').appendChild(newTag);};socket.onmessage = function (event) {/* 服务器端向客户端发送数据时,自动执行 */var response = event.data;var newTag = document.createElement('div');newTag.innerHTML = response;document.getElementById('content').appendChild(newTag);};socket.onclose = function (event) {/* 服务器端主动断开连接时,自动执行 */var newTag = document.createElement('div');newTag.innerHTML = "【关闭连接】";document.getElementById('content').appendChild(newTag);};function sendMsg() {var txt = document.getElementById('txt');socket.send(txt.value);txt.value = "";}function closeConn() {socket.close();var newTag = document.createElement('div');newTag.innerHTML = "【关闭连接】";document.getElementById('content').appendChild(newTag);}</script>
</body>
</html>

View Code

6. 基于Tornado框架实现Web聊天室

Tornado是一个支持WebSocket的优秀框架,其内部原理正如1~5步骤描述,当然Tornado内部封装功能更加完整。

以下是基于Tornado实现的聊天室示例:

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import uuid
import json
import tornado.ioloop
import tornado.web
import tornado.websocketclass IndexHandler(tornado.web.RequestHandler):def get(self):self.render('index.html')class ChatHandler(tornado.websocket.WebSocketHandler):# 用户存储当前聊天室用户waiters = set()# 用于存储历时消息messages = []def open(self):"""客户端连接成功时,自动执行:return: """ChatHandler.waiters.add(self)uid = str(uuid.uuid4())self.write_message(uid)for msg in ChatHandler.messages:content = self.render_string('message.html', **msg)self.write_message(content)def on_message(self, message):"""客户端连发送消息时,自动执行:param message: :return: """msg = json.loads(message)ChatHandler.messages.append(message)for client in ChatHandler.waiters:content = client.render_string('message.html', **msg)client.write_message(content)def on_close(self):"""客户端关闭连接时,,自动执行:return: """ChatHandler.waiters.remove(self)def run():settings = {'template_path': 'templates','static_path': 'static',}application = tornado.web.Application([(r"/", IndexHandler),(r"/chat", ChatHandler),], **settings)application.listen(8888)tornado.ioloop.IOLoop.instance().start()if __name__ == "__main__":run()

app.py

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Python聊天室</title>
</head>
<body><div><input type="text" id="txt"/><input type="button" id="btn" value="提交" οnclick="sendMsg();"/><input type="button" id="close" value="关闭连接" οnclick="closeConn();"/></div><div id="container" style="border: 1px solid #dddddd;margin: 20px;min-height: 500px;"></div><script src="/static/jquery-2.1.4.min.js"></script><script type="text/javascript">$(function () {wsUpdater.start();});var wsUpdater = {socket: null,uid: null,start: function() {var url = "ws://127.0.0.1:8888/chat";wsUpdater.socket = new WebSocket(url);wsUpdater.socket.onmessage = function(event) {console.log(event);if(wsUpdater.uid){wsUpdater.showMessage(event.data);}else{wsUpdater.uid = event.data;}}},showMessage: function(content) {$('#container').append(content);}};function sendMsg() {var msg = {uid: wsUpdater.uid,message: $("#txt").val()};wsUpdater.socket.send(JSON.stringify(msg));}</script></body>
</html>

index.html

示例源码下载

转载于:https://www.cnblogs.com/mengqingjian/p/8530994.html

python之websocket相关推荐

  1. 用python实现websocket请求遇到的问题及解决方法。

    用python实现websocket请求遇到的问题及解决方法. 参考文章: (1)用python实现websocket请求遇到的问题及解决方法. (2)https://www.cnblogs.com/ ...

  2. Python进行websocket接口测试

    Python进行websocket接口测试 Nikon937 Python进行websocket接口测试 - 简书 我们在做接口测试时,除了常见的http接口,还有一种比较多见,就是socket接口, ...

  3. python实现websocket服务端

    2019独角兽企业重金招聘Python工程师标准>>> ws.py是服务端,文件内容如下 #!/usr/bin/env python import socket import bas ...

  4. python requests发送websocket_Pywss - 用python实现WebSocket服务端

    一种类似Flask开发的WebSocket-Server服务端框架,适用python3.X 1.安装模块Pywss pip install pywss 2.搭建简易服务器 2.1 服务端代码 代码简介 ...

  5. Python之WebSocket协议

    一.WebSocket理论部分 1.websocket是什么 Websocket是html5提出的一个协议规范,参考rfc6455. websocket约定了一个通信的规范,通过一个握手的机制,客户端 ...

  6. Python——eventlet.websocket

    使用该模块可以方便地创建 websocket 服务器,要创建一个websocket服务器,只需要将一个句柄函数用装饰器 WebSocketWSGI 装饰即可,然后这个函数就可以当做一个WSGI应用: ...

  7. 网上测试了很多关于PYTHON的WEBSOCKET样例,下面这个才成功了

    这是最底层的, 嘿嘿,我 还是习惯搞个框架来实现急需要的功能... 这个东东玩得很有意思的.. 服务器端的代码: import simplejson import socket import sys ...

  8. python websocket模块_python websocket学习使用

    前言 今天看了一些资料,记录一下心得. websocket是html5引入的一个新特性,传统的web应用是通过http协议来提供支持,如果要实时同步传输数据,需要轮询,效率低下 websocket是类 ...

  9. python处理pdf实例_Python实战-从菜鸟到大牛的进阶之路pdf_Python教程

    资源名称:Python实战-从菜鸟到大牛的进阶之路 pdf 第1章 Python 处理 cassandra 升级后的回滚脚本 第 2 章 多套方案来提高 python web 框架的并发处理能力 第 ...

最新文章

  1. python中文注释报错
  2. pptpd免radius限速、限连接+自由定制功能脚本
  3. 异步请求时有时会让js不起作用,那么重新加载js
  4. ibm v5000增加磁盘_存储扩容首选 IBM Storwize V5000促销
  5. SharePoint 向多行文本类型字段插入特殊类型链接
  6. 学习3D游戏开发进阶之路
  7. Android高效开发:
  8. python计数器Count
  9. Ignite(四)-遇到的问题
  10. C语言随机产生26个大写字母
  11. 颈椎病的康复锻炼方法
  12. 油管YouTube直播点赞订阅提示PR字幕Mogrt动态图形模板
  13. macOS Monterey 如何设置PPT演讲者模式实现电脑看备注投影看播放内容
  14. 下载visual studio 2022 Professional 离线包
  15. pygame笔记(更新中
  16. 安全狗“老用户推荐新用户”有奖活动进行中 最高IPhone 4S手机
  17. oracle- 同义词
  18. HTML期末学生大作业-在线电影网站html+css+javascript
  19. Hadoop百度百科
  20. 图漾相机-虚拟机Ubuntu连接相机

热门文章

  1. Android事件分发小结
  2. 国外一些知名ASP.Net开源CMS系统
  3. 一个html数据编辑模板
  4. 用python写三角形_python写个三角形的问题
  5. RocketMQ的安装与启动
  6. 如何查看电脑CPU实时功耗
  7. itext 将pdf页面转成png_如何将PDF页面从横向改成纵向?
  8. PG修改表owner提示must be member of role “pg13“
  9. 抽象类java启动线程_java 线程复习笔记
  10. java语音jvm_java环境中基于jvm的两大语言:scala,groovy