基于Delphi API写的UDP通讯类

转载地址:http://www.codefans.net/articles/159.shtml

基于Delphi API写的UDP通讯类,可以广播和单播,类作者:王彦鹏。这个类是作者2007年的时候写的,代码里基本没什么注释,有需要的朋友自己摸索下,懂Delphi的应该可以看懂。

unit TUdp_Class;
interface
uses
  Classes,Windows,WinSock;
type
  TRecv= procedure (RIP:string;buf:pchar;Bufsize:integer) of object;
  TRecvExpand= procedure (RIP:string;Port:integer;buf:pchar;Bufsize:integer) of object;
  TUdp = class(TThread)
  private
    WSocket:TSocket;
    FActive:Boolean;
    FPort,FSendPort:integer;
    Addr: TSockAddr;
    FSockAddrIn : TSockAddrIn;
    FOnRecv:TRecv;
    FOnRecvExpand:TRecvExpand;
    Rtl:TRTLCriticalSection;
    procedure SetPort(Value:integer);
    procedure SetOnRecv(value:TRecv);
    procedure SetOnRecvExpand(value:TRecvExpand);
    function GetCurPort:integer;
    { Private declarations }
  protected
    procedure Execute; override;
  public                    
    constructor Create;
    destructor Destroy; override;
    function SendBuf(Host:string;Buf:pchar;BufSize:integer;Broadcast:boolean=false):integer;
    Function GetLocalIP():string;
  published
    property Port:integer read FPort write SetPort default 0;
    property SendPort:integer read FSendPort write FSendPort default 0;
    property OnRecv:TRecv read FOnRecv write SetOnRecv;
    property OnRecvExpand:TRecvExpand read FOnRecvExpand write SetOnRecvExpand;
    property CurPort:Integer read GetCurPort;
  end;
implementation
uses SysUtils;
{ TUdp }
constructor TUdp.Create();
var wsadata: Twsadata;
begin
  InitializeCriticalSection(rtl);
  if wsastartup($2, wsadata) <> 0 then
  begin
    Raise Exception.Create(SysErrorMessage(GetLastError));
  end
  else
    WSocket:=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
  if WSocket= INVALID_SOCKET then
    Raise Exception.Create(SysErrorMessage(GetLastError))
  else
    inherited create(true);
end;
destructor TUdp.Destroy;
begin
  closesocket(WSocket);
  wsacleanup();
  DeleteCriticalSection(Rtl);
  inherited;
end;
procedure TUdp.Execute;
var
  buf: pchar;
  Len: integer;
  FDS:TFDSet;
  TimeOut:TimeVal;
begin
  buf := AllocMem(10240);
  timeout.tv_sec := 0;
	timeout.tv_usec := 10;
  FSockAddrIn.SIn_Port := htons(FPort);
  while not Terminated do
  begin
    EnterCriticalSection(rtl);
    fillchar(Fds,sizeof(Fds),0);
    FD_SET(WSocket ,fds);
    len:=select(0,@fds,nil,nil,@TimeOut);
    if len>0 then
    begin
      len:=sizeof(FSockAddrIn);
      fillchar(buf[0],10240,0);
      len := recvfrom(WSocket, buf[0], 10240, 0,FSockAddrIn,len);
      if (len<>0) and (len<>-1) then
      begin
        if Assigned(fonRecv) then
          FOnRecv(inet_ntoa(FSockAddrIn.sin_addr) ,buf,len);
        if Assigned(fOnRecvExpand) then
          FOnRecvExpand(inet_ntoa(FSockAddrIn.sin_addr),htons(FSockAddrIn.sin_port),buf,len);
      end;
    end;
    LeaveCriticalSection(rtl);
    sleep(10);
  end;
  freemem(buf);
  closesocket(WSocket);
end;


function TUdp.GetCurPort: integer;
begin
  Result:=htonl(FSockAddrIn.SIn_Port);
end;

function TUdp.GetLocalIP(): string;
var
    HostEnt: PHostEnt;
    Ip: string;
    addr: pchar;
    Buffer: array [0..63] of char;
    GInitData: TWSADATA;
begin
  Result := '';
  try
    WSAStartup(2, GInitData);
    GetHostName(Buffer, SizeOf(Buffer));
    HostEnt := GetHostByName(buffer);
    if HostEnt = nil then Exit;
    addr := HostEnt^.h_addr_list^;
    ip := Format('%d.%d.%d.%d', [byte(addr [0]),
          byte (addr [1]), byte (addr [2]), byte (addr [3])]);
    Result :=Ip;
  finally
    WSACleanup;
  end;
end;

function TUdp.SendBuf(Host: string; Buf:pchar; BufSize: integer;Broadcast:boolean=false  ): integer;
var optval:integer;
begin
  if Broadcast then
  begin
    optval:= 1;
    if setsockopt(WSocket,SOL_SOCKET,SO_BROADCAST,pchar(@optval),sizeof(optval)) = SOCKET_ERROR then
       Raise Exception.Create(SysErrorMessage(GetLastError))
    else
    begin
      FSockAddrIn.SIn_Family := AF_INET;
      FSockAddrIn.SIn_Port := htons(FSendPort);
      FSockAddrIn.SIn_Addr.S_addr := INADDR_BROADCAST;
      result:=sendto(WSocket,buf[0],BufSize,0,FSockAddrIn,sizeof(FSockAddrIn));
    end;
  end
  else
  begin
    FSockAddrIn.SIn_Family := AF_INET;
    FSockAddrIn.SIn_Port := htons(FSendPort);
    FSockAddrIn.SIn_Addr.S_addr :=inet_addr(pchar(host));
    result:=sendto(WSocket,buf[0],BufSize,0,FSockAddrIn,sizeof(FSockAddrIn));
  end;
end;

procedure TUdp.SetOnRecv(value: TRecv);
begin
  if @FOnRecv = @value then
    exit;
  FOnRecv:=value;
  Addr.sin_family := AF_INET;
  addr.sin_addr.S_addr := INADDR_ANY;
  addr.sin_port := htons(FPort);
  if Bind(WSocket, addr, sizeof(addr)) <> 0  then
    Raise Exception.Create(SysErrorMessage(GetLastError));
  Resume;
end;

procedure TUdp.SetOnRecvExpand(value:TRecvExpand);
begin
  if @FOnRecvExpand = @value then
    exit;
  FOnRecvExpand:=value;
  Addr.sin_family := AF_INET;
  addr.sin_addr.S_addr := INADDR_ANY;
  addr.sin_port := htons(FPort);
  if Bind(WSocket, addr, sizeof(addr)) <> 0  then
    Raise Exception.Create(SysErrorMessage(GetLastError));
  Resume;
end;

procedure TUdp.SetPort(Value: integer);
begin
  if FPort =Value then
    exit;
  if FActive then
    Suspend;
  FPort:=Value;
end;
end.

股票,证券等,用这个来发布行情数据,刷刷的。 UDP通信的优势 速度比TCP快。 UDP通信的缺点 一旦UDP包过大的话,也能正常工作。只是优势就丢失了。 idUdpClient 主要用于发送udp请求,在接收udp响应的时候是同步的,所以一定要设置超时,否则的话程序容易死。 idUpdServer 即能用于发送udp数据包,也能用于接收udp数据包。但是设计的主要目的还是用于收到udp数据包之后给于反馈。 UDP包的大小问题 资料1:以太网的MTU是1500字节,IP包头占20个字节,UDP首部占8个字节,也就是说实际数据应该小于1472字节. 资料2:鉴于Internet上的标准MTU值为576字节,所以我建议在进行Internet的UDP编程时.最好将UDP的数据长度控件在548字节(576-8-20)以内. 测试结果: 0-548字节:会完美的展现UDP协议的优势(速度刷刷的)。 大于1472字节以后的话,也可以正常执行。你会见识到什么叫做不可靠的信道(经过测试90%以上还是成功的,只是速度慢了很多)。 数据包大于2K速度明显变慢了;数据包大于3K,成功率60%到80%;数据包大于4k,成功率20%以下。 结论: 1.UDP协议还是比较可靠的。使用它能充分挖掘速度的潜力。通常大部分请求和相应都在548以下,小部分请求超过548。 2.548字节,可以存储274个汉字呢。比手机短信都长。你传什么那么大? 3.尤其是双方都在修改数据,需要实施数据实时同步的时候。修改量都比较小,用udp再合适不过了。 客户端的阻塞式响应不太理想 可以采用的办法是双方都开UDP服务器来接受。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值