📄 hxudp.pas
字号:
unit hxUdp;
interface
uses
Windows, Classes, SysUtils, WinSock, WinSock2;
const
DEFAULT_SENDBUF_SIZE = 8192;
DEFAULT_RECVBUF_SIZE = 8192;
type
{代理服务器验证类型}
TAuthenType = (atNone, atUserPass);
ThxUDPRecvThread = class;
ThxUDPSocket = class;
TProxyInfo = record
Enabled: Boolean; //是否使用代理
Address: string; //代理服务器地址
Port: Integer; //代理服务器端口号
Username: string; //代理服务器验证用户名
Password: string; //密码
end;
TUDPException = class(Exception);
TPeerInfo = record
PeerIP: string;
PeerPort: integer;
end;
{ utInit: 初始化包括设置缓冲区 utSend:发送数据 utRecv: 接收数据 utClose:关闭Socket}
TUDPErrorType = (utInit, utSend, utRecv, utClose);
TUDPErrorEvent = procedure(Sender: TObject; ErrorType: TUDPErrorType;
var ErrorCode: Integer) of object;
{ 读数据事件 }
TUDPReadEvent = procedure(UDPSocket: ThxUDPSocket; const PeerInfo: TPeerInfo) of object;
//主要的UDP类
ThxUDPSocket = class(TObject)
private
FSocket: TSocket;
FPort: integer;
//错误处理事件
FOnSocketError: TUDPErrorEvent;
//读数据事件
FOnDataRead: TUDPReadEvent;
//发送和接受缓冲大小
FSendBufSize: Integer;
FRecvBufSize: Integer;
//记录接受到数据的远程机器的信息
FPeerInfo: TPeerInfo;
//可以在这段时间进行一些客户清理工作,得到数据到达时间
FTimeOut: Longword;
FOnTimeOut: TThreadMethod;
//判断是否打开了套接字
FActive: Boolean;
FBroadcast: Boolean;
FProxyInfo: TProxyInfo;
//使用代理时保持连接的Tcp Socket
FTcpSocket: TSocket;
//代理服务器上的Udp映射地址信息
FUdpProxyAddr: TSockAddrIn;
//得到和设置缓冲大小的函数
function GetSendBufSize: Integer;
function GetRecvBufSize: Integer;
procedure SetSendBufSize(Value: Integer);
procedure SetRecvBufSize(Value: Integer);
procedure SetActive(Value: Boolean);
procedure SetTimeOut(Value: Longword);
function InitSocket: Boolean;
procedure FreeSocket;
procedure DoActive(Active: boolean);
procedure DataReceive;
//连接代理服务器
function ConnectToProxy: Boolean;
//Tcp握手
function Handclasp(Socket: TSocket; AuthenType: TAuthenType): Boolean;
//建立Udp映射通道
function MapUdpChannel(Socket: TSocket): Boolean;
//通过Proxy发送数据
function SendByProxy(Socket: TSocket; var buf; len: Integer; RemoteIP: string;
RemotePort: Integer): Integer;
//从Proxy接收数据
function RecvByProxy(Socket: TSocket; var buf; len: Integer; RemoteIP: string;
RemotePort: Integer): Integer;
protected
FUdpRecvThread: ThxUdpRecvThread;
public
constructor Create;
destructor Destroy; override;
//发送缓冲区数据
function SendBuf(var Buf; Size: Integer; IP: string; Port: Integer): Boolean;
//发送文本
function SendText(Text: string; IP: string; Port: integer): Boolean;
//两个发送广播消息的函数
function BroadcastBuf(var Buf; Size: Integer; Port: Integer): Boolean;
function BroadcastText(Text: string; Port: Integer): Boolean;
//接收函数
function RecvBuf(var Buf; Size: Integer; IP: string; Port: Integer): Boolean;
//接受到远程数据的Client信息
property PeerInfo: TPeerInfo read FPeerInfo;
//发送和接收缓冲区大小
property SendBufSize: Integer read GetSendBufSize write SetSendBufSize;
property RecvBufSize: Integer read GetRecvBufSize write SetRecvBufSize;
//监听端口
property Port: Integer read FPort write FPort;
//等待数据超时间 默认是$FFFFFFFF;
property TimeOut: DWORD read FTimeOut write SetTimeOut;
//打开套接字
property Active: Boolean read FActive write SetActive;
//是否可以广播
property EnableBroadcast: Boolean read FBroadcast write FBroadcast;
//代理配置
property ProxyInfo: TProxyInfo read FProxyInfo write FProxyInfo;
//有数据到达的事件
property OnDataRead: TUdpReadEvent read FOnDataRead write FOnDataRead;
//套接字发生错误事件
property OnSocketError: TUdpErrorEvent read FOnSocketError write FOnSocketError;
//接受数据发生超时
property OnTimeOut: TThreadMethod read FOnTimeOut write FOnTimeOut;
end;
ThxUdpRecvThread = class(TThread)
private
FSocket: ThxUdpSocket;
FEvent: WSAEvent;
//接受到数据的事件
FOnDataRecv: TThreadMethod;
procedure InitEvent;
procedure FreeEvent;
protected
procedure Execute; override;
public
constructor Create(CreateSuspended: Boolean; AUdpSocket: ThxUdpSocket);
destructor Destroy; override;
property OnDataRecv: TThreadMethod read FOnDataRecv write FOnDataRecv;
procedure Stop;
end;
implementation
{ ThxUDPSocket }
function ThxUDPSocket.BroadcastBuf(var Buf; Size, Port: Integer): Boolean;
var
ret, ErrorCode: Integer;
saRemote: TSockAddrIn;
begin
Result:= False;
saRemote.sin_family:= AF_INET;
saRemote.sin_port:= htons(Port);
saRemote.sin_addr.S_addr:= htonl(INADDR_BROADCAST);
if FProxyInfo.Enabled then
ret:= SendByProxy(FSocket, Buf, Size, inet_ntoa(saRemote.sin_addr), ntohs(saRemote.sin_port))
else
ret:= sendto(FSocket, Buf, Size, 0, saRemote, SizeOf(saRemote));
if ret = SOCKET_ERROR then
begin
ErrorCode:= GetLastError;
if ErrorCode <> WSAEWOULDBLOCK then
begin
if Assigned(FOnSocketError) then
FOnSocketError(Self, utSend, ErrorCode);
if ErrorCode <> 0 then
raise TUDPException.CreateFmt('广播数据时出错。错误码是%d', [ErrorCode]);
end;
end
else
Result:= True;
end;
function ThxUDPSocket.BroadcastText(Text: string; Port: Integer): Boolean;
begin
Result:= BroadcastBuf(Text[1], Length(Text), Port);
end;
constructor ThxUDPSocket.Create;
var
WSAData: TWSAData;
begin
FActive:= False;
FPort:= 0;
FillChar(FPeerInfo, SizeOf(TPeerInfo), 0);
FSendBufSize:= DEFAULT_SENDBUF_SIZE;
FRecvBufSize:= DEFAULT_RECVBUF_SIZE;
FSocket:= INVALID_SOCKET;
FUdpRecvThread:= nil;
FTimeOut:= $FFFFFFFF;
FBroadcast:= False;
FTcpSocket:= INVALID_SOCKET;
if WSAStartup(MakeWord(2, 2), WSAData) <> 0 then
raise TUDPException.Create('本程序需要WinSock2,该机器上的Socket版本太低!');
end;
destructor ThxUDPSocket.Destroy;
begin
if FActive then
DoActive(False);
if FTcpSocket <> INVALID_SOCKET then
closesocket(FTcpSocket);
if WSACleanup <> 0 then
MessageBox(0, 'Socket清理失败!', '错误', MB_OK + MB_ICONERROR);
inherited Destroy;
end;
procedure ThxUDPSocket.DoActive(Active: boolean);
var
ErrorCode: Integer;
begin
if Active = True then
begin
if InitSocket then
begin
FActive:= True;
try
SetSendBufSize(FSendBufSize);
SetRecvBufSize(FRecvBufSize);
FUdpRecvThread:= ThxUDPRecvThread.Create(True, Self);
FUdpRecvThread.FOnDataRecv:= DataReceive;
FUdpRecvThread.Resume;
except
DoActive(False);
raise TUDPException.Create('建立监听线程发生错误!');
end;
end
else
begin
ErrorCode:= GetLastError;
if Assigned(FOnSocketError) then
FOnSocketError(Self, utInit, ErrorCode);
if ErrorCode <> 0 then
raise TUDPException.CreateFmt('初始化套接字发生错误,错误码是%d', [ErrorCode]);
end;
end
else // 关闭套接字
begin
if Assigned(FUDPRecvThread) then
begin
FUdpRecvThread.Stop;
FreeAndNil(FUDPRecvThread);
end;
FreeSocket;
FActive:= False;
end;
end;
procedure ThxUDPSocket.FreeSocket;
begin
if FSocket <> INVALID_SOCKET then
begin
closesocket(FSocket);
FSocket:= INVALID_SOCKET;
end;
if FTcpSocket <> INVALID_SOCKET then
begin
closesocket(FTcpSocket);
FTcpSocket:= INVALID_SOCKET;
end;
end;
function ThxUDPSocket.GetRecvBufSize: Integer;
begin
Result:= FRecvBufSize;
end;
function ThxUDPSocket.GetSendBufSize: Integer;
begin
Result:= FSendBufSize;
end;
function ThxUDPSocket.InitSocket: Boolean;
var
saLocal: TSockAddrIn;
bReLinten: Boolean;
begin
Result:= False;
FSocket:= WSASocket(AF_INET, SOCK_DGRAM, 0, nil, 0, WSA_FLAG_OVERLAPPED);
if FSocket = INVALID_SOCKET then
Exit;
//设置在TIME_WAIT状态下可以再次在相同的端口上监听
{
if FPort = 0 then
begin
Result:= True;
Exit;
end;
}
bReLinten:= True;
if setsockopt(FSocket, SOL_SOCKET, SO_REUSEADDR, @bReLinten, SizeOf(bReLinten)) <> 0 then
Exit;
if setsockopt(FSocket, SOL_SOCKET, SO_BROADCAST, @FBroadcast, SizeOf(Integer)) <> 0 then
Exit;
saLocal.sin_family:= AF_INET;
saLocal.sin_port:= htons(FPort);
saLocal.sin_addr.S_addr:= INADDR_ANY;
if bind(FSocket, @saLocal, SizeOf(saLocal)) = SOCKET_ERROR then
begin
FreeSocket;
Exit;
end;
//有代理时需先建立Udp映射通道
if FProxyInfo.Enabled then
begin
if not ConnectToProxy then
Exit;
end;
Result:= True;
end;
procedure ThxUDPSocket.DataReceive;
begin
if Assigned(FOnDataRead) then
FOnDataRead(Self, FPeerInfo);
end;
function ThxUDPSocket.RecvBuf(var Buf; Size: Integer; IP: string; Port: Integer): Boolean;
var
saRemote: TSockAddrIn;
ret, fromlen: Integer;
ErrorCode: Integer;
begin
Result:= False;
saRemote.sin_family:= AF_INET;
saRemote.sin_addr.S_addr:= inet_addr(PChar(IP));
saRemote.sin_port:= htons(Port);
fromlen:= SizeOf(saRemote);
if FProxyInfo.Enabled then
ret:= RecvByProxy(FSocket, Buf, Size, IP, Port)
else
ret:= recvfrom(FSocket, Buf, Size, 0, saRemote, fromlen);
with FPeerInfo do
begin
PeerIP:= inet_ntoa(saRemote.sin_addr);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -