【问题标题】:Simple TCP connect timeout wrapper in DelphiDelphi中的简单TCP连接超时包装器
【发布时间】:2014-11-14 23:05:02
【问题描述】:

我正在尝试创建一个支持超时参数的connect 包装器。

主函数应该是这样的:

function ConnectToHost (Host : PAnsiChar; Port : Word; Timeout: DWORD): Integer;

我也希望尽可能简单,并将套接字保持在阻塞模式。

此外,我们的想法是使用纯 Winsock API,不使用 Indy 组件或任何东西。


主函数应返回一个整数,以获取有关所发生事件的更多信息。

我在考虑主要由 connect 函数返回的返回值以及超时信息的附加值。


在主函数内部有一个线程负责连接,而主函数在给定的超时时间内等待线程。


现在担心将Host和Port等数据传递给线程和Memory Leaks。

我正在考虑使用这些 API 进行超时操作:

  • CreateThread
  • WaitForSingleObject
  • GetExitCodeThread
  • TerminateThread?

以及这些使用 winsock 进行 TCP 连接的 API:

  • socket
  • htons
  • inet_addr
  • connect

考虑到超时,安全的 connect 包装器看起来如何?

现在我似乎不知道如何才能做到这一点。任何帮助,将不胜感激。

【问题讨论】:

  • Indy 实现了线程连接超时。你可以看看它是怎么做的。

标签: multithreading delphi sockets tcp winsock


【解决方案1】:

试试这样的:

type
  PConnectInfo = ^ConnectInfo;
  ConnectInfo = record
    Socket: TSocket;
    Addr: sockaddr_in;
    ErrCode: Integer;
  end;

function ConnectProc(Param : Pointer): DWORD; stdcall;
begin
  With PConnectInfo(Param)^ do
  begin
    if connect(Socket, PAddrInfo(@Addr), SizeOf(Addr)) = 0 then
      ErrCode := 0
    else
      ErrCode := WSAGetLastError;
  end;
  Result := 0;
end;

function ConnectToHost (Host : PAnsiChar; Port : Word; Timeout: DWORD): Integer;
var
  ci: ConnectInfo;
  H: THandle;
  tid: DWORD;
begin
  Result := 0;

  ZeroMemory(@ci, SizeOf(ci));

  ci.Socket := ...;

  ZeroMemory(@ci, SizeOf(ci));
  ci.Addr.sin_family := AF_INET;
  ci.Addr.sin_addr.s_addr := inet_addr(Host);
  ci.Addr.sin_port := htons(Port);

  if Timeout = INFINITE then
  begin
    ConnectProc(@ci);
  end else
  begin
    h := CreateThread(nil, 0, @ConnectProc, @ci, 0, @tid);
    if h = 0 then
    begin
      Result := GetLastError;
      Exit;
    end;

    case WaitForSingleObject(h, Timeout) of
      WAIT_FAILED: begin
        Result := GetLastError;
      end;
      WAIT_OBJECT_0: begin
        Result := ci.ErrCode;
      end;
      WAIT_TIMEOUT: begin
        Result := WSAETIMEDOUT;
      end;
    else
      Result := -1;
    end;

    CloseHandle(h);
  end;

  if Result <> 0 then
    closesocket(ci.Socket);
end;

【讨论】:

  • 完美!谢谢你。这比我想象的还要先进。
  • 虽然在connect 仍处于活动状态时调用closesocket 是否有效?
  • 是的,它是有效的,并且是中止阻塞连接的唯一方法。
  • TerminateThread 是否可以替代 abort connect
  • 远离TerminateThread()。蛮力终止线程使其资源处于不确定状态。而且您可能会弄乱套接字的内部结构。最好只使用closesocket(),它将以可检测到的错误中止connect(),并让线程正常退出。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-03
  • 2023-03-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多