Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Disconnected TCPServer thread won't terminate
#3
(04-18-2022, 04:13 PM)rlebeau Wrote: Why are you waiting for the OnExecute event?  You should disconnect them in the OnConnect event if the WebSocket handshake is not valid.

I originally performed the disconnects in OnConnect but I saw a post from you in StackOverFlow telling someone else to do it in OnExecute. Perhaps I misunderstood the context. Any in case, I get the same results either way.

Quote:What is the point of doing that? First off, Indy threads are already named by Indy.

Just for debugging purposes to see how far the thread progresses. And to tie them to IP addresses to see if they are rogue or from players on the game server.

Quote:But more importantly, having MadExcept name threads only makes sense if an error report is generated, and you shouldn't be getting an error just for disconnecting clients normally.

It's too late to name them after an error occurs. Like I mentioned, I have a report that generates a list of all running threads to help debug the very type of situation I'm dealing with now: zombie threads using up system resources.

Quote:Do you have thread pooling enabled on your server?

I don't know what that is so probably not unless it's on by default.

Quote:What do AdminSessionDisconnect() and PlayerSessionDisconnect() look like?

I've not had any issues with normal connections terminating. None of those leave zombie threads behind. But here are those OnDisconnect events:

Code:
procedure AdminSessionDisconnect(AContext: TIdContext);
var
  ID, IP, User: string;
  Admin: TAdminSession;
begin
  ID := TConnectRec(AContext.Data).ID;
  IP := '?';
  User := '?';
  Admin := TAdminSession(AdminSessionList.GetKeyObject(ID));
  if Admin <> Nil then
  begin
    IP := Admin.IP;
    User := Admin.UserName;
    if Admin.ReleaseAndFree then FreeAndNil(Admin);
  end;
  AdminSessionList.RemoveKey(ID);
  AdminSessionUpdate.QueueAdd('Del', ID);
  LogData.AddEvent('Admin', User + ' logged out session ' + ID + ' from IP ' + IP);
end;

procedure PlayerSessionDisconnect(AContext: TIdContext);
var
  PS: TPlayerSession;
  ID, p: string;
begin
  if gvar.Running = False then Exit;
  if Assigned(AContext.Data) then ID := TConnectRec(AContext.Data).ID else ID := '';
  if ID = '' then Exit;
  PS := TPlayerSession(PlayerSessionList.GetKeyObject(ID));
  if PlayerSessionList.OpenObject(PS) then // make sure session object not already destroyed
  begin
    PS.ReleaseAndFree;  // decrement AccessCount from GetKeyObject
    PS.PacketCS.Enter;
    try
      PS.Status := 'Discon';
      PS.Disconnected := Now;
      PS.Connection := Nil;
      SessionUpdate.QueueAdd('Ref', PS.ID);
      p := PS.Player;
      if p = '' then p := 'IP ' + PS.IP;
      LogData.AddEvent('Connect', p + ' disconnects session ' + PS.ID + ', PC ' + PS.PC);
    finally
      PS.PacketCS.Leave;
      if PS.ReleaseAndFree then FreeAndNil(PS);
    end;
  end;
end;

FYI, this was the StackOverFlow thread I was referring to:
https://stackoverflow.com/questions/4500...the-server

And if helps, here is my OnConnect event:

Code:
class procedure WSServerEvents.OnConnect(AContext: TIdContext);
var
  IO: TIdIOHandler;
  Key, ProxyHeader, Header, LCHeader, RawHost, Host, RawOrigin, Origin, IP, ProxyIP: string;
  Admin: boolean;
  c: integer;
begin
  try
    IP := AContext.Binding.PeerIP;
    MadExcept.NameThread(GetCurrentThreadID, 'WebSocket Connect (' + IP + ')');
    AContext.Connection.Intercept := TIdConnectionIntercept.Create(AContext.Connection);  // track total bytes in and out
    AContext.Connection.Intercept.OnReceive := InterceptEvents.OnReceive;
    AContext.Connection.Intercept.OnSend := InterceptEvents.OnSend;
    if (AContext.Connection.IOHandler is TIdSSLIOHandlerSocketBase) then
      TIdSSLIOHandlerSocketBase(AContext.Connection.IOHandler).PassThrough:= false;
    IO := AContext.Connection.IOHandler;
    IO.ReadTimeout := 10000;  // 10 second readln timeout
    ProxyIP := IP;
    Key := '';
    ProxyHeader := LowerCase(Trim(sysProxyIPHeader.sValue));
    RawHost := '';
    RawOrigin := '';
    Host := '';
    Origin := '';
    IP := '';
    Admin := False;
    c := 0;

    MadExcept.NameThread(GetCurrentThreadID, 'WebSocket Header Start (' + ProxyIP + ')');

    repeat // read headers until a blank line received
      Sleep(10);
      Header := IO.ReadLn();
      LCHeader := LowerCase(Header);
      if Pos('get /admin', LCHeader) = 1 then Admin := True else
      if Pos('host:', LCHeader) = 1 then
      begin
        RawHost := Trim(Copy(Header, 6, 255));
        Host := URLToHost('http://' + RawHost);
      end else
      if Pos('origin:', LCHeader) = 1 then
      begin
        RawOrigin := Trim(Copy(Header, 8, 255));
        Origin := URLToHost(RawOrigin);
      end else
      if Pos('sec-websocket-key:', LCHeader) = 1 then Key := Trim(Copy(Header, 19, 99))
      else if (ProxyHeader <> '') and (Pos(ProxyHeader + ':', LCHeader) = 1) then
        IP := Trim(Copy(Header, Length(ProxyHeader) + 2, 99));
      Inc(c);
    until (Header = '') or (c > 50);  // 50 header lines max

    MadExcept.NameThread(GetCurrentThreadID, 'WebSocket Header End (' + ProxyIP + ')');

    if Key = '' then Exit; // invalid websocket header, disconnect in OnExecute
    if not(Admin) and not(gvar.Running) then Exit;  // offline for players, disconnect in OnExecute

    MadExcept.NameThread(GetCurrentThreadID, 'WebSocket Key Ok (' + ProxyIP + ')');

    // send response headers and hashed key in accordance with RFC 6455
    IO.WriteLn('HTTP/1.1 101 Switching Protocols');
    IO.WriteLn('Upgrade: websocket');
    IO.WriteLn('Connection: Upgrade');
    IO.WriteLn('Sec-WebSocket-Accept: ' + HashKey(Key));
    IO.WriteLn('');
    if sysSameOriginPolicy.bValue and (Host <> Origin) then
    begin
      SendCloseFrame(AContext, 4000);
      Sleep(1000);  // enough time for writes to go out
      Exit;  // disconnect in OnExecute
    end;
    if IP = '' then
    begin
      IP := ProxyIP;
      ProxyIP := '';
    end;
    if Admin then AdminSessionConnect(AContext, IP, ProxyIP)
    else PlayerSessionConnect(AContext, IP, ProxyIP);
  except
    on E: Exception do
    begin
      if E is EIdException then raise else
      begin
        LogData.AddError('WSServerConnect error: ' + E.Message);
        raise;
      end;
    end;
  end;
end;
Reply


Messages In This Thread
RE: Disconnected TCPServer thread won't terminate - by kbriggs - 04-18-2022, 05:48 PM

Forum Jump:


Users browsing this thread: 1 Guest(s)