前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >语音评测之——websocket

语音评测之——websocket

作者头像
用户5521279
发布2020-03-03 14:45:24
3.3K0
发布2020-03-03 14:45:24
举报
文章被收录于专栏:搜狗测试搜狗测试

前言

前段时间小编收到一份测试任务要求对搜狗输入法的语音功能进行评测。评测任务主要拆分为评测语料的选取和整理,硬件的调研和采购,评测工具的开发以及评测的执行和结果整理。小编负责评测工具服务端的开发工作,主要使用了websocket的技术,此次与大家做一个简单的分享。

评测过程

语音的评测过程中由web端连接音响实现语音的播放功能,手机客户端接收语音并处理。这整个过程中web端和客户端的正确通信是保证整个过程顺畅高效的重要环节。

评测过程会逐条播放音频,每一条音频播放后web端需要知道每个手机客户端的状态(是否语音处理完毕,是否准备好接收下一条语音等等),以此来决定何时开始播放下一条音频;同理客户端也需要实时接收到web端的播放状态(何时开始,何时结束等等)。整个评测过程中web端和客户端需要频繁通信,所以我们需要选择一个合适的通讯技术以保证效率和质量。

通讯技术

在选择通信技术之前我们先了解几个基本概念:

短连接:每次http请求都会建立tcp连接,管理很容易,但是我们知道一次完整的tcp连接包含3次握手和4次挥手,如果频繁请求,将在tcp的建立和关闭操作上浪费较多时间和带宽。

长连接:http1.1之后默认建立长连接,由客户端发出请求,服务端接收请求,双方建立连接,在服务端没有返回之前保持连接,当客户端在发送请求时,它会使用同一个连接。这一直继续到客户端或者服务端认为会话已经结束,其中一方中断连接,更适用于追求实时性高的场景比如数据库的连接等等。

在本次评测过程中由于客户端与服务端通信频繁,且对实时性要求较高,开始便考虑使用长连接的方式。但是http协议一般只能由客户端发起,服务端无法直接进行推送,这就导致了如果服务端有持续的变化客户端想要获知就比较麻烦。而我们的评测过程中客户端会实时发送自己的状态信息给服务端,而服务端也会主要发送信息给客户端告知自己当前的状态,而这种真的全双工的协议便是websocket协议。

websocket:全双工通信协议,在第一次tcp链路建立之后,后续数据客户端和服务端都可以主动发送,不需要发送请求头,并且这个连接会持续存在直到客户端或者服务器端的某一方主动关闭连接,与HTTP长连接不同,WebSocket可以更灵活的控制连接关闭的时机,而不是HTTP协议的Keep-Alive一到,服务端立马就关闭。

websocket实现

websocket使用了自定义的协议,未加密的连接不再是http://,而是ws://,默认端口为80,加密的连接也不是https://,而是wss://,默认端口为443。比如:

代码语言:javascript
复制
ws://example.com/wsapi
wss://wsapi.example.com/

一个典型的Websocket握手请求如下:

客户端请求

代码语言:javascript
复制
GET / HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Host: example.com
Origin: http://example.com
Sec-WebSocket-Key: sN9cRrP/n9NdMgdcy2VJFQ==
Sec-WebSocket-Version: 13

服务器回应

代码语言:javascript
复制
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: fFBooB7FAkLlXgRSz0BT3v4hq5s=
Sec-WebSocket-Location: ws://example.com/
  • Connection 必须设置 Upgrade,表示客户端希望连接升级。
  • Upgrade 字段必须设置 Websocket,表示希望升级到 Websocket 协议。
  • Sec-WebSocket-Key 是随机的字符串,服务器端会用这些数据来构造出一个 SHA-1 的信息摘要。把 “Sec-WebSocket-Key” 加上一个特殊字符串 “258EAFA5-E914-47DA-95CA-C5AB0DC85B11”,然后计算 SHA-1 摘要,之后进行 BASE-64 编码,将结果做为 “Sec-WebSocket-Accept” 头的值,返回给客户端。如此操作,可以尽量避免普通 HTTP 请求被误认为 Websocket 协议。

websocket 协议本质上是一个基于 TCP 的协议。对于如何实现一个基本的websocket通信也非常容易,可以用任意的编程语言来实现,只要该语言能实现基本的Berkeley sockets。本次评测中小编使用python作为web端开发语言,而且python中对于websocket的实现封装了很多适用的库可以直接调用,为开发过程大大降低了难度。

客户端

websocket 使用 ws 或 wss 的统一资源标志符。而js就支持ws协议,因此使用websocket的时候,前端的使用一般都比较规范。

代码语言:javascript
复制
var s = new WebSocket("ws://%s/<id>");//创建一个websocket连接
s.onopen = function() {}
s.onmessage = function(e) {}//监听消息
s.onerror = function(e) {}//监听错误
s.onclose = function(e) {}//关闭连接
s.send(value);//发送消息

服务端

python中常用的通信框架为flask,该框架中以gevent-websocket库为例,以下为服务端部分实现代码,代码简单实现了服务端接收到客户端发送的消息(message = wsock.receive())并将消息同样发送给客户端(wsock.send(message)):

代码语言:javascript
复制
from flask import Flask
from gevent-websocket import WebSocketError
from gevent.pywsgi import WSGIServer
from gevent-websocket.handler import WebSocketHandler
 
app = Flask(__name__)
users = dict()
@app.route('/websocket/<uid>')
def handle_websocket(id):
    wsock = request.environ.get('wsgi.websocket')
    users[id] = wsock
 
    if not wsock:
        abort(400, 'Expected WebSocket request.')
 
    while True:
        try:
            message = wsock.receive() # 接收客户端发来的信息
        except WebSocketError:
            break
 
        if message:
            for uid in users:
                try:
                    users[uid].send(message) # 给客户端推送信息
                except WebSocketError:
                    print u'用户已断开连接'
 
    # 如果有客户端断开,则删除这个断开的websocket
    users.pop(id)
 
if __name__ == '__main__':
    server = WSGIServer(("0.0.0.0", 8000), app, handler_class=WebSocketHandler)
    server.serve_forever()
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-02-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 搜狗测试 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 评测过程
  • 通讯技术
  • websocket实现
  • 客户端
  • 服务端
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档