2016-06-14 24 views
1

我在Delphi中創建了一個程序來創建服務器並偵聽客戶端的響應。客戶端連接到服務器,發送一些數據,並立即斷開連接。問題是,有時當數據收到我的程序停止響應。當我關閉程序時,大多數時候我看到EOSError 1400 [窗口句柄無效。](我知道這個錯誤是因爲襪子線程)。在關閉窗口之前,我將TCPServer的Active屬性設置爲false。我測試了TTCPServer和TIdTCPServer,但問題沒有解決。TCPServer和Error1400

這是我TTCPServer代碼:

procedure TMonitorFrm.TcpSerAccept(Sender: TObject; 
    ClientSocket: TCustomIpClient); 
var 
    b: array [0..300] of Byte; 
    z, k: Byte; 
    s: String; 
begin 
repeat 
    z := ClientSocket.ReceiveBuf(b, SizeOf(b), 0); 
    s := ''; 
    if (z > 6) then 
    begin 
    for k := 0 to z - 1 do 
     begin 
     s := s + IntToHex(b[k], 2); 
     if (k in [2, 5, 6]) then s := s + ' '; 
     end; 
    FullLst.Items.Add(s); 
    FullMessageEdt.Text := s; 
    if (Length(s) > 17) then Delete(s, 1, 17) else s := ''; 
    k := MessagesGrd.RowCount; 
    MessagesGrd.RowCount := k + 1; 
    MessagesGrd.Cells[0, k] := Format('%d.%d.%d', [b[3], b[4], b[5]]); 
    MessagesGrd.Cells[1, k] := Format('%d.%d.%d:%d', [b[0], b[1], b[2], b[6]]); 
    MessagesGrd.Cells[2, k] := s; 
    MessagesGrd.Cells[3, k] := TimeToStr(Now); 
    MessagesGrd.Row := k; 
    end; 
until (z = 0); 
Application.ProcessMessages; 
end; 

這是我TIdTCPServer代碼:

procedure TMonitorFrm.IdTCPSerExecute(AContext: TIdContext); 
var 
    r: TIdBytes; 
    k: Byte; 
    s: String; 
begin 
AContext.Connection.IOHandler.ReadTimeout := TCPTimeOut; 
AContext.Connection.IOHandler.ReadBytes(r, -1, False); 
if (Length(r) > 6) then 
    begin 
    for k := 0 to High(r) do 
    begin 
    s := s + IntToHex(r[k], 2); 
    if (k in [2, 5, 6]) then s := s + ' '; 
    end; 
    FullLst.Items.Add(s); 
    FullMessageEdt.Text := s; 
    if (Length(s) > 17) then Delete(s, 1, 17) else s := ''; 
    k := MessagesGrd.RowCount; 
    MessagesGrd.RowCount := k + 1; 
    MessagesGrd.Cells[0, k] := Format('%d.%d.%d', [b[3], b[4], b[5]]); 
    MessagesGrd.Cells[1, k] := Format('%d.%d.%d:%d', [b[0], b[1], b[2], b[6]]); 
    MessagesGrd.Cells[2, k] := s; 
    MessagesGrd.Cells[3, k] := TimeToStr(Now); 
    MessagesGrd.Row := k; 
    end; 
Finalize(r); 
Application.ProcessMessages; 
end; 
+1

1.有[在多線程應用該SO條目約EOSError 1400修改的GUI組件]看看(http://stackoverflow.com/questions/6353903/possible-causes-of-eoserror- 1400-invalid-window-handle),簡而言之:嘗試同步UI修改。否則,這個(至少對於TTCPServer /而言)看起來非常像embarcadero的例子。 2.對於您的TTCPServer代碼,ReceiveBuf可能會在套接字錯誤(或者如果這樣配置的情況下返回一個異常)返回一個負值。一般來說,不要在循環/終止條件中保留開放範圍。 – makadev

回答

2

與兩個代碼示例的問題是,他們是從上下文之外操縱UI控件的主UI線程。兩個代碼都在工作線程中運行它們的客戶端I/O,所以它們必須與主UI線程同步。要做到這一點的方法之一是與TThread.Synchronize()方法,如:

procedure TMonitorFrm.TcpSerAccept(Sender: TObject; 
    ClientSocket: TCustomIpClient); 
var 
    b: array [0..300] of Byte; 
    z, k: Byte; 
    s: String; 
begin 
    repeat 
    z := ClientSocket.ReceiveBuf(b, SizeOf(b), 0); 
    s := ''; 
    if (z > 6) then 
    begin 
     for k := 0 to z - 1 do 
     begin 
     s := s + IntToHex(b[k], 2); 
     if (k in [2, 5, 6]) then s := s + ' '; 
     end; 
     TThread.Synchronize(nil, 
     procedure 
     begin 
      FullLst.Items.Add(s); 
      FullMessageEdt.Text := s; 
      k := MessagesGrd.RowCount; 
      MessagesGrd.RowCount := k + 1; 
      MessagesGrd.Cells[0, k] := Format('%d.%d.%d', [b[3], b[4], b[5]]); 
      MessagesGrd.Cells[1, k] := Format('%d.%d.%d:%d', [b[0], b[1], b[2], b[6]]); 
      MessagesGrd.Cells[2, k] := Copy(s, 18, MaxInt); 
      MessagesGrd.Cells[3, k] := TimeToStr(Now); 
      MessagesGrd.Row := k; 
     end 
    ); 
    end; 
    until (z = 0); 
end; 

procedure TMonitorFrm.IdTCPSerConnect(AContext: TIdContext); 
begin 
    AContext.Connection.IOHandler.ReadTimeout := TCPTimeOut; 
end; 

procedure TMonitorFrm.IdTCPSerExecute(AContext: TIdContext); 
var 
    r: TIdBytes; 
    k: Byte; 
    s: String; 
begin 
    AContext.Connection.IOHandler.ReadBytes(r, -1, False); 
    if (Length(r) > 6) then 
    begin 
    for k := 0 to High(r) do 
    begin 
     s := s + IntToHex(r[k], 2); 
     if (k in [2, 5, 6]) then s := s + ' '; 
    end; 
    TThread.Synchronize(nil, 
     procedure 
     begin 
     FullLst.Items.Add(s); 
     FullMessageEdt.Text := s; 
     k := MessagesGrd.RowCount; 
     MessagesGrd.RowCount := k + 1; 
     MessagesGrd.Cells[0, k] := Format('%d.%d.%d', [b[3], b[4], b[5]]); 
     MessagesGrd.Cells[1, k] := Format('%d.%d.%d:%d', [b[0], b[1], b[2], b[6]]); 
     MessagesGrd.Cells[2, k] := Copy(s, 18, MaxInt); 
     MessagesGrd.Cells[3, k] := TimeToStr(Now); 
     MessagesGrd.Row := k; 
     end 
    ); 
    end; 
end; 

然而,隨着中說,要小心不要從主UI線程,而與主同步停用其中一臺服務器UI線程。這是一個保證的僵局。您將有兩種:

  1. 確保沒有同步請求正在進行停用服務器之前。

  2. 使用異步UI更新而不是同步更新。您可以使用TThread.Queue(),TIdNotify等。或者將您的數據存儲在線程安全變量中,然後使用UI定時器定期更新UI。這樣,當主UI線程停用服務器時,I/O線程不會被阻塞。

  3. 使用另一個線程停用服務器,以便主UI線程可以在停用忙時繼續處理同步請求。

+1

謝謝,我用PostMessage和GlobalAlloc發送數據到UI窗口。 – Vahid

+0

只要確保使用持久HWND,如從Application.Handle或AllocateHWnd()。不要使用UI控件的'Handle'屬性,如TForm。不保證在線程的生命週期中持久化。 –

+0

好的,非常感謝。 – Vahid