公司的充电宝项目使用物联网技术连接设备,而与设备交互需要用到 TCP (或者 MQTT,性质一样)长连接。

PHP 做这种项目大概率都会使用 workerman 框架(可以处理 TCP、Websockt 等请求),而业务框架 thinkphp 似乎也是首选。因为 thinkphp 可以将 wokerman 以扩展库的形式直接安装到框架中使用,过程非常简便。thinkphp 会在 config 下生成 worker.phpworker_server.php 两个文件,分别对应客户端和服务端,使用起来也异常轻松。

之前处理的消息都是字符串类型的,直接接收就可以解析消息内容。这次接的新机器通讯协议规定收发消息使用 16 进制编码进行解析,内容解析给了个 demo,一开始以为这次对接会比较容易。详细看 demo 代码才发现,并没处理 16 进制数据的逻辑,直接就拿字符串开始解析的,结果就在这卡了壳。接了一天都在搞 tp 框架的东西,临下班时才发现,通过 TCP 工具拿到的消息解析总是出错,输出来都是乱码。

公司领导层都是门外汉,大 boss 甚至在晚上就过来问,新设备接好了没,搞得我一脸懵逼。幸好上面还有直接领导顶着,不然我真的要骂娘了(当然真要吵架还是有点虚的,毕竟大 boss,一句话我可能就要走人了)。问题在于即便是给了一个可以直接使用的代码,也要跟现有的业务框架所有的消息都对接上才行啊。而且不同的生产厂家,消息内容都不一样,还要额外花时间去适配处理,真以为技术都是搬运工啊!

牢骚话不扯了,这两天就加班搞吧。今天花了些时间,终于搞明白怎么处理 16 进制数据了。说是 16 进制数据,其实也是对使用者来说的,TCP 底层传输的都是二进制流。与正常的字符串数据不同点在于,字符串数据其实是 ascii 编码,在计算机获取到之后就可以直接转成成字符串;而 16 进制数据需要对其进行转换,不然以默认的 ascii 编码进行输出,就是一团乱码。

通讯协议里需要解析的是 16 进制的字符串。其中的部分字段需要 abcd 之类的字符串类型,这个可以在后续截取 16 进制的字符串再转。这里需要强调的是,16 进制数据与 16 进制字符串数据是完全不同的两个概念

16 进制数据大概的样子:\x00\x01\x9A,而 16 进制字符串长这样:0001

Tcp udp通信发送的十六进制和字符的区别

博文内容较短,我就直接转发一下了:

字符用ASCII集来表示,需要8个二进制位,ASCII表有255个字符
十六进制是原样发送,需要4个二进制位
例如发送 A8
字符发送的是 0000 1010 0000 1000
而十六进制发送的是 1010 1000
因此,在收发需要进行转换

他的评论区还有人质疑说,发 Hello 并不是这样。其实是那位质疑者没有搞明白计算机的数据转换方式。A8Hello 都属于 ascii 码的范畴,所以这两者对于计算机来说,并没有什么区别,都是 8 个 bit 位表示一个字符(高位不足用 0 填充)。所有的计算机输入数据在转化为二进制流时会存储相应的编码方式(区位码)。计算机在接受到 ascii 编码的数据后是可以直接识别的。

ASCII码是由美国国家标准学会(ANSI)制定的,是一种标准的单字节字符编码方案,统一规定了常用符号用哪些二进制数来表示。用于基于文本的数据。

而 16 进制数据并没有区位码标识解码方式,或者说不能直接读取出来数据内容,强行以 ascii 码输出当然就是乱码了。

回到问题本身,16 进制数据如何转化为 16 进制字符串?在一段无效 coding 之后,终于找到了一个 PHP 的库函数 bin2hex ( string $str ) : stringphp 进制转换函数 是进制数据之间的转换,之前想过先将二进制流转成 16 进制,然后再转成 16 进制字符串,但闹腾了半天也不顶用:

bindec() — 二进制转换为十进制
decbin() — 十进制转换为二进制
dechex() — 十进制转换为十六进制
decoct() — 十进制转换为八进制
hexdec() — 十六进制转换为十进制
octdec() — 八进制转换为十进制
base_convert()– 在任意进制之间转换数字

bin2hex 是我在 php 如何将字符串转换为 16 进制字符串 文章中找到的,w3school 上的解释:

bin2hex() 函数把 ASCII 字符的字符串转换为十六进制值。字符串可通过使用 pack() 函数再转换回去。

库函数定义里的功能解释:

Convert binary data into hexadecimal representation

意思是 bin2hex 可以将二进制流数据转化为 16 进制代表的字符串。二进制流数据可以表示成字符串,这一点也佐证了我上面说的,常规的 ASCII 编码的字符串在传输后是可以直接被计算机直接识别的。

TCP 报文发送工具 sokit 的使用

参考 mac 下的 socket 调试工具---sokit,也是从这篇文章里才知道,sokit 发送 16 进制数据,是将数据内容放在方括号里,比如:[FF AB CD 12 12]

实际发送时会过滤掉方括号本身,以及其中的空格字符。(使用两个连续的 [[ 表示 [ 字符本身)

mac 下可以使用博文里作者重构的 sokit,windows 可以在网上直接找。使用华军软件园下载需要注意,下载下来如果直接就一个 exe 文件,最好不要使用,会有一大堆的捆绑安装软件,且关都关不掉。不清楚是怎么授权安装的,下载的软件一个接一个,搞得电脑都卡卡的。最后用 360 清除了一遍,也不知道顶不顶用,算是心里安慰吧。