⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 hxudp.pas

📁 非常好的UDP穿透Socks5的实例源码教程
💻 PAS
📖 第 1 页 / 共 2 页
字号:
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 + -