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
Indy10.2.5的危险做法的更多相关文章
- C#使用EmguCV实现视频读取和播放,及多个视频一起播放的问题
大家知道WPF中多线程访问UI控件时会提示UI线程的数据不能直接被其他线程访问或者修改,该怎样来做呢? 分下面两种情况 1.WinForm程序 1)第一种方法,使用委托: private delega ...
- Mybatis_总结_05_用_Java API
一.前言 使用 MyBatis 的主要 Java 接口就是 SqlSession.你可以通过这个接口来执行命令,获取映射器和管理事务. 二.主要类 (1)SqlSession 是由 SqlSessio ...
- Android之父Andy Rubin:被乔布斯羡慕嫉妒的天才
今年中国掀起一股“苹果热”,智能手机iPhone.平板电脑iPad遭疯抢,一度卖断货.然而,令许多人意想不到的是,在“苹果”的老家——美国市场,智能手机中卖得最火的并不是iPhone,而是Androi ...
- Java-MyBatis: MyBatis3 | Java API
ylbtech-Java-MyBatis: MyBatis3 | Java API 1.返回顶部 1. Java API 既然你已经知道如何配置 MyBatis 和创建映射文件,你就已经准备好来提升 ...
- 7.搭建hyperledger fabric环境及启动——2019年12月12日
2019年12月12日13:05:16 声明:从网络中学习整理实践而来. 1.介绍fabric Fabric 是一个面向企业应用的区块链框架,基于 Fabric 的开发可以粗略分为几个层面: 1. 参 ...
- 从源码角度分析 MyBatis 工作原理
一.MyBatis 完整示例 这里,我将以一个入门级的示例来演示 MyBatis 是如何工作的. 注:本文后面章节中的原理.源码部分也将基于这个示例来进行讲解.完整示例源码地址 1.1. 数据库准备 ...
- 让ASP.NET接受有“潜在危险”的提交
什么是有“潜在危险”的提交?马上动手写个简单的例子: 用Visual Studio创建一个空白的ASP.NET MVC程序,一切默认即可,添加一个空白的HomeController,增加一个Ind ...
- URL传递中文字符,特殊危险字符的解决方案(仅供参考)urldecode、base64_encode
很多时候,我们需要在url中传递中文字符或是其它的html等特殊字符,似乎总会有各种乱,不同的浏览器对他们的编码又不一样, 对于中文,一般的做法是: 把这些文本字符串传给url之前,先进行urlenc ...
- 从客户端中检测到有潜在危险的Request.Form值的详细解决方案
ASP.Net1.1后引入了对提交表单自动检查是否存在XSS(跨站脚本攻击)的能力.当用户试图用之类的输入影响页面返回结果的时候,ASP.Net的引擎会引发一个HttpRequestValidatio ...
随机推荐
- Ajax以及类似百度搜索框的demo
public class Ajax01 extends HttpServlet{ @Override protected void service(HttpServletRequest request ...
- JVM--Garbage First
Garbage First Garbage First 简称 G1,它的目标是要做到尽量减少 GC 所导致的应用暂停的时间,让应用达到准实时的效果,同时保持 JVM 堆空间的利用率,其最大的特色在于允 ...
- POJ 3047 Fibonacci
DEBUG很辛苦,且行, 且珍惜 原代码: ans[0][0] = (ans[0][0] * a[flag][0][0] + ans[0][1] * a[flag][1][0]) % 10000; a ...
- 基于FPGA的DW8051移植(三)
总结一下问题: 1) http://www.cnblogs.com/sepeng/p/4137405.html 基于FPGA的DW8051移植(一)里面用modelsim观测波形发现程序进入了ida ...
- PHP urlencode()和rawurlencode()使用和区别
string urlencode ( string $str ) 此函数便于将字符串编码并将其用于 URL 的请求部分,同时它还便于将变量传递给下一页. 返回值 返回字符串,此字符串中除了 -_. 之 ...
- 快速解决PDF文档加密不能打印问题_百度经验
快速解决PDF文档加密不能打印问题_百度经验 快速解决PDF文档加密不能打印问题 | 浏览:182 | 更新:2014-01-06 1 ...
- 函数可以作为Javascript对象(哈希表)的键吗
一般Javascript书在讲解对象时,都指出Javascript中的对象可以作为哈希表,存储键值数据.通常情况下,键为字符串,如果键是数字的话,实际上在内部也会转换为字符串. 比如 var o = ...
- LTP介绍
1.LTP介绍 LTP--linut test project ,ltp套件是由Linux Test Project所开发的一套系统測试套件.它基于系统资源的利用率统计开发了一个測试的组合,为系 ...
- 最简单也最难——如何获取到Android控件的高度
问题 如何获取一个控件的长和高,相信很多朋友第一眼看见这个问题都会觉得很简单,直接在onCreate里面调用getWidth.getMeasuredWidth不就可以获得了吗,但是,事实上是并没有简单 ...
- Android中使用"running services"查看service进程内存
从Android 2.0开始,在Settings中加入了一个新的activity("Running Services" activity),它用于显示当前运行的每个Services ...