下面是一个组播的例子,组播的原理是针对同一组内的所有IP地址只需要发送一份数据。这样就大大的降低了你去循环所有IP地址发送数据的情况,关于连接池的实现我没有写过,请自己查找这方面的资料,这个例子比较简单,仅供参考,如果使用的话可能还需要处理数据的丢失、数据包前后不一致等一些情况:
实现类:
unit UdpSocket;
interface
uses
Classes, SysUtils, WinSock, Windows;
const
DEFAULTBUFFERSIZE = 16384;
MAXBUFFERSIZE = 63488;
MULTICAST_TTL = 10;
type
PIP_mreq = ^TIP_mreq;
TIP_mreq = record
imr_multiaddr : in_addr;
imr_interface : in_addr;
end;
ESocketError = class(Exception);
TSockSytle = (MultCastSend, MultCastRecv);
TUdpRecv = procedure(var Buf;
Len: Integer;
FromIP: string;
FromPort: u_Short) of object;
TUcpRecvThd = class(TThread)
private
FSocket : TSocket;
FBufSize : Integer;
FOnUdpRecv : TUdpRecv;
protected
procedure Execute;
override;
end;
TUcpSocket = class(TObject)
private
class procedure StartSocket();
static;
class procedure StopSocket();
static;
private
FOnUdpRecv : TUdpRecv;
FLocalAddr : String;
FPort : u_Short;
FSocket : TSocket;
FAddrTo : TSockAddr;
FStyle : TSockSytle;
FBufSize : Integer;
FRemoteAddr : String;
FMCReq : TIP_mreq;
FUcpRecvThd : TUcpRecvThd;
private
procedure SetLocalAddr(Value: String);
procedure SetPort(Value: u_Short);
procedure SetSytle(Value: TSockSytle);
procedure SetBufSize(Value: Integer);
procedure SetRemoteAddr(Value: String);
public
function Send(var Buf;
Len: Integer): Boolean;
procedure Busk();
published
property LocalAddr: String read FLocalAddr write SetLocalAddr;
property Port: u_Short read FPort write SetPort;
property Style: TSockSytle write SetSytle;
property BufSize: Integer read FBufSize write SetBufSize;
property RemoteAddr: String read FRemoteAddr write SetRemoteAddr;
property OnUdpRecv: TUdpRecv read FOnUdpRecv write FOnUdpRecv;
public
constructor Create();
destructor Destroy;
override;
end;
implementation
{ TUcpSocket }
procedure TUcpSocket.Busk;
var
pPE : PProtoEnt;
Sock : TSocket;
SockAddrLocal, SockAddrRemote : TSockAddr;
nTTL, nReuseAddr : integer;
MCReq : TIP_mreq;
begin
pPE := GetProtoByName('UDP');
Sock := Socket(AF_INET, SOCK_DGRAM, pPE.p_proto);
if Sock = INVALID_SOCKET then
raise ESocketError.Create('创建Socket失败!');
nReuseAddr := 1;
if SetSockOpt(Sock, SOL_SOCKET, SO_REUSEADDR, @nReuseAddr, SizeOf(integer)) = SOCKET_ERROR then
begin
CloseSocket(Sock);
Exit;
end;
FillChar(SockAddrLocal, SizeOf(SockAddrLocal), 0);
SockAddrLocal.sin_family := AF_INET;
if FStyle = MultCastSend then
SockAddrLocal.sin_port := htons(0)
else
SockAddrLocal.sin_port := htons(Port);
SockAddrLocal.sin_addr.S_addr := Inet_Addr(PChar(FLocalAddr));
if Bind(Sock, SockAddrLocal, SizeOf(SockAddrLocal)) = SOCKET_ERROR then
begin
CloseSocket(Sock);
Exit;
end;
if FStyle = MultCastSend then
begin
//设置发送缓冲大小
if SetSockOpt(Sock, SOL_SOCKET, SO_SNDBUF,
@FBufSize, SizeOf(Integer)) = SOCKET_ERROR then
begin
CloseSocket(Sock);
Exit;
end;
//设置发送时的参数
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;
FillChar(SockAddrRemote, SizeOf(SockAddrRemote), 0);
SockAddrRemote.sin_family := AF_INET;
SockAddrRemote.sin_port := htons(Port);
SockAddrRemote.sin_addr.S_addr := Inet_Addr(PChar(FRemoteAddr));
FAddrTo := SockAddrRemote;
end else
//接收
begin
//设置接收缓冲大小
if SetSockOpt(Sock, SOL_SOCKET, SO_RCVBUF, @fBufSize, SizeOf(integer)) = SOCKET_ERROR then
begin
CloseSocket(Sock);
Exit;
end;
//加入组
MCReq.imr_multiaddr.S_addr := Inet_Addr(PChar(FRemoteAddr));
MCReq.imr_interface.S_addr := Inet_Addr(PChar(FLocalAddr));
if SetSockOpt(Sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, @MCReq,
SizeOf(TIP_mreq)) = SOCKET_ERROR then
begin
CloseSocket(Sock);
Exit;
end;
fMCReq := MCReq;
end;
FSocket := Sock;
if FStyle = MultCastRecv then
begin
FUcpRecvThd.FSocket := FSocket;
FUcpRecvThd.FBufSize := FBufSize;
FUcpRecvThd.FOnUdpRecv := FOnUdpRecv;
FUcpRecvThd.Resume;
end;
end;
constructor TUcpSocket.Create;
begin
FOnUdpRecv := nil;
FLocalAddr := '127.0.0.1';
FPort := 0;
FStyle := MultCastRecv;
FBufSize := DEFAULTBUFFERSIZE;
FUcpRecvThd := TUcpRecvThd.Create(true);
end;
destructor TUcpSocket.Destroy;
begin
CloseSocket(FSocket);
FUcpRecvThd.Free;
inherited;
end;
function TUcpSocket.Send(var Buf;
Len: Integer): Boolean;
begin
Result := false;
if SendTo(FSocket, Buf, Len, MSG_DONTROUTE, FAddrTo,
SizeOf(FAddrTo)) <> SOCKET_ERROR then
Result := true;
end;
procedure TUcpSocket.SetLocalAddr(Value: String);
begin
FLocalAddr := Value;
end;
procedure TUcpSocket.SetBufSize(Value: Integer);
begin
FBufSize := Value;
end;
procedure TUcpSocket.SetPort(Value: u_Short);
begin
FPort := Value;
end;
procedure TUcpSocket.SetRemoteAddr(Value: String);
var
nMCAddr : Cardinal;
begin
FRemoteAddr := Value;
nMCAddr := ntohl(inet_addr(PChar(FRemoteAddr)));
if not ((nMCAddr <= $efffffff) and (nMCAddr >= $e0000100)) then
raise ESocketError.Create('无效的组播地址!');
end;
procedure TUcpSocket.SetSytle(Value: TSockSytle);
begin
FStyle := Value;
end;
class procedure TUcpSocket.StartSocket;
var
WsData: TWSAData;
err: Integer;
begin
err := WSAStartup(MAKEWORD(2, 2), WsData);
if err <> 0 then
raise ESocketError.Create('不能使用SOCKET服务!');
if ( LOBYTE( WsData.wVersion ) <> 2 ) or
( HIBYTE( WsData.wVersion ) <> 2 ) then
raise ESocketError.Create('没有找到所需要的SOCKET版本!');
end;
class procedure TUcpSocket.StopSocket;
begin
WSACleanup;
end;
{ TUcpRecvThd }
procedure TUcpRecvThd.Execute;
var
readFDs : TFDSet;
nRecved, nAddrLen: integer;
Buf : array [0..MAXBUFFERSIZE] of Byte;
SockFrom : TSockAddr;
begin
Priority := tpHighest;
while not Terminateddo
begin
nAddrLen := SizeOf(SockFrom);
FD_ZERO(readFDs);
FD_SET(FSocket, readFDs);
Select(0, @readFDs, nil, nil, nil);
if FD_ISSET(FSocket, readFDs) then
begin
nRecved := RecvFrom(FSocket, buf, FBufSize, 0, SockFrom, nAddrLen);
if Assigned(FOnUdpRecv) then
FOnUdpRecv(Buf, nRecved, string(Inet_Ntoa(SockFrom.sin_addr)),
Cardinal(Ntohs(SockFrom.sin_port)));
end;
end;
end;
initialization
TUcpSocket.StartSocket;
finalization
TUcpSocket.StopSocket;
end.
例子:
unit Demo;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, UdpSocket, WinSock;
const
MULTCASTADDR: String = '225.0.1.177';
MULTCASTPORT: Integer = 10000;
type
TUdpSocketDemo = class(TForm)
edtSendText: TEdit;
meoRecvText: TMemo;
cmdSend: TButton;
cmdInit: TButton;
cmdExit: TButton;
procedure FormClose(Sender: TObject;
var Action: TCloseAction);
procedure cmdExitClick(Sender: TObject);
procedure cmdSendClick(Sender: TObject);
procedure cmdInitClick(Sender: TObject);
private
{ Private declarations }
FMultCastUdpSend: TUcpSocket;
//Send Socket
FMultCastUdpRecv: TUcpSocket;
//Recv Socket
public
{ Public declarations }
procedure OnUdpRecv(var Buf;
Len: Integer;
FromIP: string;
FromPort: u_Short);
end;
var
UdpSocketDemo: TUdpSocketDemo;
implementation
{$R *.dfm}
procedure TUdpSocketDemo.cmdInitClick(Sender: TObject);
begin
FMultCastUdpSend := TUcpSocket.Create;
FMultCastUdpSend.
LocalAddr := '172.18.2.212';
FMultCastUdpSend.
Port := MULTCASTPORT;
FMultCastUdpSend.
Style := MultCastSend;
FMultCastUdpSend.
RemoteAddr := MULTCASTADDR;
FMultCastUdpSend.
Busk;
FMultCastUdpRecv := TUcpSocket.Create;
FMultCastUdpRecv.LocalAddr := '172.18.2.212';
FMultCastUdpRecv.Port := MULTCASTPORT;
FMultCastUdpRecv.Style := MultCastRecv;
FMultCastUdpRecv.RemoteAddr := MULTCASTADDR;
FMultCastUdpRecv.OnUdpRecv := OnUdpRecv;
FMultCastUdpRecv.Busk;
cmdInit.Enabled := false;
end;
procedure TUdpSocketDemo.cmdSendClick(Sender: TObject);
var
Buf: array of Char;
Len: Integer;
begin
Len := Length(edtSendText.Text) + 1;
SetLength(Buf, Len);
StrPCopy(@Buf[0], edtSendText.Text);
FMultCastUdpSend.
Send(Buf, Len);
end;
procedure TUdpSocketDemo.cmdExitClick(Sender: TObject);
begin
Close;
end;
procedure TUdpSocketDemo.FormClose(Sender: TObject;
var Action: TCloseAction);
begin
FMultCastUdpSend.
Free;
end;
procedure TUdpSocketDem
nUdpRecv(var Buf;
Len: Integer;
FromIP: string;
FromPort: u_Short);
begin
meoRecvText.Lines.Add(String(Buf));
end;
end.