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.WriteMessagedebug

最终发现原因:gorilla/websocket客户端writebuffer默认大小为defaultWriteBufferSize(4096)个字节。这就对上了。

解决方法

解决方法非常简单,就是设置writebuffer的大小

dialer := &websocket.Dialer{
      HandshakeTimeout: 3 * time.Second,
      WriteBufferSize:  20000,
}

再次进行测试时就ok了

posted @ 2020-12-28 19:55  虾敏四把刀  阅读(608)  评论(0编辑  收藏  举报