为了排查一个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

Indy10.2.5的危险做法的更多相关文章

  1. C#使用EmguCV实现视频读取和播放,及多个视频一起播放的问题

    大家知道WPF中多线程访问UI控件时会提示UI线程的数据不能直接被其他线程访问或者修改,该怎样来做呢? 分下面两种情况 1.WinForm程序 1)第一种方法,使用委托: private delega ...

  2. Mybatis_总结_05_用_Java API

    一.前言 使用 MyBatis 的主要 Java 接口就是 SqlSession.你可以通过这个接口来执行命令,获取映射器和管理事务. 二.主要类 (1)SqlSession 是由 SqlSessio ...

  3. Android之父Andy Rubin:被乔布斯羡慕嫉妒的天才

    今年中国掀起一股“苹果热”,智能手机iPhone.平板电脑iPad遭疯抢,一度卖断货.然而,令许多人意想不到的是,在“苹果”的老家——美国市场,智能手机中卖得最火的并不是iPhone,而是Androi ...

  4. Java-MyBatis: MyBatis3 | Java API

    ylbtech-Java-MyBatis:  MyBatis3 | Java API 1.返回顶部 1. Java API 既然你已经知道如何配置 MyBatis 和创建映射文件,你就已经准备好来提升 ...

  5. 7.搭建hyperledger fabric环境及启动——2019年12月12日

    2019年12月12日13:05:16 声明:从网络中学习整理实践而来. 1.介绍fabric Fabric 是一个面向企业应用的区块链框架,基于 Fabric 的开发可以粗略分为几个层面: 1. 参 ...

  6. 从源码角度分析 MyBatis 工作原理

    一.MyBatis 完整示例 这里,我将以一个入门级的示例来演示 MyBatis 是如何工作的. 注:本文后面章节中的原理.源码部分也将基于这个示例来进行讲解.完整示例源码地址 1.1. 数据库准备 ...

  7. 让ASP.NET接受有“潜在危险”的提交

    什么是有“潜在危险”的提交?马上动手写个简单的例子:   用Visual Studio创建一个空白的ASP.NET MVC程序,一切默认即可,添加一个空白的HomeController,增加一个Ind ...

  8. URL传递中文字符,特殊危险字符的解决方案(仅供参考)urldecode、base64_encode

    很多时候,我们需要在url中传递中文字符或是其它的html等特殊字符,似乎总会有各种乱,不同的浏览器对他们的编码又不一样, 对于中文,一般的做法是: 把这些文本字符串传给url之前,先进行urlenc ...

  9. 从客户端中检测到有潜在危险的Request.Form值的详细解决方案

    ASP.Net1.1后引入了对提交表单自动检查是否存在XSS(跨站脚本攻击)的能力.当用户试图用之类的输入影响页面返回结果的时候,ASP.Net的引擎会引发一个HttpRequestValidatio ...

随机推荐

  1. PHPer的等级划分

    PHPer的等级划分 前一段时间刚刚完成PHP的培训,然后想知道自己目前的水平(或者说等级),并且应该在哪些方面进行提高,所以在网上查了一下相关介绍.其中有一篇介绍讲的挺清楚的,至少目前的我还是很认同 ...

  2. lnmp-zabbix

    wget http://down1.chinaunix.net/distfiles/freetype-2.4.7.tar.bz2 tar -jxvf freetype-2.4.7.tar.bz2 cd ...

  3. Automake创建项目

    autoconf和automake可以方便的构建linux下项目,一个简单的automake项目实例,麻雀虽小五脏俱全,以后无外乎在这基础上扩展相应的宏完善而已. .首先建立项目目录树 )创建目录树 ...

  4. Linux定义系统提示符的变量:PS1

  5. android蓝牙的调试(博通蓝牙工作 and 低功耗模式)

    首先结合项目从整体上去把握这部分: 蓝牙模块中一个比较核心的文件是bluetooth.c, 在我们上电的时候, 会调用这个文件中bt_enable()这个函数, 在这个函数里面先调用set_bluet ...

  6. easyui只选择年份月份的插件---SimpleCanleder

    <td>日期</td> <td> <input type="text" id="search_begindate" s ...

  7. PyDev下PyQt 的尝试

    刚刚装完PyDev ,试了下之前写的调用PyQt的下代码,发现运行出错:搜索只还需在System PYHONPATH下 添加PyQt的路径,步骤如下: eclipse--window--Prefere ...

  8. 转:Android -- ActivityLifeCycleCallbacks

    http://www.cnblogs.com/yydcdut/p/4945990.html 一个不常见类的使用,名字叫ActivityLifeCycleCallbacks 通常一个项目中有很多个act ...

  9. ios网络学习------1get post异步请求

    网络请求的步骤: get请求: #pragma mark - 这是私有方法,尽量不要再方法中直接使用属性,由于一般来说属性都是和界面关联的,我们能够通过參数的方式来使用属性 #pragma mark ...

  10. 关于SSH框架设计的一些理解

    近期在学习企业开发领域非常流行的SSH框架(Struts.Hibernate.Spring).因为之前有做过原生的Servlet+JSP的项目,所以在学习过程中我会跟原生开发模式进行对照,在这里我把自 ...