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)
websocket加解密

 

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)
websocket握手和加解密

 

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
wav2pcm.py


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')
基于百度ai的Simnet的 实现短文本相似度

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('北京天气')
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>
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)
flask_app.py
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()
flask_ws.py

 

posted @ 2018-10-16 21:20  富0代  阅读(365)  评论(0编辑  收藏  举报
返回
顶部