• Delphi Sockets.pas单元中TIpSocket的Bug


      由于最近在看UDP打洞,本着力求精简,不用三方控件的原则下,折腾了Sockets.pas这个单元。在弄清如何用Sockets单元中类执行udp server的功能时,发现了一个小BUG。

    BUG描述:

      无法判断UDP数据包是谁发来的。这个BUG存在于TIpSocket类的ReceiveFrom方法中,功能是从当前的Socket中接收数据包,并可以返回对方的TSockAddr,在UDP接收中,这个很重要,因为UDP需要知道数据包是从哪里发送来的。

      此函数的实现为:

    function TIpSocket.ReceiveFrom(var buf; bufsize: Integer; ToAddr: TSockAddr; var len: Integer; flags: Integer): Integer;
    begin
    {$IFDEF MSWINDOWS}
    Result := ErrorCheck(WinSock.recvfrom(FSocket, buf, bufsize, flags, ToAddr, len));
    {$ENDIF}
    {$IFDEF LINUX}
    Result := ErrorCheck(Libc.recvfrom(FSocket, buf, bufsize, flags, @ToAddr, @len));
    {$ENDIF}
    if Result <> SOCKET_ERROR then
    DoReceive(pchar(@Buf), Result);
    end;

    可以发现,ToAddr是传值进入函数而被WinSock.RecvFrom调用的,这意味着,返回的源地址并不能返回。修正起来很简单,把声明改成var传入就可以了

    function TIpSocket.ReceiveFrom(var buf; bufsize: Integer; var ToAddr: TSockAddr; var len: Integer; flags: Integer): Integer;
    begin
    {$IFDEF MSWINDOWS}
    Result := ErrorCheck(WinSock.recvfrom(FSocket, buf, bufsize, flags, ToAddr, len));
    {$ENDIF}
    {$IFDEF LINUX}
    Result := ErrorCheck(Libc.recvfrom(FSocket, buf, bufsize, flags, @ToAddr, @len));
    {$ENDIF}
    if Result <> SOCKET_ERROR then
    DoReceive(pchar(@Buf), Result);
    end;

    修正方案:
    使用给vcl打补丁的方法

    unit u_SocketsPatcher;

    interface
    uses
    Sockets, WinSock;

    implementation
    Procedure PatchVCLCode(ProcOld, ProcNew: Pointer);
    var
    newCode : packed record
    JmpRel32 : Byte;
    Offset32 : Integer;
    end;
    begin
    newCode.JmpRel32 := $E9;
    newCode.Offset32 := Integer(procNew) - Integer(procOld) - 5;
    WriteProcessMemory(
    GetCurrentProcess,
    procOld,
    @newCode,
    SizeOf(newCode),
    DWORD(nil^) );

    end;

    type
    TIPSocketPatch = Class(TIpSocket)
    Private
    function ReceiveFrom2(var buf; bufsize: Integer; var ToAddr: TSockAddr; var len: Integer; flags: Integer = 0): Integer;
    End;

    { TIPSocketPatch }

    function TIPSocketPatch.ReceiveFrom2(var buf; bufsize: Integer;
    var ToAddr: TSockAddr; var len: Integer; flags: Integer): Integer;
    begin
    {$IFDEF MSWINDOWS}
    Result := ErrorCheck(WinSock.recvfrom(Handle, buf, bufsize, flags, ToAddr, len));
    {$ENDIF}
    {$IFDEF LINUX}
    Result := ErrorCheck(Libc.recvfrom(FSocket, buf, bufsize, flags, @ToAddr, @len));
    {$ENDIF}
    if Result <> SOCKET_ERROR then
    DoReceive(pchar(@Buf), Result);
    end;

    initialization
    PatchVCLCode(@TIpSocket.ReceiveFrom, @TIPSocketPatch.ReceiveFrom2);
    end.

    在工程中加入此单元的引用即可

    BUG的影响

      由于DELPHIER们使用UDP很少使用Sockets.pas中的单元而一般用Indy组件,所以这个BUG影响范围很小,即使使用udp,也都是只用TUdpSocet类作为Client端,而不会调用这个方法。所以,这是一个基本上不会被调用的BUG函数,也很少影响到Delphier们。但如果要想自己力求精简的编写一些基础组件的话,这个问题就会被波及,比如此次我弄的这个UDP Hole Punching的时候,终于显现出来了,也花了我半天的时间,唉....
     

        

  • 相关阅读:
    Delegte的BeginInvoke
    C# socket 实现客户端连续发送数据
    Button的PerformClick()
    Invoke和BeginInvoke
    Application.DoEvents() 处理队列消息,防界面假死
    BackgroundWorker后台线程
    CF773F
    ORM框架,没必要搞那么复杂
    VS Unable to copy file
    Unbuntu auto start program
  • 原文地址:https://www.cnblogs.com/littlestone08/p/2296015.html
Copyright © 2020-2023  润新知