解决收包和发包内容不同的问题

前言

问题现象:

  • 和用户A型号主机通讯,收到的内容,按照协议,算完校验和都是对的。
  • 和用户B型号主机通讯,收到的内容,按照协议,校验和通不过。
    这个bug是代码实现问题,但是问题出哪了呢?用了我2个半天。

前一个半天,先去看了客户给的服务端通讯的旧参考工程(网络通讯部分一样),将本地数据序列化到网络这部分仔细看了一下,没问题。

本来还想继续查,被老大拉去干其他活。
等下班了,继续查这个问题。

既然看不出问题,那我看看数据是否有问题。数据发送方和数据接收方的数据一致么?
我捡了2个函数,将数据发送方的字节数组内容和算的每一次校验和的中间步骤都保存起来,然后打到文件。
客户端接收+算校验, 服务段发送+算校验 , 这2端都写了完整日志。

QString int2HexString(int i_dec)
{
    QString str = QString("%1").arg(i_dec, 2, 16, QChar('0'));
    return str;
}

void print_ary(QString log_file_name, QString tip, quint8* puc_ary, int len_ary)
{
    QFile file(log_file_name);
    file.open(QIODevice::WriteOnly | QIODevice::Append);
    QTextStream text_stream(&file);

    if (NULL != puc_ary) {
        text_stream << "\r\n";
        text_stream << "\r\n";

        text_stream << "ary_name = " << tip << ", length = " << len_ary << "\r\n";

        for (int i = 0; i < len_ary; i++) {
            if ((0 != i) && (0 == (i % 8))) {
                text_stream << "\r\n";
            }

            text_stream << int2HexString(puc_ary[i]);
            text_stream << " ";
        }
    }

    file.flush();
    file.close();
}
...
                print_ary(tr("uc_ary_char_cur_clt.txt"), tr("uc_ary_char_cur"), uc_ary_char_cur, sizeof(uc_ary_char_cur));
                print_ary(tr("uc_ary_chksum_clt.txt"), tr("uc_ary_chksum"), uc_ary_chksum, sizeof(uc_ary_chksum));

先运行服务端,再运行客户端,只有客户端发出请求,服务程序才会准备数据,然后记录日志。
客户端收到数据后,也会记录日志。
运行了几个交互。

将服务程序记录的2个日志和客户端记录的2个日志都放到一起,用BC4先比较收到的内容是否相同。
惊奇的发现,到了每端数据循环的后半部分,数据就不相同了。那校验自然就不对了。
为啥呢?

又去看了一下服务程序发包的全过程,没发现有问题。纠结了一会。
通过日志,可以确认,发送和接收的数据长度是一样的。那内容不一样了,谁改了?
客户端这边,进来数据就这样(被改了)。

那还得去看服务程序那边。因为前面已经看了几次服务程序,只能边纠结边单步。
当数据准备完时,数据经过日志记录,还是正确的。
那发送时,出了问题?
开始查发送缓冲区,谁动了,发现,没人动。
F2,跳到发送缓冲区的定义出看一眼。看到发送缓冲区大小是260,因为我通过单步和日志,是知道发送的size的。大约发送给了483个字节。哦,那明白了,缓冲区溢出了。多出的数据将发送缓冲区周围写脏了。发送时,虽知道发送的是啥。没崩溃,就不错了,其实,如果服务程序当场挂掉,那客户那的工程师,也能很快修正。那太明显了。就怕这种bug, 理论上是要崩溃的,居然服务程序还好好的活着…, 为什么? 真健壮…

将发送缓冲区改成4096, 搞定。收发校验都正常了。
等明天,看客户那的工程师,按照实际情况,改个合适的值。

总结一下问题出现的原因

客户那的程序初始版本和后续维护不是一个人。
可能初始作者,为了节省空间,就按实际情况,定成了260.

当后续作者,根据需求,要加大通讯量时,就改了一个发送结构数据的size宏。e.g. uc_info[INFO_NUM], INFO_NUM原来是3, 现在为了符合新需求, 改成了 # define INFO_NUM 10

咋一看,没啥问题。因为客户端程序不是他们写的。改完一看,有点easy, 就动了一个宏,就搞定。
等我写的客户端程序连上来时,现象不是崩溃,而是客户端收到数据,校验不过。刚开始,也不会想到是缓冲区溢出。

只有将客户端代码和服务程序代码反复联调时,才能准备缩小范围。然后不经意的意外发现了缓冲区溢出问题。

我在查问题的过程中,发现他们的结构定义没有使用一字节对齐,也不用结构来直接发给对方通讯,而是用QT序列化后,给对方。整的麻烦了。如果将结构一字节对齐后,可以直接通过网络发送,也能使结构实例在本地省点空间。

对于本地缓冲区的开辟,我习惯以KB为单位开。如果能跑嵌入式linux的主机,也不至于查那点空间。假设有个大的嵌套结构,一字节对齐后,省下的空间也不少。特别是工程维护人变更后,他不可能像原作者那么清楚工程的细节,一般是哪疼治哪。就可能出现考虑不周的地方。

以前有个搞java的同事,写C程序的时候(在标准debian下运行),居然也算buffer开多大啊,别浪费太多字节啊。我很不以为然。
如果资源紧张到要考虑节省多少个字节的时候,当然要节省。
如果内存资源很充裕,为啥要一个字节一个字节的抠呢,这不折腾自己么:)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值