zoukankan      html  css  js  c++  java
  • Delphi中的TUdpSocket不能当作服务端的问题

      在不同的DELHI版本中,INDY的版本是不同的,而且9和10不兼容,为了让代码简单,轻便,跨版本移值,决定udp使用时使用TUdpSocket控件来实现收发。结果却令人郁闷的发现,无法接收,因为TUdpSocket是为作Client设置的,没有进行Bind,于是,显式调用了Bind,依然不行,返回一个错误码。查看netstat ,显示的是一个随机的端口。

      由于对WINSOCK底层不是很了解,看源码时没有发现什么问题。结果在CSDN上有人指出了问题所在,TUdpSocket在Open时会调用Connect,而Connect时会bind到一个随机的端口,所以再次bind时会失败。

      原来如此,原来一个Socket不管是Client还是Server,都会Bind到一个端口的,我还以为只有服务端会Bind,惭愧啊。

      既然找到了原因,也就找到了解决的办法,,办法有两个:

      (1))给VCL打补丁,在Open的时候先进行bind,然后再调用connect,但是这样就改变了VCL原来控件的行为,也许以后某个人会进行怎么的调用,想当然的认为Tudpsoket发送的时候不会bind在设置的本地端口上,但用了这个patch,却会一直绑在固定的端口上,让他一头雾水。

    unit u_UDPSocketPatch;

    interface
    uses
    Windows, Sockets, WinSock;



    implementation
    type
    TUdpSocketPatcher = Class(TIpSocket)
    Protected
    Procedure Open; Override;
    end;
    TAccessCrack = Class(TCustomIpClient)
    private
    FConnected: Boolean;
    FOnConnect: TSocketNotifyEvent;
    FOnDisconnect: TSocketNotifyEvent;
    end;
    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;
    { TIPSocketPatcher }

    procedure TUdpSocketPatcher.Open;
    var
    addr: TSockAddr;
    begin
    inherited Open;
    Bind;
    if Active and not TAccessCrack(Self).FConnected then
    begin
    addr := GetSocketAddr(RemoteHost, RemotePort);
    {$IFDEF MSWINDOWS}
    TAccessCrack(Self).FConnected := ErrorCheck(WinSock.connect(Handle, addr, sizeof(addr))) = 0;
    {$ENDIF}
    {$IFDEF LINUX}
    FConnected := ErrorCheck(Libc.connect(Handle, addr, sizeof(addr))) = 0;
    if not FConnected then // Workaround on bug in Red Hat 6.2
    Close;
    {$ENDIF}
    if TAccessCrack(Self).Connected then
    TAccessCrack(Self).DoConnect;
    end;
    end;

    initialization
    PatchVCLCode(@TUdpSocket.Open, @TUdpSocketPatcher.Open );
    end.



      (2)另一个重写一个组件作为服务器,这个办法就是需要用户再安装此组件到DELPHI上,感觉也不是很好啊。

    unit u_UDPSocketServer;

    interface
    uses
    Classes, Sockets, Winsock;
    type
    TudpSocketMode = (smClient, smServer);
    TUdpSocketServer = class(TIpSocket)
    private
    FMode: TudpSocketMode;
    procedure SetMode(const Value: TudpSocketMode);
    Public
    Constructor Create(AOwner: TComponent); Override;
    procedure Open; Override;
    published
    property Active;
    property BlockMode;
    property LocalHost;
    property LocalPort;
    property RemoteHost;
    property RemotePort;
    property OnCreateHandle;
    property OnDestroyHandle;
    property OnReceive;
    property OnSend;
    property OnError;
    Property Mode: TudpSocketMode read FMode write SetMode;
    end;

    implementation

    { TUdpSocketServer }

    constructor TUdpSocketServer.Create(AOwner: TComponent);
    begin
    inherited;
    SockType := stDgram;
    Protocol := IPPROTO_UDP;
    end;

    procedure TUdpSocketServer.Open;
    begin
    inherited;
    if (FMode = smServer) and Active then
    Bind;
    end;

    procedure TUdpSocketServer.SetMode(const Value: TudpSocketMode);
    begin
    FMode := Value;
    end;

    end.


    比较起来,还是新生成一个控件比较简洁,而且不会影响到原来的VCL

  • 相关阅读:
    Flask上下文管理源码分析 ——(3)
    Flask 快速使用 进阶—— (2)
    HTML-语法
    安装kubenetes-遇到的问题总结
    CentOS7-部署kubernetes
    k8s-部署及介绍
    docker-macvlan网络
    Dom编程-左侧菜单栏设计模型实现
    JavaScript-checkbox标签-隐藏、显示、全选、取消和反选等操作
    docker-Overlay原生网络
  • 原文地址:https://www.cnblogs.com/littlestone08/p/2295390.html
Copyright © 2011-2022 走看看