2016-05-16 192 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

如果我只爲每個服務器分配一個客戶端,由於客戶端是在工作站上運行的醫療設備,因此會同步化問題。只是好奇? –