2016-05-16 111 views
1

我已经适应从本网站另一篇文章下面的代码,但它似乎仍然冻结。即使有客户端连接,我也必须能够关闭或断开服务器。我会等待,直到他们完成发送消息,但如果我启动服务器,从客户端连接到它,我仍然不能关闭服务器,不会冻结。然后我必须关闭Windows任务管理器。Delphi XE3,Indy TCP服务器。关闭服务器

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; 

产生额外的信息......

我用下面的代码来连接到服务器。当它连接服务器时发回一条有连接的消息。

客户端连接到服务器

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服务器上

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; 

的这2个程序的结合将导致服务器冻结当我试图关闭服务器程序,如果我不关闭先下客户。我很抱歉,我太新,印,看看有什么问题或怎么做线程的工作来解决这个问题。我希望你能看到我的初学者在2个连接程序中的一个出错。

这里是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; 

回答

3

with声明呼吁Contexts.LockList(),然后你的循环再次调用Contexts.LockList(),但循环完成后,你只叫Contexts.UnlockList()一次。因此,Contexts名单还在锁定,任何其他线程的任何进一步的访问将无限期死锁,包括客户端线程,当他们尝试从Contexts名单,这反过来又因为它等待将死锁Active属性setter自行拆除所有的客户端线程终止。

在你的循环,更换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; 

事实上,所有的代码你展现的是完全多余的,应予删除,这是所有你需要:

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

它已经做了所有断开活动的客户端,清除的辛勤工作3210列表,并关闭服务器。你根本不需要手动做这些东西。

+0

我已经尝试过,基于你的另一篇文章,但如果客户端已经连接到服务器,如果我做PantherIdTCPServer.Active:= False,然后我尝试关闭窗体程序冻结,不会关闭。如果关闭已连接的客户端程序,然后关闭服务器,我可以关闭窗体而不冻结。如果客户端已连接且仍处于连接状态,则关闭服务器并关闭表格冻结。我在服务器程序上没有OnClose或OnCloseQuery事件。它似乎冻结,直到我断开客户端的客户端。 –

+0

您遇到了死锁问题,例如,如果在客户端执行与主线程同步的某些操作时从主线程停用服务器,则会出现这种问题。客户端将被阻塞在主线程中等待,并且主线程将被阻塞等待客户端。您在问题中显示的代码遭受同样的问题。因此,在主线程中停用服务器时,不要与主线程同步,要么从单独的工作线程停用服务器,以免主线程被阻塞。 –

+0

如果我只为每个服务器分配一个客户端,由于客户端是在工作站上运行的医疗设备,因此会同步化问题。只是好奇? –