多址广播控件

unit MulticastSocket;

{
    * 多址广播控件
    * 本文件提取自 U_UDPSock.pas
    * 整理于2001年11月17~2001年11月18日
    * 关于 NB30 单元,主要用于
    *   "取得本地计算机所有的MAC地址"
    *   procedure LocalMAC(slMac : TStringList);
    * 所以被我注释掉了
    * 并不影响使用
}

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  WinSock;//, NB30;

const
  MINBUFFERSIZE = 2048;
  DEFAULTBUFFERSIZE = 16384;
  MAXBUFFERSIZE = 63488; //62*1024
  MULTICAST_TTL = IP_DEFAULT_MULTICAST_TTL;
  MAX_MULTICAST_TTL = 128;

type
  PIP_mreq = ^TIP_mreq;
  TIP_mreq = record
     imr_multiaddr  : in_addr;
     imr_interface  : in_addr;
  end;

  TAPInAddr = Array [0..10] of PInAddr; // array of pInaddr
  PAPInAddr = ^TAPInaddr;               // pointer of Array

  (*
  PASTAT = ^TASTAT;
  TASTAT = record
    adapter : TAdapterStatus;
    name_buf : TNameBuffer;
  end;
  *)

  TUDPOnRecv = procedure (buffer: Pointer; len: integer; fromIP: string; fromPort: u_Short) of Object;

  //接收数据线程
  TUDPRecvThd = class(TThread)
    private
      fSocks : TSocket;
      fBufSize : integer;
      fOnRecv : TUDPOnRecv;
    protected
      procedure Execute ; override;
    public
      constructor Create(var Socks : TSocket; OnRecv : TUDPOnRecv; BufSize : integer);
  end;

type
  TMulticastSocket = class(TComponent)
  private
    { Private declarations }
    fActived    : Boolean;      {是否激活}

    fsock       : TSocket;      {socket}
    fRecvThd    : TUDPRecvThd;  {接收线程}
    fMCReq      : TIP_mreq;     {记录加入的组地址,释放资源时用}
    fSendBufSize: integer;      {发送缓冲区大小}
    fRecvBufSize: integer;      {接收缓冲区大小}
    fLocalIP    : String;       {本地IP地址}
    fAddrTo     : TSockAddr;    {发送IP地址}
    fCanRead    : Boolean;      {可以读取数据}
    fCanWrite   : Boolean;      {可以发送数据}
    fTTL        : integer;      {Time To Live,生存时间,即可以跨越的网关数}
    fGroupAddress:String;       {组地址}
    fGroupPort  : integer;      {组端口}
    //fRecvState  : Boolean;      {接收线程是否启动}
    fOnRecv     : TUDPOnRecv;   {响应的事件}

    {组地址}
    procedure SetGroupAddress(addr:String);
    {组端口}
    procedure SetGroupPort(port:integer);
    {读}
    procedure SetCanRead(CanRead:Boolean);
    {写}
    procedure SetCanWrite(CanWrite:Boolean);
    {发送缓冲区大小}
    procedure SetSendBufSize(SendBufSize:integer);
    {接收缓冲区大小}
    procedure SetRecvBufSize(RecvBufSize:integer);
    {本地IP地址}
    procedure SetLocalIP(addr:String);
    {是否激活}
    procedure SetActived(const Value: Boolean);
    {Time To Live,生存时间,即可以跨越的网关数}
    procedure SetTTL(const Value: integer);

    {改变响应事件的限制}
    //procedure SetOnRecv(const Value: Boolean);
    procedure SetOnRecv(const Value: TUDPOnRecv);

    {Local IP set valid?}
    {参数为''的话,就得到默认IP}
    function  LocalIPValid(var LocalIP:String) : Boolean;

    {设置Socket可以接收数据}
    function  EnabledListen:Boolean;
    {设置Socket不能接收数据}
    procedure DisabledListen;
    {设置Socket可以发送数据}
    function  EnabledSend:Boolean;
  protected
    { Protected declarations }
  public
    { Public declarations }
    function    Close:Boolean;
    function    Send(buffer : Pointer; len : integer ; Flag : integer = 0) : Boolean;
    function    AddToGroup : integer;
    procedure   StartReceive;

    {取得本地计算机所有的IP地址}
    procedure LocalIPs(slIPs : TStringList);
    {取得本地计算机所有的MAC地址}
    //procedure LocalMAC(slMac : TStringList);

    function  Connect:Boolean;
    function  DisConnect:Boolean;
  published
    { Published declarations }
    property    LocalAddress : String read fLocalIP write SetLocalIP nodefault;
    property    CanRead  : Boolean read fCanRead  write SetCanRead  default true;
    property    CanWrite : Boolean read fCanWrite write SetCanWrite default true;
    property    TTL : integer read fTTL write SetTTL default MULTICAST_TTL;
    property    SendBufSize: integer read fSendBufSize write SetSendBufSize default DEFAULTBUFFERSIZE;
    property    RecvBufSize: integer read fRecvBufSize write SetRecvBufSize default DEFAULTBUFFERSIZE;
    property    GroupAddress:String read fGroupAddress write SetGroupAddress nodefault;
    property    GroupPort:integer read fGroupPort write SetGroupPort default 6000;
    property    Actived:Boolean read fActived write SetActived default False;

    property    OnDataArrive:TUDPOnRecv read fOnRecv write SetOnRecv nodefault;

    constructor Create(AOwner:TComponent);override;
    destructor  Destroy;override;
  end;

procedure Register;

implementation

var
  wsData : TWSAData;

procedure Register;
begin
  RegisterComponents('FastNet', [TMulticastSocket]);
end;

{ TMulticastSocket }

function TMulticastSocket.AddToGroup:integer;
var
  nReuseAddr : integer;
  SockAddrLocal : TSockAddr;
  pPE : PProtoEnt;
begin
  Result:=-1;

  pPE := GetProtoByName('UDP');
  //Create Socket
  fSock := Socket(AF_INET, SOCK_DGRAM, pPE.p_proto);
  if fSock = INVALID_SOCKET then
    Exit;

  nReuseAddr := 1;
  if SetSockOpt(fSock, SOL_SOCKET, SO_REUSEADDR, @nReuseAddr, SizeOf(integer)) = SOCKET_ERROR then
  begin
    CloseSocket(fSock);
    Exit;
  end;

  //Set Local Address and bind
  FillChar(SockAddrLocal, SizeOf(SockAddrLocal), 0);
  SockAddrLocal.sin_family := AF_INET;
    //发送用0
    //SockAddrLocal.sin_port := htons(0);
  SockAddrLocal.sin_port := htons(fGroupPort);
  SockAddrLocal.sin_addr.S_addr := Inet_Addr(PChar(fLocalIP));
  if Bind(fSock, SockAddrLocal, SizeOf(SockAddrLocal)) = SOCKET_ERROR then
  begin
    CloseSocket(fSock);
    Exit;
  end;

    if fCanWrite then
        if not EnabledSend then
            Exit;

    if fCanRead then
        if not EnabledListen then
            Exit;

    Result:=0;
end;

function TMulticastSocket.Close: Boolean;
begin
    //MulticastReceiver
    //Exception will be? :( I don't know
    //释放接收数据线程
    if fRecvThd <> nil then
    begin
        fRecvThd.Suspend;
        fRecvThd.Free;
        fRecvThd := nil;
    end;

    DisabledListen;
    //Close Socket
    CloseSocket(fSock);
    Result:=True;
end;

constructor TMulticastSocket.Create(AOwner:TComponent);
begin
    {这里设置默认属性,我不知道为什么在Default中写的没有效果}
    LocalIPValid(fLocalIP);
    fCanRead:=True;
    fCanWrite:=True;
    fSendBufSize:=DEFAULTBUFFERSIZE;
    fRecvBufSize:=DEFAULTBUFFERSIZE;
    fGroupAddress:='225.0.0.1';
    fGroupPort:=6000;
    fTTL:=MULTICAST_TTL;
    inherited Create(AOwner);
end;

destructor TMulticastSocket.Destroy;
begin
    Close;
    inherited Destroy;
end;

procedure TMulticastSocket.SetGroupAddress(addr: String);
var
    nMCAddr : Cardinal;
begin
    if Actived=True then
        Exit;

    //Multicast address valid?
    nMCAddr := ntohl(inet_addr(PChar(addr)));
    //though Multicast ip is between 224.0.0.0 to 239.255.255.255
    //the 224.0.0.0 to 224.0.0.225 ips are reserved for system
    if not ((nMCAddr <= $efffffff) and (nMCAddr >= $e0000100)) then
        Exit;
    fGroupAddress:=addr;
end;

function TMulticastSocket.Send(buffer:Pointer;len:integer;Flag:integer=0):Boolean;
begin
    Result := False;
    if not CanWrite then
        Exit;
    if SendTo(fSock, buffer^, len, Flag{MSG_DONTROUTE}, fAddrTo,
            SizeOf(fAddrTo)) <> SOCKET_ERROR then
        Result := True;
end;

procedure TMulticastSocket.StartReceive;
begin
    if fRecvThd<> nil then
    //接收线程已经启动
        Exit;
    //启动接收线程
    if Assigned(fOnRecv) then
        fRecvThd := TUDPRecvThd.Create(fSock, fOnRecv, fSendBufSize);
end;

procedure TMulticastSocket.SetCanRead(CanRead: Boolean);
begin
    //if Actived=True then
    //    Exit;
    if fCanRead=CanRead then
        Exit;

    if CanRead then
    begin
        if not EnabledListen then
            Exit;
    end else
        DisabledListen;

    fCanRead:=CanRead;
end;

procedure TMulticastSocket.SetCanWrite(CanWrite: Boolean);
begin
    if Actived=True then
        Exit;

    fCanWrite:=CanWrite;
end;

procedure TMulticastSocket.SetGroupPort(Port: integer);
begin
    if Actived=True then
        Exit;

    fGroupPort:=Port;
end;

procedure TMulticastSocket.SetRecvBufSize(RecvBufSize: integer);
begin
    if Actived=True then
        Exit;

  //Buffer Size Valid?
  if not ((RecvBufSize <= MAXBUFFERSIZE) and (RecvBufSize >= MINBUFFERSIZE)) then
    Exit;
  fRecvBufSize:=RecvBufSize;
end;

procedure TMulticastSocket.SetSendBufSize(SendBufSize: integer);
begin
    if Actived=True then
        Exit;

  //Buffer Size Valid?
  if not ((SendBufSize <= MAXBUFFERSIZE) and (SendBufSize >= MINBUFFERSIZE)) then
    Exit;
  fSendBufSize:=SendBufSize;
end;

function TMulticastSocket.LocalIPValid(var LocalIP:String): Boolean;
var
  i : integer;
  slLocalIPs : TStringList;
begin
  Result := False;
  slLocalIPs := TStringList.Create;
  Self.LocalIPs(slLocalIPs);
  if slLocalIPs.Count = 0 then
  begin
    slLocalIPs.Free;
    Exit;
  end;

  if LocalIP = '' then
  begin
    LocalIP := slLocalIPs[0]; //Default Interface
    Result := True;
  end else
    for i:=0 to slLocalIPs.Count-1 do
      if Trim(slLocalIPs[i]) = Trim(LocalIP) then
      begin
        Result := True;
        Break;
      end;
  slLocalIPs.Free;
end;

procedure TMulticastSocket.SetLocalIP(addr: String);
begin
    if Actived=True then
        Exit;

    //Local IP set valid?
    if not LocalIPValid(addr) then
        Exit;
    fLocalIP:=addr;
end;

procedure TMulticastSocket.LocalIPs(slIPs: TStringList);
var
  strLocalHost : string;
  pHE : PHostent;
  pInAd : PAPInAddr;
  saLocal : TSockAddr;
  i : integer;
begin
  SetLength(strLocalHost, 255);
  if GetHostName(PChar(strLocalHost), 254) = SOCKET_ERROR then
    Exit;

  pHE := GetHostByName(PChar(strLocalHost));
  pInAd := PAPInAddr(pHE^.h_addr_list);
  saLocal.sin_addr := (pInAd^[0]^);
  i := 0;
  while True do
  begin
    slIPs.Add(inet_ntoa(saLocal.sin_addr));
    i := i + 1;
    if(pInAd^[i] <> nil) then
      saLocal.sin_addr := (pInAd^[i]^)   //local host
    else
      break;
  end;
end;

(*
procedure TMulticastSocket.LocalMAC(slMac: TStringList);
var
  ncb : TNCB;
  adapt : TASTAT;
  lanaEnum : TLanaEnum;
  i, j : integer;
  strPart, strMac : string;
begin
  FillChar(ncb, SizeOf(TNCB), 0);
  ncb.ncb_command := Char(NCBEnum);
  ncb.ncb_buffer := PChar(@lanaEnum);
  ncb.ncb_length := SizeOf(TLanaEnum);
  Netbios(@ncb);

  for i := 0 to integer(lanaEnum.length)-1 do
  begin
    FillChar(ncb, SizeOf(TNCB), 0);
    ncb.ncb_command := Char(NCBReset);
    ncb.ncb_lana_num := lanaEnum.lana[i];
    Netbios(@ncb);

    FillChar(ncb, SizeOf(TNCB), 0);
    ncb.ncb_command := Chr(NCBAstat);
    ncb.ncb_lana_num := lanaEnum.lana[i];
    ncb.ncb_callname := '*               ';
    ncb.ncb_buffer := PChar(@adapt);
    ncb.ncb_length := SizeOf(TASTAT);
    if Netbios(@ncb) = Chr(0) then
    begin
      strMac := '';
      for j := 0 to 5 do
      begin
        strPart := IntToHex(integer(adapt.adapter.adapter_address[j]), 2);
        strMac := strMac + strPart + '-';
      end;
      SetLength(strMac, Length(strMac)-1);
      slMac.Add(strMac);
    end;
  end;
end;*)

procedure TMulticastSocket.SetActived(const Value: Boolean);
begin
    if Value=fActived then
    //状态未发生变化
        Exit;
    if Value then
        Connect
    else
        DisConnect;
end;

function TMulticastSocket.Connect: Boolean;
begin
    Result:=(AddToGroup=0);
    if not Result then
        Exit;
    if CanRead and Assigned(fOnRecv) then
        StartReceive;
    fActived:=Result;
end;

function TMulticastSocket.DisConnect: Boolean;
begin
    Result:=Close;
    if Result then
        fActived:=False;
end;

procedure TMulticastSocket.SetOnRecv(const Value: TUDPOnRecv);
begin
    if Actived and Assigned(fOnRecv) then
    //事件已经在运行了
        Exit;
    fOnRecv := Value;
    if Actived then
    //已经激活但未设置事件
        StartReceive;
end;

procedure TMulticastSocket.SetTTL(const Value: integer);
begin
    if Actived
        or (Value>MAX_MULTICAST_TTL)
        or (Value<0) then
        Exit;
    fTTL := Value;
end;

function  TMulticastSocket.EnabledListen : Boolean;
var
    MCReq : TIP_mreq;
begin
    Result:=False;

    {接收数据缓冲区大小}
    if SetSockOpt(fSock, SOL_SOCKET, SO_RCVBUF, @fRecvBufSize, SizeOf(integer)) = SOCKET_ERROR then
    begin
      CloseSocket(fSock);
      Exit;
    end;

    {加入多址广播组}
    MCReq.imr_multiaddr.S_addr := Inet_Addr(PChar(fGroupAddress));
    MCReq.imr_interface.S_addr := Inet_Addr(PChar(fLocalIP));
    if SetSockOpt(fSock, IPPROTO_IP, IP_ADD_MEMBERSHIP, @MCReq,
                  SizeOf(TIP_mreq)) = SOCKET_ERROR then
    begin
        CloseSocket(fSock);
        Exit;
    end;
    fMCReq := MCReq;

    if Actived and Assigned(fOnRecv) then
        StartReceive;

    Result:=True;
end;

function TMulticastSocket.EnabledSend: Boolean;
var
  SockAddrLocal, SockAddrRemote : TSockAddr;
begin
    Result:=False;

    FillChar(SockAddrLocal, SizeOf(SockAddrLocal), 0);
    SockAddrLocal.sin_family := AF_INET;
    SockAddrLocal.sin_port := htons(fGroupPort);
    SockAddrLocal.sin_addr.S_addr := Inet_Addr(PChar(fLocalIP));

    {发送数据缓冲区大小}
    if SetSockOpt(fSock, SOL_SOCKET, SO_SNDBUF, @fSendBufSize, SizeOf(integer)) = SOCKET_ERROR then
    begin
      CloseSocket(fSock);
      Exit;
    end;
    {IP multicast output interface}
    if SetSockOpt(fSock, IPPROTO_IP, IP_MULTICAST_IF, @(SockAddrLocal.sin_addr),
                  SizeOf(In_Addr)) = SOCKET_ERROR then
    begin
        CloseSocket(fSock);
        Exit;
    end;
    {设置Time To Livw}
    if SetSockOpt(fSock, IPPROTO_IP, IP_MULTICAST_TTL, @fTTL, SizeOf(integer)) = SOCKET_ERROR then
    begin
        CloseSocket(fSock);
        Exit;
    end;

    {设置发送的目的位置到fAddrTo中}
    FillChar(SockAddrRemote, SizeOf(SockAddrRemote), 0);
    SockAddrRemote.sin_family := AF_INET;
    SockAddrRemote.sin_port := htons(fGroupPort);
    SockAddrRemote.sin_addr.S_addr := Inet_Addr(PChar(fGroupAddress));
    fAddrTo := SockAddrRemote;

    Result:=True;
end;

procedure TMulticastSocket.DisabledListen;
begin
    SetSockOpt(fSock, IPPROTO_IP, IP_DROP_MEMBERSHIP, @fMCReq, SizeOf(fMCReq));
end;

{ TUDPRecvThd }

constructor TUDPRecvThd.Create(var Socks: TSocket; OnRecv: TUDPOnRecv;
  BufSize: integer);
begin
    fSocks := Socks;
    fOnRecv := OnRecv;
    fBufSize := BufSize;
    FreeOnTerminate := True;
    inherited Create(False);
end;

procedure TUDPRecvThd.Execute;
var
    readFDs : TFDSet;
    nRecved, nAddrLen: integer;
    buf : array [0..MAXBUFFERSIZE] of Byte;
    SockFrom : TSockAddr;
begin
    Priority := tpHighest;
    while not Terminated do
    begin
        nAddrLen := SizeOf(SockFrom);
        FD_ZERO(readFDs);
        FD_SET(fSocks, readFDs);
        Select(0, @readFDs, nil, nil, nil);
        if FD_ISSET(fSocks, readFDs) then
        begin
            nRecved := RecvFrom(fSocks, buf, fBufSize, 0, SockFrom, nAddrLen);
            if Assigned(fOnRecv) then
            fOnRecv(@buf, nRecved, string(Inet_Ntoa(SockFrom.sin_addr)),
                Cardinal(Ntohs(SockFrom.sin_port)));
        end;
    end;
end;

initialization
  if WSAStartup(MakeWord(2,0), wsData)<>0 then
    raise Exception.Create('Cannot use the socket service!');

finalization
  WSACleanup;

end.

你可能感兴趣的:(多址广播控件)