简体   繁体   English

Delphi XE3,Indy TCP Server。 关闭服务器

[英]Delphi XE3, Indy TCP Server. Shut down server

I have adapted the following code from another post on this website, but it still seems to freeze. 我已经在本网站上的另一篇文章中修改了以下代码,但它似乎仍然冻结了。 I must be able to turn off or disconnect the server even if there are clients connected. 即使连接了客户端,我也必须能够关闭或断开服务器。 I will wait till they finish sending messages but if I start the server, connect to it from a client, I still can't shut down the server without freezing. 我会等到他们完成发送消息但是如果我启动服务器,从客户端连接到它,我仍然无法冻结服务器。 I then have to shut off with Windows Task Manager. 然后我必须关闭Windows任务管理器。

procedure TTasksForm.ShutDownPantherServer;
var i : integer ;
    Context: TidContext;
begin

  if PantherIdTCPServer.Active = True then
    with PantherIdTCPServer.Contexts.LockList do
    try

      for i := (PantherIdTCPServer.Contexts.LockList.Count - 1) downto 0 do
      begin

        Context := Items[i] ;

        if Context = nil then
          Continue;
        Context.Connection.IOHandler.WriteBufferClear;
        Context.Connection.IOHandler.InputBuffer.Clear;
        Context.Connection.IOHandler.Close;

        if Context.Connection.Connected then
          Context.Connection.Disconnect;

      end;

    finally
      PantherIdTCPServer.Contexts.UnLockList ;
    end ;

  if PantherIdTCPServer.Active = True then
    PantherIdTCPServer.Active := False ;

end;

Additonal information... 附加信息......

I use the following code to connect to the server. 我使用以下代码连接到服务器。 When it connects the server sends back a message that there was a connection. 当它连接时,服务器发回一条有连接的消息。

Client Connect To Server 客户端连接到服务器

procedure TPantherSimulatorForm.ConnectToServer ;
var MsgIn : String ;
begin

  PantherIdTCPClient.Host := IPAddressEdit.Text ;
  PantherIdTCPClient.Port := StrToInt(PortEdit.Text) ;

  PantherIdTCPClient.Connect;

  MsgIn := PantherIdTCPClient.IOHandler.ReadLn();

  TThread.Synchronize(nil,
    procedure
    begin

      ClientTrafficMemo.Clear ;
      ClientTrafficMemo.Lines.Add(FormatDateTime( 'yyyy-mm-dd    hh:nn:ss.zzz', now ) +
'  ' + MsgIn) ;
    end ) ;

end;

OnConnect on server 服务器上的OnConnect

procedure TTasksForm.PantherIdTCPServerConnect(AContext: TIdContext);
begin

  AContext.Connection.IOHandler.DefStringEncoding := Indy8BitEncoding ;

  TThread.Synchronize(nil,
    procedure
    begin
      ServerTrafficMemo.Lines.Add(FormatDateTime( 'yyyy-mm-dd hh:nn:ss.zzz', now ) +
'  OnConnect') ;
end );

  // connected message
  AContext.Connection.IOHandler.WriteLn('Connected');

end;

The combination of these 2 procedures will cause the server to freeze when I attempt to close the server program if I do not shut down the client first. 如果我不首先关闭客户端,当我尝试关闭服务器程序时,这两个过程的组合将导致服务器冻结。 I apologize I am too new to Indy to see what the issue is or how to do the thread work to solve the problem. 我道歉我对Indy太新了,看看问题是什么,或者如何解决问题。 I was hoping you would see my beginners error in one of the 2 connection procedures. 我希望你会在2个连接程序之一中看到我的初学者错误。

Here is the OnExecute code: 这是OnExecute代码:

procedure TForm2.PantherIdTCPServerExecute(AContext: TIdContext);
begin
  Sleep(1000) ;

  TThread.Queue(nil,
    procedure
    begin
        ServerTrafficMemo.Lines.Add(FormatDateTime( 'yyyy-mm-dd hh:nn:ss.zzz', now ) +
'  OnExecute') ;
    end ) ;

end;

Your with statement is calling Contexts.LockList() and then your loop is calling Contexts.LockList() again, but you are only calling Contexts.UnlockList() once after the loop has completed. 你的with语句调用Contexts.LockList()然后你的循环再次调用Contexts.LockList() ,但是你只在循环完成后调用一次Contexts.UnlockList() As such, the Contexts list is still locked, and any further access by any other threads will deadlock indefinitely, including the client threads when they try to remove themselves from the Contexts list, which in turn will deadlock the Active property setter since it waits for all of the client thread to terminate. 因此, Contexts列表仍然被锁定,任何其他线程的任何进一步访问将无限期地死锁,包括客户端线程,当他们试图从Contexts列表中删除自己时,这反过来将使Active属性设置器死锁,因为它等待所有客户端线程终止。

In your loop, replace PantherIdTCPServer.Contexts.LockList.Count with simply Count since the with is acting on the TList that LockList() returned: 在你的循环,更换PantherIdTCPServer.Contexts.LockList.Count用简单的Count ,因为with作用于TListLockList()返回:

procedure TTasksForm.ShutDownPantherServer;
var
  i : integer ;
  Context: TidContext;
begin
  if PantherIdTCPServer.Active = True then
    with PantherIdTCPServer.Contexts.LockList do
    try
      // HERE!!!
      for i := ({PantherIdTCPServer.Contexts.LockList.}Count - 1) downto 0 do
      begin

        Context := Items[i] ;

        if Context = nil then
          Continue;
        Context.Connection.IOHandler.WriteBufferClear;
        Context.Connection.IOHandler.InputBuffer.Clear;
        Context.Connection.IOHandler.Close;

        if Context.Connection.Connected then
          Context.Connection.Disconnect;

      end;

    finally
      PantherIdTCPServer.Contexts.UnLockList ;
    end ;

  if PantherIdTCPServer.Active = True then
    PantherIdTCPServer.Active := False ;

end;

In fact, all of the code you have show is completely redundant and should be removed, this is all you need: 实际上,您显示的所有代码都是完全冗余的,应该删除,这就是您所需要的:

procedure TTasksForm.ShutDownPantherServer;
begin
  PantherIdTCPServer.Active := False;
end;

It already does all of the hard work of disconnecting active clients, clearing the Contexts list, and shutting down the server. 它已经完成了断开活动客户端,清除Contexts列表和关闭服务器的所有艰苦工作。 You don't need to do that stuff manually at all. 你根本不需要手动完成这些工作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM