具有非阻塞 tcp 调用的 TThread

Posted

技术标签:

【中文标题】具有非阻塞 tcp 调用的 TThread【英文标题】:TThread with Non-Block tcp calls 【发布时间】:2015-08-03 20:16:44 【问题描述】:

我在 TThread 中使用 ipWorks 组件 TipwIPPort。据我了解,TipwIPPort 是非阻塞的。

我熟悉创建线程,其中 Execute 方法中的所有处理都是“阻塞”的。

在这种情况下,我需要连接到远程服务器,然后使用 TIpwIPPort.DataToSend 进行后续调用。我在线程的Execute函数中调用了Connect方法。但是永远不会触发 OnConnected 事件。

我需要设置哪些参数和属性(例如,“CreateSuspended”传递给构造函数,FreeOnTerminate 值),以便我可以控制何时终止线程。

type TMyThread=class(TThread)
private
  IPPort1: TipwIPPort;
  procedure IPPort1Connected(Sender: TObject; StatusCode: Integer; const Description: String);
  procedure IPPort1DataIn(Sender: TObject; Text: String; EOL: Boolean);
end;



procedure TMyThread.IPPort1Connected(Sender: TObject; StatusCode: Integer; const Description: String);
begin
// never get here
  AppendToLog('Status Code in Connect:'+inttostr(StatusCode)+'; Description:'+Description);
  if StatusCode = 0 then begin
    //  send data to server using ipport1.datatosend.....
  end;
end;

procedure TMyThread.Execute;
begin
  appendtolog('TMyThread.Execute');
  IPPort1    := TipwIPPort.Create(nil);
  try
    With IPPort1 do begin
      EOL := #4;
      KeepAlive           := True;
      OnConnected         := IPPort1Connected;
      OnDataIn            := IPPort1DataIn;
    end;
    IPPort1.Connect('xxx.xxx.xxx.xxx',8888);
    appendtolog('done with execute');
  finally
  end;
end;

procedure TMyThread.IPPort1DataIn(Sender: TObject; Text: String; EOL: Boolean);
begin

  if (Pos('keytoendconnection',Text)>0)  then begin
    ipPort1.Disconnect;
    // Terminate the thread and free
  end;
end;

procedure TForm1.Button1Click(sender: TObject);
var
  myThread;
begin
  // what parameters and properties do I need to set to allow me to control when the thread is terminated???
  myThread := TMyThread.Create(True);
  mSouthernObject.FreeOnTerminate := False;
  mSouthernObject.Resume;
end;

【问题讨论】:

推荐讲座***.com/questions/4206377/… 连接后,线程立即返回。你也发布了假代码。请显示真实代码。一个 MCVE。您现在已经在这里提出了 100 多个问题。你应该知道现在不要发布假代码。 @DavidHeffernan - 对不起......我确实尝试从我的更大项目中举一个例子来说明这个问题。那么在“连接”之后,退出“执行”过程,线程是否终止?有没有办法对服务器进行级联调用并控制线程何时终止? 您必须正确获取这些详细信息。当我们根据问题中的内容回答时,提问者会说:“哦,但我的代码不同”,这真的很令人沮丧。 @DavidHeffernan - 我会尽力而为。但在某些情况下,制作 MCVE 可能需要数小时。连雷米都给我打电话了。虽然我提供的是“原型”,但我似乎至少已经解决了根本问题。对不起 - 下次会做得更好。感谢您过去对我的问题的贡献。 【参考方案1】:

如果您阅读了 IPPort 组件的documentation,它指出:

组件的操作几乎是完全异步的。除处理域名解析的调用外,所有调用均通过异步消息(无阻塞调用)进行操作。与使用阻塞调用相比,性能提升相当可观。

因此,您的线程需要自己的消息队列/循环来接收和分发这些套接字消息,例如:

type
  TMyThread = class(TThread)
  private
    IPPort1: TipwIPPort;
    procedure IPPort1Connected(Sender: TObject; StatusCode: Integer; const Description: String);
    procedure IPPort1DataIn(Sender: TObject; Text: String; EOL: Boolean);
  protected
    procedure Execute; override;
    procedure DoTerminate; override;
    procedure TerminatedSet; override;
  end;

procedure TMyThread.IPPort1Connected(Sender: TObject; StatusCode: Integer; const Description: String);
begin
  AppendToLog('Status Code in Connect:'+inttostr(StatusCode)+'; Description:'+Description);
  if StatusCode = 0 then begin
    //  send data to server using ipport1.datatosend.....
  end else begin
    // start a timer or something to issue WM_CONNECT again after a small delay, say 5-10 seconds at least...
  end;
end;

const
  WM_CONNECT = WM_APP+1;

procedure TMyThread.Execute;
var
  Message: TMsg;
begin
  appendtolog('TMyThread.Execute');
  IPPort1 := TipwIPPort.Create(nil);
  try
    IPPort1.EOL := #4;
    IPPort1.KeepAlive := True;
    IPPort1.OnConnected := IPPort1Connected;
    IPPort1.OnDataIn := IPPort1DataIn;
    PostThreadMessage(ThreadID, WM_CONNECT, 0, 0);
    while not Terminated do
    begin
      if not GetMessage(Message, 0, 0, 0) then Break;
      case Message.Msg of
        WM_CONNECT:
        begin
          IPPort1.Connect('xxx.xxx.xxx.xxx', 8888);
        end;
        //...
      else
        TranslateMessage(Message);
        DispatchMessage(Message);
      end;
    end;
  finally
    IPPort1.Free;
  end;
end;

procedure TMyThread.DoTerminate;
begin
  appendtolog('done with execute');
  inherited;
end;

procedure TMyThread.TerminatedSet;
begin
  PostThreadMessage(ThreadID, WM_QUIT, 0, 0);
end;

procedure TMyThread.IPPort1DataIn(Sender: TObject; Text: String; EOL: Boolean);
begin
  if Pos('keytoendconnection', Text) > 0 then
  begin
    ipPort1.Disconnect;
    Terminate;
  end;
end;

private
  myThread: TMyThread;

procedure TForm1.Button1Click(sender: TObject);
begin
  myThread := TMyThread.Create(False);
end;

procedure TForm1.Button2Click(sender: TObject);
begin
  if myThread <> nil then
  begin
    myThread.Terminate;
    myThread.WaitFor;
    FreeAndNil(myThread);
  end;
end;

【讨论】:

以上是关于具有非阻塞 tcp 调用的 TThread的主要内容,如果未能解决你的问题,请参考以下文章

关系 和非关系 阻塞非阻塞的区别

windows下在非阻塞TCP套接字上使用SO_SNDBUF的奇怪行为

非阻塞socket总结

socket编程 ------ 客户端(非阻塞方式)

NIO实现TCP的非阻塞通信

如何将 TCP 套接字更改为非阻塞?