zoukankan      html  css  js  c++  java
  • Indy10.2.5的危险做法

    为了排查一个Bug今天无意看了看Indy源码,结果吓了一跳。TIdIOHandler.ReadLongWord函数用于读取通讯数据并转换成LongWord类型返回,它做用了一种危险的做法可能会导致数据传输不正确。

    函数源码如下:

    function TIdIOHandler.ReadLongWord(AConvert: Boolean): LongWord;
    var
      LBytes: TIdBytes;
    begin
      ReadBytes(LBytes, SizeOf(LBytes), False);
      Result := BytesToLongWord(LBytes);
      if AConvert then begin
        Result := GStack.NetworkToHost(Result);
      end;
    end;
    问题就在于ReadBytes(LBytes, SizeOf(LBytes), False)这句,和ReadInt64、ReadByte等其他函数明显不同,读取内容的长度为SizeOf(LBytes)而非SizeOf(LongWord)。这个LBytes空间为何物?它的定义是TIdBytes = TBytes , ==> TBytes = array of Byte,也就是说其他Byte数据类型。

    LongWord类型有几长?在Delphi中它和UInt32一样是Cardinal类型,占4个字节位,即SizeOf(LongWord)=4。

    LBytes类型又有几长?因为它是数组类型,最终会变成指针引用,所以和指针变量一样占一个机器字长,在32位机上就是32bit即4个字节位了,但是在64机上就要变成8个字节位了(我没64位机,请看官自行验证一下)。

    这样,这个ReadLongWord函数在32位机上Read到的的确是LongWord,到了64位机上就成成了ReadInt64了,在网络通讯这种位位计较的地方数据就全乱套了。

    Indy是Delphi中被使用得最多的网络通讯组件,它不断升级却也bug不断,很难想像Indy公司内部是如何进行测试的。如果是我的程序员写出这样的代码,我肯定得找他谈谈心了。

    ----------------------------------------------------------------------------------

    ReadBytes()仅仅是从缓冲区读取一个指向数据的指针,在这里的用法完全是正确的,适用于平台字位变化的情况。
    真正有问题的,应该是
    Result := BytesToLongWord(LBytes);
    在转换的时候出错。
    我所谓的“存在的问题是‘可能’”的,是指:
    如果缓冲区中的数据属于int32范围,那么转换不会出问题;
    如果缓冲区中的数据属于int64范围,那么由于转换的截断处理,的确会导致结果的错误。
    Re: nhconch 2009-12-14 23:36发表 [回复]
    你的观点我同意一半,缓冲区中是何种数据类型是发送端决定的,接收端和发送端之间应有数据同步的协议才能有效通讯,如果发送端发出INT32的数据,接收端当INT64来处理当然会出错,但这是程序开发者的错误,不能怪Indy。
    但Indy10.2.5的危险做法是在该用SizeOf(LongWord)的地方用了SizeOf(LBytes),这就是你说的“存在的问题是‘可能’”,因为没法保证不SizeOf(LongWord)永远等于SizeOf(LBytes)。
    4楼 现货黄金 2009-11-23 13:18发表 [回复]
    本人并不完全赞同作者的观点!
    存在问题是“可能”的。
    在Delphi中,出来High, Low, Succ, Pred, Inc, Dec, IntToStr, and IntToHex 以及其他一些明确含有"64"字样的函数支持64位操作之外,其他的函数几乎都会截断64位的变量成32位来操作。
    其二就是和编译器有关,帮助中说的明白,除了int、指针类型会随着操作系统环境的字长而自动确定位数外,其他类型除非特殊指定,否则编译器是按照定长编译的。

    究竟是否存在32位环境下编译的程序到64上就会产生混乱,这需要实际测试!
    再说,直接将32位环境下编译的程序用到64位环境下,还是值得商榷的!
    Re: nhconch 2009-11-23 18:11发表 [回复]
    即使最新Delphi2010还是不支持64位的,虽然“直接将32位环境下编译的程序用到64位环境下还值得商榷”,但如果是用Delphi来做的,只能直接拿来用了。
    含有"64"字样的函数操作的是64位变量(或直接数),不论32位还是64位环境得到的结果是一致的。
    array of byte是数组,最终也是指针,就是你说的“随着操作系统环境的字长而自动确定位数”的,Indy10.2.5的这人危险做法就是在该取LongWord长度(固定值)的地方使用的指针的长度(会变的)。
    3楼 jeanler 2009-11-22 19:47发表 [回复] [引用] [举报]
    我在win7x86上试了一下, SizeOf(LBytes)=4, 我猜测这个应该跟windows版本或者编译器有关, 估计在64位编译器下可能就是8了
    Re: nhconch 2009-12-14 23:20发表 [回复] [引用] [举报]
    Delphi在编译时直接将SizeOf换成相应数据值,也就是说代码中写SizeOf(LBytes),生成EXE时直接被换成4了。

    http://blog.csdn.net/nhconch/article/details/4840070

  • 相关阅读:
    数据库事务的四个隔离级别
    synchronized与Lock的区别
    线程池的注意事项
    守护线程与非守护线程
    wait与sleep的区别
    String,StringBuffer,StringBuilder
    2019牛客暑期多校训练营 第二场
    2019牛客暑期多校训练营 第一场
    Codeforces Round #568 (div. 2)
    Codeforces Round #570 (Div. 3)
  • 原文地址:https://www.cnblogs.com/findumars/p/5393706.html
Copyright © 2011-2022 走看看