Winsock2的WSABUF对齐问题记录

    本人是Delphi老程序员,开发Windows下异步底层通信组件已经好多年,最高性能的完成端口组件也已经实现好多年了,可以说对Winsock已经是相当熟悉,完成端口的运行机制也比较熟悉,所开发的组件应用于公司的系统,成功应答数千大数据量并发连接的考验,应该说技术上算是比较成熟稳定了。

   我开发电脑上,目前使用2个版本的Delphi,古老的Delphi7和稍微新点的XE2,Delphi7够老,不支持64位平台,XE2是支持的。由于系统性能一直还不错,所以也就没有抽出时间来对完成端口组件进行64位升级改造,最近心血来潮,决定把这个捡起来,后续会开放源代码到Github上,原来32位的封装有的地方也有点不合理,新的64位封装会尽量改造好一些,接口却会简化。

    写这篇博客的原因,其实我早就发现Delphi的Winsock2在64位下有点问题,Winsock2.pas单元定义的WSABUF结构带有packed字样,表明结构是按字节对齐,一个数据长度,一个数据指针,这个在32位程序里没有问题,因为大家都是4字节,无论怎么对,都是8字节大小,换到64平台,如果带packed,程序运行肯定不会成功,WSABUF此时的大小应该是12字节,这个问题在改写一个重叠I/O的UDP组件时发现,把packed去掉WSARecvFrom和WSASendTo就可以正确调用。

    我封装新的64位完成端口组件的时候,发现调用WSARecv总返回10014(WSAEFAULT)的错误,看MSDN,说是WSABUF的地址不对,系统无法访问它,因此返回这个10014的错误,如果不是有前人的经验,大家想破脑袋,也无法解决这个问题,因为WSABUF的指针看起来不会有任何问题,实际上是微软玩了手脚,要求WSABUF的指针必须对齐到4的倍数,换句话说,这个指针整数必须是4的倍数,NativeUInt(pBuf) mod 4=0,这里pBuf就是WSABUF指针,有如下两篇文章可以参考,他们也发现这个问题,并解决,感谢两位的努力和分享,文章链接如下:http://www.cppblog.com/Tim/archive/2011/07/07/150391.html  https://blog.csdn.net/shandongmachao/article/details/48541971。

  按照他们两位的说法,我是这么解决的,代码例子如下:

var

   buf:array[0..127] of Byte;

   dd:NativeUInt;

  pBuf:PWSABUF;

  err:Integer;

  FinishLen,Flag:DWORD;

begin

  FillChar(buf, SizeOf(buf), 0);
  dd:=NativeUInt(@buf[0]);
  dd:=dd + (WSABUF_ALIGN_NUM - dd mod WSABUF_ALIGN_NUM);
  pBuf:=PWSABUF(dd);

  err:=WSARecv(sock,     //客户端套接字
             pBuf,  //接收缓冲区
             1,                      //缓冲区个数
             FinishLen, //接收到的字节数,完成包里有,这里不用管
             Flag,                   //标志,必须初始化为0或有效值,否则会出错
             nil, //重叠结构
             nil) ;

  ...

end;

这么搞一下以后,WSARecv和WSASend就正常了。

 

最后透露一下未来我的64位完成端口通信组件的功能,目前已经初具雏形,第一波测试已经基本结束:

1、可以同时监听多个端口,每个端口都可以自由控制接收发送

2、支持高性能文件发送,使用TransmitFile API函数

3、支持高性能使用完成端口控制的文件读写

 

posted @ 2023-04-25 16:43  tangrh  阅读(174)  评论(0编辑  收藏  举报