golang调用百度音转文websocket服务“invalid frame type”错误排查及解决
背景
本文旨在记录解决问题的办法及思路。
需求是识别视频中的话语转为文字,此服务是调用的百度的websocket服务,其要求是:
- 发送一个text类型的帧,用于登录。
- 后续发送binary类型的音频数据。
开发语言:Golang
websocket库:github.com/gorilla/websocket
demo
核心流程就四步:连接服务器,发送text类型的登录消息,写音频数据,接收识别内容
package baidu
import (
"fmt"
"github.com/gorilla/websocket"
"io/ioutil"
"os"
"time"
)
func demo() error {
dialer := &websocket.Dialer{
HandshakeTimeout: 3 * time.Second,
}
url := genUrl("testsn")
conn, _, err := dialer.Dial(url, nil)
if err != nil {
return err
}
//登录验证的消息
if err := conn.WriteJSON(newStartMsg("dummy")); err != nil {
return err
}
f, err := os.Open("/data/16k-0.pcm")
if err != nil {
return err
}
data, err := ioutil.ReadAll(f)
if err != nil {
return err
}
//从websocket中读
go func() {
for {
_, b, err := conn.ReadMessage()
if err != nil {
return
} else {
fmt.Printf("%s\n", b)
}
}
}()
total := int64(len(data))
var (
start int64
len int64 = 5120
end int64
)
//写入音频二进制文件
for start < (total) {
end = start + len
if end > total {
end = total
}
if err := conn.WriteMessage(websocket.BinaryMessage, data[start:end]); err != nil {
return err
}
start = end
time.Sleep(time.Millisecond * 160)
}
time.Sleep(time.Second * 3)
return nil
}
错误排查
确定方向
通过go test调用demo后,给出以下结果:
=== RUN TestDemo
{"err_msg":"invalid frame type","err_no":-3201,"log_id":1953719668,"type":"FIN_TEXT"}
其提示为“invalid frame type”,那么有可能是以下的情况:
- 我指定的frame type不对,但是通过检查,再三确认是Binary类型,此项排除。
- websocket库的实现有问题,但是在github上有13k的star,暂时排除
- 百度的服务有问题,百度官方虽然没有golang的sdk,但是有Python的,运行过Python的sdk后,是能得到预期的
既然如此,那就是自己的问题了?
抓包
既然提示“invalid frame type”那么就抓一下看看是不是真的有问题。打开wireshark,过滤条件为“websocket”。运行程序:
如图所示,第一个是登录的text帧,第二个是音频的binary,第三个为什么不是binary,而是个continuation?
这里简单说明一下帧类型:
- opcode=0x1 :表示发送的是文本类型
- opcode=0x2 :表示发送的是二进制类型
- opcode=0x0 :表示发送的延续帧,就是完整消息对应的数据帧还没接收完。
而FIN=1,表示数据已接收完;而FIN=0,表示数据未接收完。
看来问题就是出在continuation帧上,看一下binary、continuation帧的头
binary帧
continuation帧
从上面两图可看出binary帧长度4096+continuation帧长度1024,正好等于程序里写的5120。但是为什么会分帧呢?
debug
通过从conn.WriteMessage
debug
最终发现原因:gorilla/websocket客户端writebuffer默认大小为defaultWriteBufferSize
(4096)个字节。这就对上了。
解决方法
解决方法非常简单,就是设置writebuffer的大小
dialer := &websocket.Dialer{
HandshakeTimeout: 3 * time.Second,
WriteBufferSize: 20000,
}
再次进行测试时就ok了