【问题标题】:delphi - disconnect IdHTTP in the middle of long-poll requestdelphi - 在长轮询请求中间断开 IdHTTP
【发布时间】:2015-06-01 12:45:14
【问题描述】:

我正在我的 deplhi 项目中实现实时聊天,它通过向服务器发出 GET 请求来接收新消息。如果没有新消息出现,服务器本身会在 20 秒后关闭连接。上面描述的代码位于一个单独的线程中(它是在访问聊天“页面”时创建的),因此它不会冻结 GUI。当我从聊天转到非聊天页面时,我在thread_chat 之外调用此代码以使线程退出:

if thread_chat <> nil then
begin
  thread_chat.Terminate;
  thread_chat := nil;
end;

但是,由于我的服务器超时为 20 秒,因此线程实际上仅在收到响应时才关闭(是的,这听起来合乎逻辑,因为我的线程循环是 while not Terminated do)。所以我正在寻找的是在请求中间关闭 HTTP 连接。

尝试 #1

最初我通过称呼它来查看TerminateThread

TerminateThread(thread_chat.Handle, 0)

这工作正常,直到我第二次尝试杀死线程 - 我的应用程序完全冻结。于是我去了

尝试 #2

我创建了一个全局变量URL_HTTP: TIdHTTP,并通过此函数接收服务器页面内容:

function get_URL_Content(const Url: string): string;
var URL_stream: TStringStream;
begin
  URL_HTTP := TIdHTTP.Create(nil);
  URL_stream := TStringStream.Create(Result);
  URL_HTTP.Get(Url, URL_stream);
  if URL_HTTP <> nil then
  try
    URL_stream.Position := 0;
    Result := URL_stream.ReadString(URL_stream.Size);
  finally
    FreeAndNil(URL_HTTP);
    FreeAndNil(URL_stream);
  end;
end;

当我在 thread_chat 之外调用此代码时

if thread_chat <> nil then
begin
  URL_HTTP.Disconnect;
  thread_chat.Terminate;
end;

我得到EidClosedSocket 异常(在写这篇文章时经过一些测试,我得到EAccessViolation 错误)。

我的想法已经用完了。如何在服务器响应之前关闭 HTTP 请求?


procedure thread_chat.Execute;  //overrided
begin
    while not Terminated do
      if check_messages then  //sends request to the server, processes response, true if new message(s) exist
        show_messages;
end;

【问题讨论】:

  • 您的第二种方法是尝试访问一个TIdHTTP 对象,该对象是get_URL_Content() 的本地对象,并且由get_URL_Content() 免费提供,因此您有一个竞争条件。您需要将TIdHTTP对象作为数据成员移动到您的线程类中,然后您可以在终止线程时断开它。
  • 话虽如此,如果您不发送 HTTP 请求,您的线程如何阻塞?或者您是否发送了一个 HTTP 请求以保持连接打开并接收服务器生成的推送消息? HTTP 通常使用命令/响应模型,因此您的线程阻塞 20 秒以等待来自服务器的响应在推送模型之外没有多大意义(TIdHTTP 无论如何都不是为这种模型设计的)。请显示您遇到问题的实际线程代码。
  • @RemyLebeau 请求的服务器端是长轮询的,因此如果有新消息到达,请求将立即关闭,否则 - 服务器将仅在 20 秒后回复。我添加了我的线程代码
  • 那么,如果服务器上没有消息等待,发送到服务器的请求会在20秒内没有收到响应?如果是这样,那么服务器设计不正确,需要修复。反应需要及时。如果没有消息等待,服务器应立即发送响应以准确说明客户端不会被阻塞。
  • @RemyLebeau 只是为了清除,20 秒后服务器响应 No new messages.。无论如何,我在网站的聊天中使用这种方法,所以我不太愿意改变时间

标签: multithreading delphi delphi-7 idhttp


【解决方案1】:

试试这样的:

type
  TThreadChat = class(TThread)
  private
    HTTP: TIdHTTP;
    function CheckMessages: Boolean;
    procedure ShowMessages;
  protected
    procedure Execute; override;
  public
    constructor Create;
    destructor Destroy; override;
    procedure Stop;
  end;

constructor TThreadChat.Create;
begin
  inherited Create(False);
  HTTP := TIdHTTP.Create(nil);
end;

destructor TThreadChat.Destroy;
begin
  HTTP.Free;
  inherited;
end;

function TThreadChat.CheckMessages: Boolean;
var
  Resp: string;
begin
  //...
  Resp := HTTP.Get(Url);
  //...
end;

procedure TThreadChat.ShowMessages;
begin
  //...
end;

procedure TThreadChat.Execute;
begin
  while not Terminated do
  begin
    if CheckMessages then
       ShowMessages;
  end;
end;

procedure TThreadChat.Stop;
begin
  Terminate;
  try
    HTTP.Disconnect;
  except
  end;
end;

thread_chat := TThreadChat.Create;

...

if thread_chat <> nil then
begin
  thread_chat.Stop;
  thread_chat.WaitFor;
  FreeAndNil(thread_chat);
end;

【讨论】:

  • 好吧,我仍然遇到同样的问题。使用您的代码,GUI 冻结在WaitFor。如果我对此发表评论,GUI 在Destroy 中的inherited 冻结。如果我也对此发表评论,则线程仍然处于活动状态,直到服务器响应(HTTP 已断开连接..
  • 奇怪,除非 DisconnectCheckMessages 之前被调用,所以它会重新连接。也许尝试将OnWork... 事件处理程序添加到TIdHTTP 并让他们调用SysUtils.Abort() 如果Terminated 为真。也可以设置TIdHTTP.ReadTimeout
猜你喜欢
  • 2016-04-06
  • 2013-06-12
  • 1970-01-01
  • 1970-01-01
  • 2015-12-29
  • 2020-10-15
  • 2021-10-13
  • 1970-01-01
  • 2012-03-27
相关资源
最近更新 更多