某音乐软件协议中,建立Socket长连接进行PCM音频数据的传递。
其中,TCP包的格式为:
其二进制数据是按照Header中定义的Length传输的,初步处理代码如下:
1 | // 头部信息 |
初看之下,是没有问题的,但是运行时发现,程序在语句:
int readLength = br.read(pcmData, tmpLength, remainLength);
挂起。怀疑是Socket没有发送完整的数据,设置3秒的超时时间,改进后的代码如下:
1 | dataSocket.setSoTimeout(3000); |
但是,随后又发现,后面的Tcp包的开头还可能包含上一个Tcp包遗留的一些二进制数据,导致Header解析失败,如下图:
其中,第一个红圈是上个包遗留的二进制数据,第二个红圈是当前TCP包获取到的二进制长度。
怀疑是,后面的Header还遗留了前面包的一些二进制数据,进行各种容错处理如下,最后,还是发现有问题。打印二进制数据,发现都是乱码,最后怀疑是编码问题。通过对二进制通过各种编码格式转字符串。参考Java-获取文件的编码。
发现,字符串经过转码后,要么是空,要么还是乱码,最后怀疑是Socket的Read方面有问题,将原来的读取方式:
1 | BufferedReader br = new BufferedReader(new InputStreamReader(dataSocket.getInputStream())); |
替换成专门针对二进制的读取方式:
1 | InputStream is = dataSocket.getInputStream(); |
完美读取,一开始TCP包二进制数据长度不对,后面包的Header出现乱码,都是因为使用BufferedReader以及InputStreamReader导致的,这两者应该是内部对二进制数据进行了Java的默认编码进行转码,而一旦经过转码后,就无法转回原来的正确的二进制格式了。