2016-06-14 22 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