为了排查一个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. [javascript]在浏览器端应用cookie记住用户名

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  2. 基于FPGA的DW8051移植(一)

    最近 半个月都在移植8051,看到DW8051内核资料比较齐全又是新思发布的,所以就开始玩弄 可是这半个月的努力几近白费 —— 移植失败了,不知道从何着手这个内核.可能大家能找到不同的版本,我的是最初 ...

  3. Winsock在Windows下的编程教程(C语言)(图文并茂,超长教程)

    https://www.0xaa55.com/forum.php?mod=viewthread&tid=378&extra=page%3D2

  4. 人人都用 Retina 屏幕的 MacBook Pro 笔记本电脑

    自从今年年初 Apple 官网产品降价我立即买了 13 寸的 Retina 屏 MacBook Pro(rMBP)之后, 这款苹果的笔记本电脑就成了我在公司和家里的唯一电脑(就是这一款). 使用苹果的 ...

  5. 项目中用到的Java注解

    元注解: @Retention(RetentionPolicy.RUNTIME)   @Target({ElementType.METHOD})  作用:@interface用来声明一个注解,其中的每 ...

  6. GCD基本使用

    主要概念 队列 dispatch_queue_t,队列名称在调试时辅助,无论什么队列和任务,线程的创建和回收不需要程序员操作,有队列负责. 串行队列:队列中的任务只会顺序执行(类似跑步) dispat ...

  7. assert()用法

    assert宏的原型定义在<assert.h>中,其作用是如果它的条件返回错误,则终止程序执行,原型定义:[1] #include <assert.h>void assert( ...

  8. 80端口的烦恼:[3]清除NT Kernel占用80端口

    链接地址:http://jingyan.baidu.com/article/f96699bbca15a1894e3c1bc4.html 当一台电脑安装了vs又安装了xampp时,可以能发生80端口号冲 ...

  9. 我的Python成长之路---第一天---Python基础(作业1:登录验证)---2015年12月26日(雾霾)

    作业一:编写登录接口 输入用户名密码 认证成功系那是欢迎信息 输错三次后锁定 思路: 1.参考模型,这个作业我参考了linux的登录认证流程以及结合网上银行支付宝等锁定规则 1)认证流程参考的是Lin ...

  10. jQuery选择器——全新的总结方式

    jQuery 选择器的总结 用于定位的选择器: 基本选择器:(用来进行绝对定位) $("#myELement")    选择id值等于myElement的元素,id值不能重复在文档 ...