`
hulunberbus
  • 浏览: 858324 次
文章分类
社区版块
存档分类
最新评论

多址广播控件

 
阅读更多

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.

分享到:
评论

相关推荐

    WINDOWS网络编程技术

    Winsock API第5章 网络原理和协议895.1 协议的特征895.1.1 面向消息895.1.2 面向连接和无连接915.1.3 可靠性和次序性915.1.4 从容关闭925.1.5 广播数据925.1.6 多播数据925.1.7 服务质量925.1.8 部分消息935.1.9 ...

    windows网络编程技术

    Winsock API第5章 网络原理和协议895.1 协议的特征895.1.1 面向消息895.1.2 面向连接和无连接915.1.3 可靠性和次序性915.1.4 从容关闭925.1.5 广播数据925.1.6 多播数据925.1.7 服务质量925.1.8 部分消息935.1.9 ...

    Windows网络编程(PDF).rar

    895.1 协议的特征 895.1.1 面向消息 895.1.2 面向连接和无连接 915.1.3 可靠性和次序性 915.1.4 从容关闭 925.1.5 广播数据 925.1.6 多播数据 925.1.7 服务质量 925.1.8 部分消息 935.1.9 路由选择的考虑 935.1.10 ...

    windows网络编程(PDF)

    Winsock API第5章 网络原理和协议895.1 协议的特征895.1.1 面向消息895.1.2 面向连接和无连接915.1.3 可靠性和次序性915.1.4 从容关闭925.1.5 广播数据925.1.6 多播数据925.1.7 服务质量925.1.8 部分消息935.1.9 ...

    Windows网络编程

    895.1 协议的特征 895.1.1 面向消息 895.1.2 面向连接和无连接 915.1.3 可靠性和次序性 915.1.4 从容关闭 925.1.5 广播数据 925.1.6 多播数据 925.1.7 服务质量 925.1.8 部分消息 935.1.9 路由选择的考虑 935.1.10 ...

    windows网络编程

    Winsock API第5章 网络原理和协议895.1 协议的特征895.1.1 面向消息895.1.2 面向连接和无连接915.1.3 可靠性和次序性915.1.4 从容关闭925.1.5 广播数据925.1.6 多播数据925.1.7 服务质量925.1.8 部分消息935.1.9 ...

    《Windows网络编程技术》.rar

    Winsock API第5章 网络原理和协议895.1 协议的特征895.1.1 面向消息895.1.2 面向连接和无连接915.1.3 可靠性和次序性915.1.4 从容关闭925.1.5 广播数据925.1.6 多播数据925.1.7 服务质量925.1.8 部分消息935.1.9 ...

    《WINDOWS网络编程技术》

    834.4 平台和性能问题 864.5 小结 87第二部分 Winsock API第5章 网络原理和协议 895.1 协议的特征 895.1.1 面向消息 895.1.2 面向连接和无连接 915.1.3 可靠性和次序性 915.1.4 从容关闭 925.1.5 广播数据 ...

Global site tag (gtag.js) - Google Analytics