首页  编辑  

网络广播

Tags: /超级猛料/Network.网络通讯/其它/   Date Created:

来自:Ironhero, 时间:2002-2-22 10:35:00, ID:934242 [显示:小字体 | 大字体]  

多播的IP地址是:224.0.0.0至239.255.255.255,我想通过组播发送信息到拨号上网的对方,

但我国的IP地址是202开头的(我用猫上网时就这样),也就是说根本不能组播,但看到好

多贴子都说可以,请问各位高手,多播是这回事吗?怎样可以做到?

来自:kucio, 时间:2002-2-22 17:49:00, ID:935651

多播地址和机器的实际IP地址是两码事,互不影响

我曾经做过局域网内的多播程序,但是在Internet上就不知道了

需要注意一点:224.0.0.0--224.0.0.255这些地址被Windows系统占用,我们在程序中

不能再使用

来自:Ironhero, 时间:2002-2-23 9:17:00, ID:936516

谢谢cukio,再次请教您,假如在局域网,那么电脑的IP地址应如何设置呢?

来自:kucio, 时间:2002-2-26 13:42:00, ID:943709

我们的IP是随意设的,但是都在同一网段中,形式如89.0.0.???。如果Client和Server

之间隔有路由的话,路由器必须经过配置多播信息才能通过。

至于多播地址我们约定的是224.1.1.1,server在初始化的时候会按照这个多播地址创

建一个多播组,然后Client初始化时加入这个多播组就可以收多播信息了。

下面的两段程序你研究一下吧(第一个类定义,第二个接受程序)

unit U_UDPSock;

interface

uses

 Classes, SysUtils, WinSock, Windows, NB30;

const

 MINBUFFERSIZE = 2048;

 DEFAULTBUFFERSIZE = 16384;

 MAXBUFFERSIZE = 63488; //62*1024

 MULTICAST_TTL = 10;

type

 TArraySocket = Array Of TSocket;

 PASTAT = ^TASTAT;

 TASTAT = record

   adapter : TAdapterStatus;

   name_buf : TNameBuffer;

 end;

 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

 //Note : Dut to broadcast fragmentation's problem, broadcast message can be at most

 //512 bytes long defined by WinSock, not longer than 1472 by Berkeley Socket

 //not longer than 1468 under MIPS machine

 //So don't send a broadcast message longer than 512 here, no use

 TUDPSockType = (stMultiCastSender, stMultiCastReceiver, stUnicastSender, stUnicastReceiver,

                 stBroadcastSender, stBroadcastReceiver);

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

 TUDPRecvThd = class(TThread)

   private

     fSocks : TArraySocket;

     fSockCount : integer;

     fBufSize : integer;

     fOnRecv : TUDPOnRecv;

   protected

     procedure Execute ; override;

   public

     constructor Create(var Socks : TArraySocket; OnRecv : TUDPOnRecv; BufSize : integer);

 end;

 TUDPSock2 = class(TObject)

   private

     fbSetupReady : Boolean;

     fSockType : TUDPSockType;

     fOnRecv : TUDPOnRecv;

     fSockCount : integer;

     fAddrTo : array of TSockAddr;

     fMCReq : array of TIP_mreq;

     fSocks : TArraySocket;

     fRecvThd : TUDPRecvThd;

     fLocalIP : String;

     fBufSize : integer;

     function LocalIPValid(var LocalIP : string): Boolean;

   public

     property OnRecv : TUDPOnRecv read fOnRecv write fOnRecv;

     constructor Create; ReIntroduce;

     destructor Destroy; Override;

     procedure LocalIPs(slIPs : TStringList);

     procedure LocalMAC(slMac : TStringList);

     procedure StartReceive;

     function Add(RemoteIP : string; Port : u_Short): integer;

     function Setup(udpSockType : TUDPSockType; LocalIP : string = '';

                    BufferSize : integer = DEFAULTBUFFERSIZE) : Boolean;

     function Close : Boolean;

     function Send(index : integer; buffer : Pointer; len : integer) : Boolean;

 end;

implementation

var

 wsData : TWSAData;

procedure TUDPRecvThd.Execute;

var

 readFDs : TFDSet;

 i, 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);

   for i := 0 to fSockCount-1 do

     FD_SET(fSocks[i], readFDs);

   //The first param of select is provided just for

   //compatibility with Berkeley Sockets, no meaning in WinSock

   //Note!!! the select's last param here is nil

   //so it can be blocked forever

   Select(0, @readFDs, nil, nil, nil);

   for i := 0 to fSockCount-1 do

   if FD_ISSET(fSocks[i], readFDs) then

   begin

     nRecved := RecvFrom(fSocks[i], buf, fBufSize, 0, SockFrom, nAddrLen);

     if Assigned(fOnRecv) then

       //Note!!! I didn't call Synchronize here so u can call Terminate and WaitFor

       //but I suggest using Suspend and Free STRONGLY!

       //For the call of select can be blocked forever

       fOnRecv(@buf, nRecved, string(Inet_Ntoa(SockFrom.sin_addr)),

               Cardinal(Ntohs(SockFrom.sin_port)));

   end;

 end;

end;

constructor TUDPRecvThd.Create(var Socks : TArraySocket; OnRecv : TUDPOnRecv; BufSize : integer);

begin

 fSocks := Socks;

 fOnRecv := OnRecv;

 fBufSize := BufSize;

 fSockCount := High(Socks) + 1; //must start with 0, Low(Socks) is always 0

 FreeOnTerminate := True;

 inherited Create(False);

end;

procedure TUDPSock2.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 TUDPSock2.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 TUDPSock2.StartReceive;

begin

 if fRecvThd <> nil then

   Exit;

 if ((fSockType = stUnicastReceiver) or (fSockType = stMulticastReceiver)

      or (fSockType = stBroadcastReceiver)) and (fSockCount > 0) then

   fRecvThd := TUDPRecvThd.Create(fSocks, fOnRecv, fBufSize);

end;

function TUDPSock2.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;

function TUDPSock2.Setup(udpSockType : TUDPSockType; LocalIP : string = '';

                       BufferSize : integer = DEFAULTBUFFERSIZE):Boolean;

begin

 Result := False;

 //Already started?

 if fSockCount > 0 then

   Exit;

 //Local IP set valid?

 if not LocalIPValid(LocalIP) then

   Exit;

 //Buffer Size Valid?

 if not ((BufferSize <= MAXBUFFERSIZE) and (BufferSize >= MINBUFFERSIZE)) then

   Exit;

 fSockType := udpSockType;

 fBufSize := BufferSize;

 fLocalIP := LocalIP;

 fbSetupReady := True;

 Result := True;

end;

function TUDPSock2.Add(RemoteIP : string; Port : u_Short): integer;

var

 nMCAddr : Cardinal;

 nTTL, nReuseAddr : integer;

 Sock : TSocket;

 SockAddrLocal, SockAddrRemote : TSockAddr;

 MCReq : TIP_mreq;

 pPE : PProtoEnt;

begin

 Result := -1;

 //Maximum fds allowed

 if fSockCount = FD_SETSIZE then

   Exit;

 //Already started?

 if (fRecvThd <> nil) or (not fbSetupReady) then

   Exit;

 //Multicast address valid?

 if (fSockType = stMultiCastSender) or (fSockType = stMultiCastReceiver) then

 begin

   nMCAddr := ntohl(inet_addr(PChar(RemoteIP)));

   //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;

 end;

 pPE := GetProtoByName('UDP');

 //Create Socket

 Sock := Socket(AF_INET, SOCK_DGRAM, pPE.p_proto);

 if Sock = INVALID_SOCKET then

   Exit;

 //Reuse the address, according to WinSock help, nReuseAddr must be a BOOL and

 //the fifth param must be SizeOf(integer), but in a sample codes, the fifth is SizeOf(BOOL)

 //faint! I used integer and SizeOf(integer) is also OK

 nReuseAddr := 1;

 if SetSockOpt(Sock, SOL_SOCKET, SO_REUSEADDR, @nReuseAddr, SizeOf(integer)) = SOCKET_ERROR then

 begin

   CloseSocket(Sock);

   Exit;

 end;

 //Set Local Address and bind

 FillChar(SockAddrLocal, SizeOf(SockAddrLocal), 0);

 SockAddrLocal.sin_family := AF_INET;

 if (fSockType = stMultiCastSender) or (fSockType = stUnicastSender)

     or (fSockType = stBroadcastSender) then

   SockAddrLocal.sin_port := htons(0)

 else

   SockAddrLocal.sin_port := htons(Port);

 SockAddrLocal.sin_addr.S_addr := Inet_Addr(PChar(fLocalIP));

 if Bind(Sock, SockAddrLocal, SizeOf(SockAddrLocal)) = SOCKET_ERROR then

 begin

   CloseSocket(Sock);

   Exit;

 end;

 if (fSockType = stMultiCastSender) or (fSockType = stUnicastSender)

    or (fSockType = stBroadcastSender)then

 begin

   //Set Send Buffer Size

   if SetSockOpt(Sock, SOL_SOCKET, SO_SNDBUF, @fBufSize, SizeOf(integer)) = SOCKET_ERROR then

   begin

     CloseSocket(Sock);

     Exit;

   end;

   //Set output interface

   if fSockType = stMultiCastSender then

   begin

     if SetSockOpt(Sock, IPPROTO_IP, IP_MULTICAST_IF, @(SockAddrLocal.sin_addr),

                   SizeOf(In_Addr)) = SOCKET_ERROR then

     begin

       CloseSocket(Sock);

       Exit;

     end;

     nTTL := MULTICAST_TTL;

     if SetSockOpt(Sock, IPPROTO_IP, IP_MULTICAST_TTL, @nTTL, SizeOf(integer)) = SOCKET_ERROR then

     begin

       CloseSocket(Sock);

       Exit;

     end;

   end else //For send, must set the opt SO_BROADCAST

   if fSockType = stBroadcastSender then

     if SetSockOpt(Sock, SOL_SOCKET, SO_BROADCAST, @nReuseAddr, SizeOf(integer))

         = SOCKET_ERROR then

     begin

       CloseSocket(Sock);

       Exit;

     end;

   FillChar(SockAddrRemote, SizeOf(SockAddrRemote), 0);

   SockAddrRemote.sin_family := AF_INET;

   SockAddrRemote.sin_port := htons(Port);

   if fSockType = stBroadcastSender then

     SockAddrRemote.sin_addr.S_addr := htonl(INADDR_BROADCAST)

   else

     SockAddrRemote.sin_addr.S_addr := Inet_Addr(PChar(RemoteIP));

   fSockCount := fSockCount + 1;

   SetLength(fAddrTo, fSockCount);

   fAddrTo[fSockCount-1] := SockAddrRemote;

 end else //UDPReceiver or MulticastReceiver or BroadcastReceiver

 begin

   //Set Receive Buffer Size

   if SetSockOpt(Sock, SOL_SOCKET, SO_RCVBUF, @fBufSize, SizeOf(integer)) = SOCKET_ERROR then

   begin

     CloseSocket(Sock);

     Exit;

   end;

   //Join Group

   if fSockType = stMulticastReceiver then

   begin

     MCReq.imr_multiaddr.S_addr := Inet_Addr(PChar(RemoteIP));

     MCReq.imr_interface.S_addr := Inet_Addr(PChar(fLocalIP));

     if SetSockOpt(Sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, @MCReq,

                   SizeOf(TIP_mreq)) = SOCKET_ERROR then

     begin

         CloseSocket(Sock);

         Exit;

     end;

   end;

   fSockCount := fSockCount + 1;

   if fSockType = stMulticastReceiver then

   begin

     SetLength(fMCReq, fSockCount);

     fMCReq[fSockCount-1] := MCReq;

   end;

 end;

 SetLength(fSocks, fSockCount);

 fSocks[fSockCount-1] := Sock;

 Result := fSockCount - 1;

end;

function TUDPSock2.Close:Boolean;

var

 i : integer;

begin

 Result := False;

 if fSockCount = 0 then

   Exit;

 if (fSockType = stUnicastReceiver) or (fSockType = stMulticastReceiver)

    or (fSockType = stBroadcastReceiver) then

 begin

   //Exception will be? :( I don't know

   if fRecvThd <> nil then

   begin

     fRecvThd.Suspend;

     fRecvThd.Free;

     fRecvThd := nil;

   end;

   if fSockType = stMulticastReceiver then

   for i := 0 to fSockCount - 1 do

     SetSockOpt(fSocks[i], IPPROTO_IP, IP_DROP_MEMBERSHIP, @fMCReq[i], SizeOf(fMCReq[i]));

 end;

 for i := 0 to fSockCount - 1 do

   CloseSocket(fSocks[i]);

 SetLength(fMCReq, 0);

 SetLength(fSocks, 0);

 SetLength(fAddrTo, 0);

 fbSetupReady := False;

 fSockCount := 0;

end;

function TUDPSock2.Send(index : integer; buffer : Pointer; len : integer) : Boolean;

begin

 Result := False;

 if (len < 0) or (index < 0) or (index >= fSockCount) then

   Exit;

 if (fSockType <> stMultiCastSender) and (fSockType <> stUnicastSender)

     and (fSockType <> stBroadcastSender) then

   Exit;

 if SendTo(fSocks[index], buffer^, len, 0{MSG_DONTROUTE}, fAddrTo[index],

           SizeOf(fAddrTo[index])) <> SOCKET_ERROR then

   Result := True;

end;

constructor TUDPSock2.Create;

begin

 fbSetupReady := False;

 fSockCount := 0;

 fRecvThd := nil;

end;

destructor TUDPSock2.Destroy;

begin

 if fSockCount > 0 then

   Self.Close;

end;

initialization

 if WSAStartup(MakeWord(2,0), wsData)<>0 then

   raise Exception.Create('Cannot use the socket service!');

finalization

 WSACleanup;

end.

---------------------------------------------------------------------

unit winclient;

interface

uses

 Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,

 ScktComp, StdCtrls, Buttons, U_UDPSock, WinSock;

type

 TwClient = class(TForm)

   Button1: TButton;

   Memo1: TMemo;

   Edit1: TEdit;

   Edit2: TEdit;

   Button2: TButton;

   procedure Button1Click(Sender: TObject);

   procedure Button2Click(Sender: TObject);

   procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);

 private

   { Private declarations }

   procedure OnMyRecv(buffer: Pointer; len: integer; fromIP: string; fromPort: U_Short);

 public

   { Public declarations }

 end;

var

 wClient: TwClient;

 SockRecv: TUDPSock2;

implementation

{$R *.DFM}

procedure TwClient.OnMyRecv(buffer: Pointer; len: integer; fromIP: string; fromPort: U_Short);

begin

 Memo1.Lines.Add(Format('"%s" From:%s',[StrPas(buffer),fromIP]));

end;

procedure TwClient.Button1Click(Sender: TObject);

begin

 SockRecv := TUDPSock2.Create;

 SockRecv.Setup(stMulticastReceiver);

 SockRecv.Add(Edit1.Text, StrToInt(Edit2.Text));

 SockRecv.OnRecv := OnMyRecv;

 SockRecv.StartReceive;

 Button1.Enabled:=False;

 Button2.Enabled:=True;

end;

procedure TwClient.Button2Click(Sender: TObject);

begin

 Button1.Enabled:=True;

 Button2.Enabled:=False;

 SockRecv.Destroy;

 SockRecv.Free;

end;

procedure TwClient.FormCloseQuery(Sender: TObject; var CanClose: Boolean);

begin

 if Button2.Enabled then Button2.OnClick(Self);

end;

end.

来自:zhanggm, 时间:2002-2-26 15:43:00, ID:944062

实现组播只能在同一网段中进行分段如下:

A类网广播地址:1~126.255.255.255

B类网广播地址:128~191.XXX.255.255

C类网广播地址:192~254.XXX.XXX.255

在相应网段中用相应广播地址进行组播

来自:iamfly, 时间:2002-2-26 16:02:00, ID:944128

拜托,组播(MULTICAST)和广播(BROADCAST)是两回事来的。。。

Ironhero,组播所用到的IP地址是属于保留的,你可以看一些网络基础知识的书,有说到

IP的范围划分,分A到E五类,其中组播用的是D类,而我们在INTERNET上用到的是C类:)

要想实现多播,必须要确保你所使用的网络中的硬件支持(SWITCH和ROUTER之类的),HUB好

像不行:)

具体的实现我也还没试过,有结果了烦请告诉一声:)

来自:Ironhero, 时间:2002-2-28 9:52:00, ID:948131

kucio:我按照您的办法做了,的确如此,但我发现了几个问题:

1、我在发送端设定的端口号,在接收端(客户端)返回的端号好象是一个随机数,并不是

发送端的端口号,设定的端口没起作用。

2、当有多个发送端在同一个多播组(都是一样的发送端口与接收端口)发送数据时,无法

区分是由谁发送的数据,数据搞混了,我发送的是屏幕捕捉的图像数据并在客户端随时显示

在画布上,当写在画布上时数据互相干扰。我已经判断是哪一个IP就写在哪一个画布上。

3、互联网上我也不想测试了,估计行不通,因为与路由器有关,恐怕不是我等能解决得了。

kucio:烦请您再指导指导我,谢谢!

来自:kucio, 时间:2002-2-28 17:26:00, ID:949459

RE:

1、你所设定的端口号实际上就是接收端的端口,而发送端使用的哪个端口是随机选择一个

空闲端口,据说可以使用什么bind命令指定发送端的端口,但具体我也没用过,况且

我想不出这有什么用。

2、判断IP就是很好的方法呀,如果IP不确定的话,你也可以在发送信息中加入一些标志。

来自:张无忌, 时间:2002-7-30 10:23:00, ID:1231236

多播只是LAN上用,INTERNET上没有办法使用,原因是他要利用以太网卡的过滤能力

ADSL和猫都不支持多播,所以在INTERNET上无法使用。

来自:jingtao, 时间:2002-8-11 1:51:00, ID:1256902

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.

来自:zw84611, 时间:2002-12-4 14:41:00, ID:1484870

真好笑,多播和在不在中国有什么关系?

应该说,组播在局域网上实现是很简单的,但是如果在Internet上,需要路由器对组播的支持。

事实上就主机而言,多播的实现和普通UDP的实现无异(TCP不支持组播),只是多加一个:

setsockopt(s,IPPROTO_IP,IP_ADD_MEMBERSHIP,pchar(@mreq),sizeof(mreq))

而已。

给你个例子:

unit udp;

interface

uses

 Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, winsock,

 StdCtrls;

const

 WM_SOCK = WM_USER + 1;     //自定义windows消息

 UDPPORT = 6543;            //设定UDP端口号

 //D类地址224.0.0.0 - 239.255.255.255

 //若为224.0.0.1则本机也能收到,否则本机收不到,其它机器能收到。

 MY_GROUP = '224.0.0.2';

(*

* Argument structure for IP_ADD_MEMBERSHIP and IP_DROP_MEMBERSHIP.

* Delphi5自带的winsock.pas中没有ip_mreq的定义。

*)

type

 ip_mreq = record

   imr_multiaddr: in_addr;  (* IP multicast address of group *)

   imr_interface: in_addr;  (* local IP address of interface *)

 end;

 TIpMReq = ip_mreq;

 PIpMReq = ^ip_mreq;

type

 Tfrmmain = class(TForm)

   Button1: TButton;

   Edit1: TEdit;

   Memo1: TMemo;

   Edit2: TEdit;

   Label1: TLabel;

   Label2: TLabel;

   Label3: TLabel;

   procedure FormCreate(Sender: TObject);

   procedure FormClose(Sender: TObject; var Action: TCloseAction);

   procedure Button1Click(Sender: TObject);

 private

   { Private declarations }

   s: TSocket;

   addr: TSockAddr;

   FSockAddrIn : TSockAddrIn;

   mreq:ip_mreq;

   //利用消息实时获知UDP消息

   procedure ReadData(var Message: TMessage); message WM_SOCK;

 public

   { Public declarations }

   procedure SendData(Content: String);

 end;

var

 frmmain: Tfrmmain;

implementation

{$R *.DFM}

procedure Tfrmmain.FormCreate(Sender: TObject);

var

 TempWSAData: TWSAData;

 //optval: integer;

begin

 Edit1.Text := MY_GROUP;

 // 初始化SOCKET

 if WSAStartup($101, TempWSAData)=1 then

   showmessage('StartUp Error!');

 s := Socket(AF_INET, SOCK_DGRAM, 0);

 if (s = INVALID_SOCKET) then   //Socket创建失败

 begin

     showmessage(inttostr(WSAGetLastError())+'  Socket创建失败');

     CloseSocket(s);

     //exit;

 end;

 //发送方SockAddr绑定

 addr.sin_family := AF_INET;

 addr.sin_addr.S_addr := INADDR_ANY;

 addr.sin_port := htons(UDPPORT);

 if Bind(s, addr, sizeof(addr)) <> 0  then

  begin

    showmessage('bind fail');

  end;

 {optval:= 1;

 if setsockopt(s,SOL_SOCKET,SO_BROADCAST,pchar(@optval),sizeof(optval)) = SOCKET_ERROR then

 begin

  showmessage('无法进行UDP广播');

 end;}

 mreq.imr_multiaddr.S_addr := inet_addr(pchar(MY_GROUP));//htonl(INADDR_ALLHOSTS_GROUP);

 mreq.imr_interface.S_addr := htonl(INADDR_ANY);

 if setsockopt(s,IPPROTO_IP,IP_ADD_MEMBERSHIP,pchar(@mreq),sizeof(mreq)) = SOCKET_ERROR then

 begin

  showmessage('无法进行UDP组播');

 end;

   

 WSAAsyncSelect(s, frmmain.Handle , WM_SOCK, FD_READ);

 //接收端SockAddrIn设定

 FSockAddrIn.SIn_Family := AF_INET;

 FSockAddrIn.SIn_Port := htons(UDPPORT);

 label3.Caption := '端口:'+inttostr(UDPPORT);

end;

procedure Tfrmmain.FormClose(Sender: TObject; var Action: TCloseAction);

begin

 CloseSocket(s);

end;

procedure Tfrmmain.ReadData(var Message: TMessage);

var

 buffer: Array [1..4096] of char;

 len: integer;

 flen: integer;

 Event: word;

 value: string;

begin

 flen:=sizeof(FSockAddrIn);

 Event := WSAGetSelectEvent(Message.LParam);

 if Event = FD_READ then

 begin

     len := recvfrom(s, buffer, sizeof(buffer), 0, FSockAddrIn, flen);

     value := copy(buffer, 1, len);

     Memo1.Lines.add(value)

 end;

end;

procedure Tfrmmain.SendData(Content: String);

var

 value{,hostname}: string;

 len: integer;

begin

 //FSockAddrIn.SIn_Addr.S_addr := INADDR_BROADCAST;

 FSockAddrIn.SIn_Addr.S_addr := inet_addr(pchar(MY_GROUP));

 value := Content;

 len := sendto(s, value[1], Length(value), 0, FSockAddrIn, sizeof(FSockAddrIn));

 if (WSAGetLastError() <> WSAEWOULDBLOCK) and (WSAGetLastError() <> 0) then

   showmessage(inttostr(WSAGetLastError()));

 if len = SOCKET_ERROR then

   showmessage('send fail');

 if len <> Length(value) then

   showmessage('Not Send all');

end;

procedure Tfrmmain.Button1Click(Sender: TObject);

begin

 senddata(Edit2.text);

end;

end.

//源程序:http://www.playicq.com/dispdoc.php?t=27&id=2079