python全栈开发day115、116-websocket、websocket原理、websocket加解密、简单问答机器人实现
1、websocket
1.websocket 与轮询 轮询: 不断向服务器发起询问,服务器还不断的回复 浪费带宽,浪费前后端资源 保证数据的实时性 长轮询: 1.客户端向服务器发起消息,服务端轮询,放在另外一个地方,客户端去另外一个地方去拿 2.服务端轮询,放在另外一个地方,直接推给客户端 释放客户端资源,服务压力不可避免,节省带宽资源 数据不能实时性 websocket:是一个新的协议 Socket-io 1.前后端hold住 2.建立长链接 彻底解决实时性 解决占用带宽的问题 解决资源 2.webscoket使用 from flask import Flask, request from geventwebsocket.websocket import WebSocket from gevent.pywsgi import WSGIServer from geventwebsocket.handler import WebSocketHandler app = Flask(__name__) @app.route("/ws") # WS://127.0.0.1:9527/ws def ws(): # request.environ["wsgi.websocket"] = <链接> user_socket = request.environ.get("wsgi.websocket") # type:WebSocket while 1: msg = user_socket.receive() print(msg) user_socket.send(msg) if __name__ == '__main__': http_serv = WSGIServer(("0.0.0.0", 9527), app, handler_class=WebSocketHandler) http_serv.serve_forever() # app.run("0.0.0.0", 9527, debug=True) 即时通讯(IM): 群聊: from flask import Flask, request,render_template from geventwebsocket.websocket import WebSocket from gevent.pywsgi import WSGIServer from geventwebsocket.handler import WebSocketHandler import json app = Flask(__name__) user_socket_dict = {} # {jinwangba:<geventwebsocket.websocket.WebSocket object at 0x000000000B35CE18>} @app.route("/") def index(): return render_template("index.html") @app.route("/ws/<nickname>") # WS://127.0.0.1:9527/ws def ws(nickname): user_socket = request.environ.get("wsgi.websocket") # type:WebSocket if user_socket: user_socket_dict[nickname]=user_socket print(len(user_socket_dict)) else: return render_template("index.html",message="请使用Websocket链接") while 1: msg = user_socket.receive() for user_nick_name,socket in user_socket_dict.items(): # type:WebSocket if user_socket != socket: try: socket.send(json.dumps({"sender":nickname,"msg":msg})) except: continue if __name__ == '__main__': http_serv = WSGIServer(("0.0.0.0",9527),app,handler_class=WebSocketHandler) http_serv.serve_forever() # app.run("0.0.0.0", 9527, debug=True) 单聊: from flask import Flask, request,render_template from geventwebsocket.websocket import WebSocket from gevent.pywsgi import WSGIServer from geventwebsocket.handler import WebSocketHandler import json app = Flask(__name__) user_socket_dict = {} # {jinwangba:<geventwebsocket.websocket.WebSocket object at 0x000000000B35CE18>} @app.route("/") def index(): return render_template("index.html") @app.route("/ws/<nickname>") # WS://127.0.0.1:9527/ws def ws(nickname): user_socket = request.environ.get("wsgi.websocket") # type:WebSocket if user_socket: user_socket_dict[nickname]=user_socket print(len(user_socket_dict),user_socket_dict) else: return render_template("index.html",message="请使用Websocket链接") while 1: msg = user_socket.receive() msg_dict = json.loads(msg) #{"to_user":jinwangba,msg:"DSB"} to_user = msg_dict.get("to_user") print("to_user:",to_user) to_user_socket = user_socket_dict.get(to_user) send_str = json.dumps({"sender":nickname,"msg":msg_dict.get("msg")}) to_user_socket.send(send_str) if __name__ == '__main__': http_serv = WSGIServer(("0.0.0.0",9527),app,handler_class=WebSocketHandler) http_serv.serve_forever() # app.run("0.0.0.0", 9527, debug=True)
2、websocket原理
import socket, base64, hashlib sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind(('127.0.0.1', 9527)) sock.listen(5) # 获取客户端socket对象 conn, address = sock.accept() # 获取客户端的【握手】信息 data = conn.recv(1024) print(data) def get_headers(data): header_dict = {} header_str = data.decode("utf8") for i in header_str.split("\r\n"): if str(i).startswith("Sec-WebSocket-Key"): header_dict["Sec-WebSocket-Key"] = i.split(":")[1].strip() return header_dict ws_key = get_headers(data).get("Sec-WebSocket-Key") # # magic string为:258EAFA5-E914-47DA-95CA-C5AB0DC85B11 magic_string = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11' socket_str = ws_key + magic_string socket_str_sha1 = hashlib.sha1(socket_str.encode("utf8")).digest() socket_str_base64 = base64.b64encode(socket_str_sha1) 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:9527\r\n\r\n" %(socket_str_base64.decode("utf8")) conn.send(response_tpl.encode("utf8")) while 1: msg = conn.recv(8096) print(msg) """ b'GET / HTTP/1.1\r\n Host: 127.0.0.1:9527\r\n User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:62.0) Gecko/20100101 Firefox/62.0\r\n Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2\r\n Accept-Encoding: gzip, deflate\r\n Sec-WebSocket-Version: 13\r\n Origin: http://localhost:63342\r\n Sec-WebSocket-Extensions: permessage-deflate\r\n Sec-WebSocket-Key: x/BjPHeWzOqqLxVuZq/bfw==\r\n Cookie: session=fe2f4896-0309-4801-b8cd-9a719b26fb8d\r\n Connection: keep-alive, Upgrade\r\n Pragma: no-cache\r\n Cache-Control: no-cache\r\n Upgrade: websocket\r\n\r\n' """ # # magic string为:258EAFA5-E914-47DA-95CA-C5AB0DC85B11 # magic_string = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11' # # # def get_headers(data): # header_dict = {} # header_str = data.decode("utf8") # for i in header_str.split("\r\n"): # if str(i).startswith("Sec-WebSocket-Key"): # header_dict["Sec-WebSocket-Key"] = i.split(":")[1].strip() # # return header_dict # # # def get_header(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 # # # 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://127.0.0.1:9527\r\n\r\n" # value = headers['Sec-WebSocket-Key'] + magic_string # print(value) # ac = base64.b64encode(hashlib.sha1(value.encode('utf-8')).digest()) # response_str = response_tpl % (ac.decode('utf-8')) # # 响应【握手】信息 # conn.send(response_str.encode("utf8")) # # while True: # msg = conn.recv(8096) # print(msg)
3、websocket加解密
上节回顾: websocket;握手 浏览器 - 链接 - 服务器 浏览器 - 发送 - 字符串(求情头) - Sec-Websocket-Key - 服务器 服务器 - 获取Sec-Websocket-Key的值+magic_string - sha1 - base64 - 拼接一个响应头 - Sec-WebSocket-Accept值就是 服务器 - 拼接好的响应头 - 浏览器 浏览器 - Sec-WebSocket-Accept 解密得到 Sec-Websocket-Key - 握手成功与否 今日内容: websocket 加解密 解密: b_str = b'\x81\xfe\x01G\xa0`~dE\xe5\xf6\x81\x18\xfd\x9b\xec;\x84\xc6\xfeF\xfc\xd4\x81-\xea\x96\xe4,\x84\xc6\xc9I\xe1\xed\x81\x14\xc9\x98\xca"\x8f\xc2\xe8D\xdb\xf4\x81\x04\xc9\x9a\xdc+\x84\xc6\xedE\xe8\xf8\x8b\x1c\xec\x99\xff*\x85\xc9\xfaG\xf6\xcc\x81\x1c\xea\x91\xd8,\x86\xd3\xc0H\xcf\xe4\x81-\xd1\x98\xe4\x05\x85\xd3\xfcD\xda\xdf\x80\x19\xeb\x99\xc3+\x84\xc7\xfbC\xe0\xfc\x83$\xd6\x9a\xda-\x85\xf3\xcfD\xd9\xf5\x8c\'\xc3\x9a\xdc-\x86\xf9\xecD\xda\xf0\x81&\xe5\x91\xd8,\x85\xc1\xc4E\xdf\xe9\x80\x19\xeb\x9b\xc7\x0b\x85\xc1\xfcH\xda\xd5\x80\x1a\xee\x9b\xc06\x88\xfe\xe1O\xdc\xf2\x83;\xf6\x96\xdb\x1d\x85\xfb\xecE\xd8\xe3\x80\x19\xeb\x98\xca*\x89\xff\xe3O\xdc\xf2\x82\x0c\xd2\x98\xee\x05\x84\xc7\xefD\xda\xf0\x8d9\xfb\x9a\xdc+\x84\xc7\xfbC\xe0\xfc\x8c\x0f\xfa\x9b\xca<\x85\xc2\xe4E\xdc\xde\x81<\xc3\x9b\xf4\x0c\x8f\xc2\xe8D\xdb\xdb\x81%\xe9\x9b\xe1(\x85\xc6\xf9I\xe1\xe9\x81\x1e\xd7\x91\xd8,\x86\xff\xc6E\xdc\xe6\x81\x1f\xf7\x9b\xc7\x0b\x84\xc7\xefF\xd0\xea\x8b\x1c\xec\x9a\xdc-\x85\xd0\xf8E\xc6\xfa\x8c\'\xca\x96\xeb\x12\x88\xe8\xe0O\xdc\xf2\x81\x1c\xf5\x9b\xf2\x1b\x85\xda\xd5D\xd9\xf7\x8b\x1c\xec\x9a\xdf\x05\x85\xdf\xfaE\xdf\xde\x8c\x10\xef\x9a\xdd+\x88\xc9\xcbD\xd9\xe1' c_str = b'\x81\xfe\x02d\xbe@<\x07' # 123 \x81 #[\x81,\x83,\xc8,\xbd,\xa6,\x9c,\xf9,\x8f,\x95] # 1000011 # 0111111 # 0000011 #-------- # 1111110 # 0111111 127 # 0111110 126 data_lenth = b_str[1] & 127 # print(data_lenth) # data_lenth == 127 之后的多少位为数据长度3-10字节代表数据长度 11-14为mask_key if data_lenth == 127: extend_payload_len = b_str[2:10] mask = b_str[10:14] data = b_str[14:] # data_lenth == 126 之后的多少位为数据长度3-4字节代表数据长度 65535 5-8为mask_key if data_lenth == 126: extend_payload_len = b_str[2:4] mask = b_str[4:8] data = b_str[8:] # data_lenth <= 125 当前的 data_lenth 就是数据的长度 125 3-6为mask_key if data_lenth <= 125: extend_payload_len = b_str[1] mask = b_str[2:6] data = b_str[6:] str_byte = bytearray() for i in range(len(data)): byte = data[i] ^ mask[i % 4] # 0123 str_byte.append(byte) print(str_byte.decode("utf8"))# 加密: import struct msg_bytes = "先帝创业未半而中道崩殂,今天下三分,益州疲弊,此诚危急存亡之秋也。然侍卫之臣不懈于内,忠志之士忘身于外者,盖追先帝之殊遇,欲报之于陛下也。诚宜开张圣听,以光先帝遗德,恢弘志士之气,不宜妄自菲薄,引喻失义,以塞忠谏之路也".encode("utf8") token = b"\x81" length = len(msg_bytes) if length < 126: token += struct.pack("B", length) elif length == 126: token += struct.pack("!BH", 126, length) else: token += struct.pack("!BQ", 127, length) msg = token + msg_bytes print(msg)
4、人工智能简易机器人
1.百度ai
2.基于百度ai实现了语音合成
3.基于百度ai实现了语音识别
from aip import AipSpeech import os from uuid import uuid4 from wav2pcm import wav_to_pcm APP_ID = '14452531' API_KEY = 'zdU6ldOh7CeQvFFzw9GWhwZr' SECRET_KEY = 'h5eVzR2G4EPFf78uzceC1zHL6Qj88RCY' client = AipSpeech(APP_ID, API_KEY, SECRET_KEY) # client.setConnectionTimeoutInMillis(5000) # 建立连接的超时时间(单位:毫秒) # client.setSocketTimeoutInMillis(10000) # 通过打开的连接传输数据的超时时间(单位:毫秒) # 语音合成 def new_synthesis(text): result = client.synthesis(text, 'zh', 1, { 'vol': 5, # 音量,取值0-15,默认为5中音量 'spd': 5, # 语速,取值0-9,默认为5中语速 'pit': 5, # 音调,取值0-9,默认为5中语调 'per': 4 # 发音人选择, 0为女声,1为男声,3为情感合成-度逍遥,4为情感合成-度丫丫,默认为普通女 }) audio_file_path = str(uuid4()) + ".mp3" # 识别正确返回语音二进制 错误则返回dict 参照下面错误码 if not isinstance(result, dict): with open(audio_file_path, 'wb') as f: f.write(result) return audio_file_path # 读取文件 def get_file_content(pcm_file_path): with open(pcm_file_path, 'rb') as fp: return fp.read() # 语音识别 def new_asr(filePath): # 将录音机文件wma转换成pcm pcm_file_path = wav_to_pcm(filePath) # 识别本地文件 res = client.asr(get_file_content(pcm_file_path), 'pcm', 16000, { 'dev_pid': 1536, }) try: text = res.get('result')[0] except Exception as e: text = "对不起,没有听清你说的啥,请再说一遍。" os.remove(pcm_file_path) return text if __name__ == '__main__': new_asr('audio.wma')
FFmpeg 转换PCM音频格式
# wav2pcm.py 文件内容 import os def wav_to_pcm(wav_file): # 假设 wav_file = "音频文件.wav" # wav_file.split(".") 得到["音频文件","wav"] 拿出第一个结果"音频文件" 与 ".pcm" 拼接 等到结果 "音频文件.pcm" pcm_file = "%s.pcm" % (wav_file.split(".")[0]) # 就是此前我们在cmd窗口中输入命令,这里面就是在让Python帮我们在cmd中执行命令 os.system("ffmpeg -y -i %s -acodec pcm_s16le -f s16le -ac 1 -ar 16000 %s" % (wav_file, pcm_file)) os.remove(wav_file) return pcm_file
4.基于百度ai NLP技术中的Simnet 实现 短文本相似度
# 基于百度ai的Simnet的 实现短文本相似度 from aip import AipNlp APP_ID = '14452531' API_KEY = 'zdU6ldOh7CeQvFFzw9GWhwZr' SECRET_KEY = 'h5eVzR2G4EPFf78uzceC1zHL6Qj88RCY' client = AipNlp(APP_ID, API_KEY, SECRET_KEY) def new_simnet(text1, text2): ret = client.simnet(text1, text2) return ret.get('score')
5.简单问答 + Tuling机器人
import requests def tuling_answer(text): to_tuling_url = "http://openapi.tuling123.com/openapi/api/v2" # 接口地址 json_data = { "reqType": 0, "perception": { "inputText": { "text": text }, }, "userInfo": { "apiKey": "93e5736f3b9e496bbb63a0f29afa4d4d", "userId": "test123" } } res = requests.post(to_tuling_url, json=json_data).json() answer = res.get("results")[0]['values']['text'] return answer if __name__ == '__main__': tuling_answer('北京天气')
6.web录音知道里面是干什么的,Audio标签 src音频地址 autoplay当src加载完成时自动播放 controls 显示或关闭播放器
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <audio src="" autoplay controls id="player"></audio> <button onclick="start_reco()">录制消息</button> <br> <button onclick="stop_reco()">发送语音消息</button> </body> <script src="/static/Recorder.js"></script> <script type="application/javascript"> var serv = "http://127.0.0.1:9527"; var ws_serv = "ws://127.0.0.1:9528/ws"; var get_music = serv + "/get_audio/"; var ws = new WebSocket(ws_serv); ws.onmessage = function (data) { console.log(data.data,111111111) document.getElementById("player").src = get_music + data.data }; var reco = null; var audio_context = new AudioContext(); navigator.getUserMedia = (navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia); navigator.getUserMedia({audio: true}, create_stream, function (err) { console.log(err) }); function create_stream(user_media) { var stream_input = audio_context.createMediaStreamSource(user_media); reco = new Recorder(stream_input); } function start_reco() { reco.record(); } function stop_reco() { reco.stop(); get_audio(); reco.clear(); } function get_audio() { reco.exportWAV(function (wav_file) { ws.send(wav_file); }) } </script> </html>
7.基于websocket传输语音
8.return send_file(file_name)
from flask import Flask, send_file, render_template from queue import Queue import os q = Queue() # type:Queue app = Flask(__name__) # type:Flask @app.route("/index") def index(): return render_template("index1.html") temp_file_name = [] @app.route('/get_audio/<filename>') def get_audio(filename): # 利用队列存储临时生成的文件名,下一次请求来的时候删除文件 if not q.empty(): temp = q.get() print(temp) os.remove(temp) q.put(filename) return send_file(filename) if __name__ == '__main__': app.run(host='0.0.0.0', port=9527)
from flask import Flask, request from geventwebsocket.websocket import WebSocket from gevent.pywsgi import WSGIServer from geventwebsocket.handler import WebSocketHandler from uuid import uuid4 from baidu_asr_and_synthesis import new_asr, new_synthesis from simple_questions_answers import question_answer app = Flask(__name__) # type:Flask @app.route('/ws') def ws(): user_websocket = request.environ.get('wsgi.websocket') # type:WebSocket if user_websocket: while 1: res = user_websocket.receive() # 保存接收音频文件 rev_file_name = ("%s" + ".wav") % str(uuid4()) with open(rev_file_name, 'wb') as f: f.write(res) print(rev_file_name) # 将接收的音频文件转换为文本 audio_to_text = new_asr(rev_file_name) print('audio_to_text',audio_to_text) # 根据问题文本回答问题 answer_text = question_answer(audio_to_text) print('answer_text',answer_text) # 将问题答案转成音频文件 answer_audio_file_path = new_synthesis(answer_text) print('answer_audio_file_path',answer_audio_file_path) # 将音频文件路径发给前端 user_websocket.send(answer_audio_file_path) if __name__ == '__main__': http_serv = WSGIServer(('0.0.0.0', 9528), app, handler_class=WebSocketHandler) http_serv.serve_forever()