TidUDPClient 通过代理服务器

TidUDPClient thru proxy server

关于下面的一段代码,我有一些相关的问题

var
  UseProxy : Boolean = True;
....
var
  IdUDPClient : TIdUDPClient;
  sText       : string;
begin
  IdUDPClient := TIdUDPClient.Create(nil);
  try
    IdUDPClient.Host := '10.10.10.10';
    IdUDPClient.Port := 5555;
    if UseProxy then begin
      IdUDPClient.TransparentProxy.Enabled := True;
      IdUDPClient.TransparentProxy.Host    := '20.20.20.20';
      IdUDPClient.TransparentProxy.Port    := 8080;
      IdUDPClient.OpenProxy;
    end;
    try
      IdUDPClient.Connect;
    except
      Writeln('Connect Error.');
    end;
    if IdUDPClient.Connected then
      Writeln('Connected')
    else begin
      Writeln('Not Connected');
      Exit;
    end;
    try
      IdUDPClient.Send('Foo');
      try
        sText := IdUDPClient.ReceiveString(1000);
        Writeln('Received: ', sText);
      except
        Writeln('Receive Error.');
      end;
    except
      Writeln('Send Error.');
    end;
    if UseProxy then
      IdUDPClient.CloseProxy;
  finally
    IdUDPClient.Free;
  end;
  Readln;
end.
  1. 为什么 try...except 块没有捕获 UDP 上的错误 客户端,即使主机不可访问,端口也关闭,例如IdUDPClient.Connected 总是 True?
  2. 使用代理时,我不确定上面的实现是否正确 因为如果 UseProxy 是 True IdUDPClient 尝试直接发送 对 10.10.10.10 的请求不是通过代理服务器。我该如何解决这个问题?我做错了什么?

我的代理测试场景如下:

30.30.30.0/24 无法达到 10.10.10.1020.20.20.20 可以。

  1. 如果我的 PC 可以直接访问 UDP 服务器,并且我将假代理服务器(未使用的 IP 和随机端口)连接到客户端。客户端可以访问不应该访问的 UDP 服务器,因为代理已关闭。我怎么不能阻止这种情况?
  1. UDP 是无连接的,因此 Connected 无法告诉您远程对等点是否可达。

    "Connecting" UDP 套接字简单地分配与对等方的本地关联 IP/Port。这样,出站数据包仅发送到该对等点,并且仅接受从该对等点接收的数据包。而已。没有像 TCP 那样的实际连接。

    并且与 TCP 不同,发送 UDP 数据包只是将数据包转储到网络上,不会确认数据包曾经到达对等方。 ack必须在应用层实现。

    至于异常,只有在实际发生错误时才会引发异常。在 UDP 中,无法访问的主机导致套接字错误的唯一方法是网络发回 ICMP 数据包以指示无法访问主机。 "connected" UDP 套接字将在内部接收此类数据包,并在 后续 reads/sends 上与同一对等方开始报告故障。因此,在您的示例中,对 Send() 的调用永远不会引发有关未到达主机的错误,因为它根本不知道。 ReceiveString() 有更好的机会报告此类错误,但是它目前的实现方式可能只是忽略它们,因为它会在实际读取之前检查套接字是否可读(有一个挂起的 UDP 数据包)。 ICMP 数据包可能不会使套接字进入可读状态。

    由于您在 ReceiveString() 上指定了超时,如果超时过去但未收到实际字符串,您将不得不假设对等方已经消失。

  2. 您的代码绕过了代理,因为您没有正确设置 TransparentProxy

    当您访问 TransparentProxy 属性 时,如果当前未分配任何代理组件,则 属性 getter 会创建一个内部 TIdSocksInfo 对象。 TIdSocksInfo 有一个 Version 属性,默认为 svNoSocks。由于您尝试通过 SOCKS v5 代理进行连接,因此您需要将 Version 设置为 svSocks5

    if UseProxy then begin
      (IdUDPClient.TransparentProxy as TIdSocksInfo).Version := svSocks5; // <--
      IdUDPClient.TransparentProxy.Host    := '20.20.20.20';
      IdUDPClient.TransparentProxy.Port    := 8080;
    end;
    

    TransparentProxy.Enabled 属性 setter 根本不与 TIdSocksInfo 一起使用,它是一个空操作。但是,Enabled属性getterreturnsTrue/False取决于TIdSocksInfo.Version属性.

    的值

    而且您不需要手动调用 OpenProxy()CloseProxy()TIdUDPClient.Connect()TIdUDPClient.Disconnect() 会在 TransparentProxy 时为您处理已启用。

现在,说了这么多,试试这个:

var
  UseProxy : Boolean = True;
  ...

var
  IdUDPClient : TIdUDPClient;
  IdSocksInfo : TIdSocksInfo;
  sText       : string;
begin
  IdUDPClient := TIdUDPClient.Create(nil);
  try
    IdUDPClient.Host := '10.10.10.10';
    IdUDPClient.Port := 5555;

    if UseProxy then begin
      IdSocksInfo := TIdSocksInfo.Create(IdUDPClient);
      IdSocksInfo.Version := svSocks5;
      IdSocksInfo.Host    := '20.20.20.20';
      IdSocksInfo.Port    := 8080;
      IdUDPClient.TransparentProxy := IdSocksInfo;
    end;

    try
      IdUDPClient.Connect;
    except
      on E: Exception do begin
        Writeln('Connect Error: ', E.Message);
        Exit;
      end;
    end;

    try
      Writeln('Connected.');

      try
        IdUDPClient.Send('Foo');
      except
        on E: Exception do begin
          Writeln('Send Error: ', E.Message);
          Exit;
        end;
      end;

      try
        sText := IdUDPClient.ReceiveString(1000);
        if sText <> '' then
          Writeln('Received: ', sText)
        else
          Writeln('Nothing Received after 1 second.');
      except
        on E: Exception do begin
          Writeln('Receive Error: ', E.Message);
        end;
      end;
    finally
      IdUDPClient.Disconnect;
    end;
  finally
    IdUDPClient.Free;
  end;

end.