📄 shudpsocket.pas
字号:
unit SHUDPSocket;
interface
uses
SysUtils, Windows, Winsock2, Classes;
const
DEFAULT_SENDBUF_SIZE = 8192; //系统默认的缓冲区大小
DEFAULT_RECVBUF_SIZE = 8192; //系统默认的缓冲区大小
type
TUDPRecvThread = class;
TSHUDPSocket = class;
TUDPException = class(Exception);
//usInit包括设置缓冲区长度
//usSend是发送数据的时候
//usRecv是接受数据的时候
//usClose是关闭套接字的时候
TPeerInfo = record
PeerIP: string; //接受到的数据包的IP
PeerPort: integer;
end;
TUDPErrorClass = (usInit, usSend, usRecv, usClose);
TUDPErrorEvent = procedure(UDPSocket: TSHUDPSocket; ErrorClass:
TUdpErrorClass; var ErrorCode: integer) of object;
TUDPReadEvent = procedure(UDPSocket: TSHUDPSocket; const PeerInf: TPeerInfo)
of object;
//主要的UDP类
TSHUDPSocket = class(TComponent)
private
//套接字
FSocket: TSocket;
//绑定端口号
FPort: integer;
//错误处理事件
FOnSocketError: TUDPErrorEvent;
//读数据事件
FOnDataRead: TUDPReadEvent;
//发送和接受缓冲大小
FSendBufSize: integer;
FRecvBufSize: integer;
//记录接受到数据的远程机器的信息
FPeerInfo: TPeerInfo;
//发送数据的套接字
FAddrOut: TSockAddrIn;
//可以在这段时间进行一些客户清理工作
//得到数据到达时间
FWaitForTime: Longword;
FOnTimeOut: TThreadMethod;
//判断是否打开了套接字
FActive: boolean;
//是否广播
FBroadcast: boolean;
//得到和设置缓冲大小的函数
function GetSendBufSize: integer;
function GetRecvBufSize: integer;
procedure SetSendBufSize(Value: integer);
procedure SetRecvBufSize(value: integer);
procedure SetActive(value: boolean);
procedure SetWaitForTime(Value: Longword);
procedure DoActive(Active: boolean);
procedure SetBroadcast(const Value: boolean);
function GetVer: string;
protected
FUDPRecvThread: TUDPRecvThread;
procedure Loaded; override;
procedure ReadData; virtual; //供后代继承重新覆盖该事件
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
//两个普通的发送函数
function SendBuf(var buf; Size: integer; IP: string; Port: integer):
boolean;
function SendText(Text: string; IP: string; Port: integer): boolean;
//两个发送广播的函数
function SendBroadBuf(var buf; Size: integer; Port: integer): boolean;
function SendBroadText(Text: string; Port: integer): boolean;
//接受函数
function RecvBuf(var buf; Size: integer): Integer;
//接受到远程数据的Client信息
property RecvInf: TPeerInfo read FPeerInfo;
published
//发送和接受缓冲区大小
property SendBufSize: integer read GetSendBufSize write SetSendBufSize
default DEFAULT_SENDBUF_SIZE;
property RecvBufSize: integer read GetRecvBufSize write SetRecvBufSize
default DEFAULT_RECVBUF_SIZE;
//监听端口
property Port: integer read FPort write FPort default 0;
//等待数据超时间 默认是$FFFFFFFF;
property WaitForTime: DWORD read FWaitForTime write SetWaitForTime default
$FFFFFFFF;
//打开套接字
property Active: boolean read FActive write SetActive default false;
//
//5种事件类型
//
//有数据到达的事件
property OnDataRead: TUDPReadEvent read FOnDataRead write FOnDataRead;
//套接字发生错误事件
property OnSocketError: TUDPErrorEvent read FOnSocketError write
FOnSocketError;
//接受数据发生超时
property OnTimeOut: TThreadMethod read FOnTimeOut write FOnTimeOut;
//是否广播数据
property Broadcast: boolean read FBroadcast write SetBroadcast default
False;
property Ver: string read GetVer;
end;
TUDPRecvThread = class(TThread)
private
FSocket: TSHUDPSocket;
FEvent: WSAEvent;
//接受到数据的事件
FOnDataRecv: TThreadMethod;
procedure InitEvent;
procedure FreeEvent;
protected
procedure Execute; override;
public
property OnDataRecv: TThreadMethod read FOnDataRecv write FOnDataRecv;
constructor Create(AUdpSocket: TSHUDPSocket);
destructor Destroy; override;
procedure Stop;
end;
procedure Register;
implementation
{ TUdpSocket }
uses
SHSocket;
procedure Register;
begin
RegisterComponents('SHNetTool', [TSHUDPSocket]);
end;
constructor TSHUDPSocket.Create(AOwner: TComponent);
begin
//初始化所有的参数
inherited Create(AOwner);
FActive := False;
FPort := 0;
FillChar(FPeerInfo, SizeOf(FPeerInfo), 0);
FSendBufSize := DEFAULT_SENDBUF_SIZE;
FRecvBufSize := DEFAULT_RECVBUF_SIZE;
FSocket := INVALID_SOCKET;
FUdpRecvThread := nil;
FWaitForTime := $FFFFFFFF;
FBroadcast := False;
end;
destructor TSHUDPSocket.Destroy;
begin
if FActive then
DoActive(False);
inherited Destroy;
end;
procedure TSHUDPSocket.DoActive(Active: boolean);
var
iError: Integer;
begin
if Active and (FSocket = INVALID_SOCKET) then //开始初始化套接字
begin
FSocket := SHSocket.InitUDPSocket(FPort);
if FSocket <> INVALID_SOCKET then
begin
FActive := True;
try
SetBroadcast(FBroadCast);
SetSendBufSize(FSendBufSize);
SetRecvBufSize(FRecvBufSize);
FUDPRecvThread := TUdpRecvThread.Create(Self);
FUDPRecvThread.FOnDataRecv := ReadData;
FUDPRecvThread.Resume;
except
DoActive(False);
raise TUDPException.Create('建立监听线程发生错误...');
end;
end
else
begin
iError := WSAGetLastError();
if Assigned(FOnSocketError) then
FOnSocketError(Self, usInit, iError);
if iError <> 0 then
raise TUDPException.CreateFmt('初始化套接字发生错误,错误代码是%d',
[iError]);
end;
end
else //关闭套接字
begin
if Assigned(FUdpRecvThread) then
begin
FUdpRecvThread.Stop;
FreeAndNil(FUdpRecvThread);
end;
FreeSocket(FSocket);
FActive := False;
end;
end;
function TSHUDPSocket.GetRecvBufSize: integer;
begin
if FActive then
begin
Result := SHSocket.GetRecvBufSize(FSocket);
FRecvBufSize := Result;
end
else
Result := FRecvBufSize;
end;
function TSHUDPSocket.GetSendBufSize: integer;
begin
if FActive then
begin
Result := SHSocket.GetSendBufSize(FSocket);
FSendBufSize := Result;
end
else
Result := FSendBufSize;
end;
function TSHUDPSocket.GetVer: string;
const
TAB = #13#10;
begin
MessageBox(0,
PChar('SHUDPSocket 2.0A' + TAB + '作者:孙辉 EMAIL:sunhuiNO1@hotmail.com'),
'版本',
MB_ICONINFORMATION);
Result := 'SHUDPSocket 2.0A';
end;
procedure TSHUDPSocket.Loaded;
begin
inherited Loaded;
SetActive(FActive);
end;
procedure TSHUDPSocket.ReadData;
begin
if Assigned(FOnDataRead) then
FOnDataRead(Self, FPeerInfo);
end;
function TSHUDPSocket.RecvBuf(var buf; Size: integer): Integer;
var
AddrIn: TSockAddrIn;
iRc, iAddr: integer;
iError: Integer;
begin
iAddr := SizeOf(AddrIn);
iRc := RecvFrom(FSocket, buf, Size, 0, AddrIn, iAddr);
with FPeerInfo do
begin
PeerPort := ntohs(AddrIn.sin_port);
PeerIP := inet_ntoa(AddrIn.sin_addr);
end;
Result := iRc;
if iRc = SOCKET_ERROR then
begin
iError := WSAGetLastError();
if iError <> WSAEWOULDBLOCK then //缓冲区满的错误代码
begin
if Assigned(FOnSocketError) then
FOnSocketError(Self, usRecv, iError);
if iError <> 0 then
raise TUDPException.CreateFmt('接受数据发生错误,错误代码是%d',
[iError]);
end;
end;
end;
function TSHUDPSocket.SendBroadBuf(var buf; Size, Port: integer): boolean;
begin
if not FBroadcast then
SetBroadcast(True);
Result := SendBuf(buf, size, '255,255,255,255', Port);
end;
function TSHUDPSocket.SendBroadText(Text: string; Port: integer): boolean;
begin
Result := SendText(Text, '255,255,255,255', Port);
end;
function TSHUDPSocket.SendBuf(var buf; Size: integer; IP: string;
Port: integer): boolean;
var
iRc, iError: integer;
begin
Result := False;
FAddrOut.sin_family := AF_INET;
FAddrOut.sin_port := htons(Port);
FAddrOut.sin_addr.S_addr := inet_addr(pchar(IP));
if (not FBroadcast) and (FAddrOut.sin_addr.S_addr = INADDR_NONE) then
raise TUDPException.Create('无效的远程IP地址...');
iRc := SendTo(FSocket, buf, Size, 0, FAddrOut, SizeOf(FAddrOut));
if iRc = SOCKET_ERROR then
begin
iError := WSAGetLastError();
if iError <> WSAEWOULDBLOCK then
begin
if Assigned(FOnSocketError) then
FOnSocketError(Self, usSend, iError);
if iError <> 0 then
raise TUDPException.CreateFmt('发送数据发生错误,错误代码是%d',
[iError]);
end;
end
else
Result := True;
end;
function TSHUDPSocket.SendText(Text: string; IP: string; Port: integer):
boolean;
begin
Result := SendBuf(Pointer(Text)^, Length(Text), IP, Port);
end;
procedure TSHUDPSocket.SetActive(value: boolean);
begin
if (csDesigning in ComponentState)
or (csLoading in ComponentState) then
begin
if (FActive <> Value) then
FActive := Value
end
else
begin
DoActive(Value);
end;
end;
procedure TSHUDPSocket.SetBroadcast(const Value: boolean);
var
iError: integer;
begin
if (Value <> FBroadcast)
or (csDesigning in ComponentState)
or (csLoading in ComponentState) then
begin
FBroadcast := Value;
Exit;
end;
if (FSocket <> INVALID_SOCKET) and (not SHSocket.SetBroadCasst(FSocket, Value))
then
begin
iError := WSAGetLastError();
if Assigned(FOnSocketError) then
FOnSocketError(Self, usInit, iError);
if iError <> 0 then
raise TUDPException.CreateFmt(
'设置广播出错,错误代码是%d',
[iError]);
end
else
FBroadCast := Value;
end;
procedure TSHUDPSocket.SetRecvBufSize(value: integer);
begin
if (csDesigning in ComponentState) or (csLoading in ComponentState) then
begin
if (Value <> FRecvBufSize) and (Value >= 0) then
FRecvBufSize := Value;
Exit;
end
else if SHSocket.SetRecvBufSize(FSocket, Value) then
begin
FRecvBufSize := Value;
end
else
raise TUDPException.CreateFmt('设置接受缓冲区出错,错误代码是%d',
[WSAGetLastError()]);
end;
procedure TSHUDPSocket.SetSendBufSize(Value: integer);
begin
if (csDesigning in ComponentState) or (csLoading in ComponentState) then
begin
if (Value <> FSendBufSize) and (Value >= 0) then
FSendBufSize := Value;
Exit;
end
else
begin
if SHSocket.SetSendBufSize(FSocket, Value) then
begin
FSendBufSize := Value;
end
else
raise TUDPException.CreateFmt('设置发送缓冲区出错,错误代码是%d',
[WSAGetLastError()]);
end;
end;
procedure TSHUDPSocket.SetWaitForTime(Value: Longword);
begin
if Value <> FWaitForTime then
begin
FWaitForTime := Value;
end;
end;
{ TUdpRecvThread }
constructor TUDPRecvThread.Create(AUdpSocket: TSHUDPSocket);
begin
inherited Create(true);
FSocket := AUdpSocket;
FEvent := WSA_INVALID_EVENT;
InitEvent;
end;
destructor TUDPRecvThread.Destroy;
begin
if not Terminated then
Stop;
FreeEvent;
inherited Destroy;
end;
procedure TUDPRecvThread.Execute;
var
dwRc: DWORD;
begin
while not Terminated do
begin
dwRc := WSAWaitForMultipleEvents(
1,
@FEvent,
False,
FSocket.FWaitForTime,
False);
if Terminated then
Break;
if (dwRc = WAIT_IO_COMPLETION) or (dwRc = WSA_WAIT_FAILED) then
Break
else
begin
WSAResetEvent(FEvent);
if dwRc = WSA_WAIT_TIMEOUT then
begin
if Assigned(FSocket.FOnTimeOut) then //设置处理多长时间没有收到数据
Synchronize(FSocket.FOnTimeOut);
end
else if Assigned(FOnDataRecv) then
Synchronize(FOnDataRecv);
end;
end;
end;
procedure TUDPRecvThread.FreeEvent;
begin
if FEvent <> WSA_INVALID_EVENT then
begin
WSACloseEvent(FEvent);
FEvent := WSA_INVALID_EVENT;
end;
end;
procedure TUDPRecvThread.InitEvent;
var
iRc: integer;
begin
FEvent := WSACreateEvent();
if FEvent = WSA_INVALID_EVENT then
raise TUDPException.CreateFmt('创建套接字事件句柄出错..,错误代码是%d',
[WSAGetLastError()]);
iRc := WSAEventSelect(FSocket.FSocket, FEvent, FD_READ);
if iRc = SOCKET_ERROR then
raise TUDPException.CreateFmt('设置套接字事件句柄出错..,错误代码是%d',
[WSAGetLastError()]);
end;
procedure TUDPRecvThread.Stop;
begin
Terminate;
SetEvent(FEvent);
WaitFor;
end;
end.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -