0

I am implementing a live chat in my deplhi project, which receives new messages by doing GET request to the server. The server itself closes connection after 20 seconds if no new messages occur. Code discribed above is located in a separate thread (it is created on visiting chat "page") so it doesnt freezes the GUI. When i go to a non-chat page from chat, i call this code outside of thread_chat in order to make the thread exit:

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

However since my server timeout is 20 seconds, thread actualy closes only when it receives a response (yea, this sounds logically, since my thread loop is while not Terminated do). So what i am looking for is to close HTTP connection in the middle of the request.

Attemp #1

Initially i looked into TerminateThread by calling it like

TerminateThread(thread_chat.Handle, 0)

and this works fine until i try to kill thread on second time - my app completly freezes. So i went to

Attemp #2

I created a global variable URL_HTTP: TIdHTTP and i receive the server page content with this function:

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;

and when i call this code outside of thread_chat

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

i get EidClosedSocket exception (after some tests while writing this post, i get EAccessViolation error instead).

I run out of ideas. How can i close HTTP request before server response?


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;
lolbas
  • 794
  • 1
  • 9
  • 34
  • Your second approach is trying to access a `TIdHTTP` object that is local to, and free by, `get_URL_Content()`, so you have a race condition. You need to move the `TIdHTTP` object into your thread class as a data member, then you can disconnect it when terminating the thread. – Remy Lebeau Jun 01 '15 at 20:25
  • That being said, how is your thread blocking if you are not sending HTTP requests? Or have you sent an HTTP request that keeps the connection open and receives push messages as they are generated by the server? HTTP generally uses a command/response model, so your thread blocking for 20 seconds waiting for a response from the server does not make much sense outside of a push model (which `TIdHTTP` is not designed for anyway). Please show your actual thread code that you are having problems with. – Remy Lebeau Jun 01 '15 at 20:28
  • @RemyLebeau server side of the request is long-polling, so if a new message arrives, the request will be closed quite instantly, and if not - server will reply only after 20 seconds. I added my thread code – lolbas Jun 01 '15 at 21:10
  • So, if there is no message waiting on the server, a request sent to the server will not receive a response for 20 seconds? If so, then the server is not designed correctly and needs to be fixed. Responses need to be timely. If there is no message waiting, the server should send a response immediately to say exactly that so the client is not blocked. – Remy Lebeau Jun 01 '15 at 21:18
  • @RemyLebeau just to clear out, after 20 seconds server responses with `No new messages.`. Anyway, i use this method on a websites' chat so im not really up to changing the timings – lolbas Jun 01 '15 at 21:22

1 Answers1

1

Try something like this:

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;
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Well, i still get the same problems. Using your code, GUI freezes at `WaitFor`. If i comment this, GUI freezes at `inherited` in `Destroy`. If i comment this one as well, thread is still active until server response(<20 secs(same happenes in all occasions)). It seems odd to me since `HTTP` is disconnected.. – lolbas Jun 02 '15 at 05:30
  • Odd, unless `Disconnect` is being called before `CheckMessages`, so it reconnects. Maybe try adding `OnWork...` event handlers to `TIdHTTP` and have them call `SysUtils.Abort()` if `Terminated` is true. Maybe also set `TIdHTTP.ReadTimeout`. – Remy Lebeau Jun 02 '15 at 08:10