TIdTCPServer 第二次连接时卡住

发布于 2024-12-25 07:25:42 字数 1287 浏览 1 评论 0原文

我正在使用 TIdTCPClient 获取文件列表,一切正常,但是当我关闭客户端应用程序并再次打开它时,尽管 TIdTCPClient 成功连接到它,但我在 TCPServer onExecute 上没有收到任何事件,并且我无法发送文件列表。

我做错了什么?

一些代码:

procedure TTaifun.csConnected(Sender: TObject);
begin
  cs.IOHandler.WriteLn('FILE_LIST|NOW'); //Sending a request to server for the files
  fname := cs.IOHandler.ReadLn(nil); //Got the file names
  files := ExplodeString(fname,'|'); // Parse the files
end;

procedure TTaifun.svExecute(AContext: TIdContext);
var
  cmds, flist: TStringList;
  i: Integer;
  tmp: string;
  FS : TFileStream;
begin
  CSection.Enter; //Enter critical section
  cmds := ExplodeString(AContext.Connection.IOHandler.ReadLn(nil), '|'); 
  try
    if cmds[0] = 'FILE_LIST' then //Check command received
    begin
      flist := TStringList.Create;
      flist.LoadFromFile(MyPath + 'files.dat');
      tmp := '';
      for i := 0 to flist.Count - 1 do
      begin
          tmp := tmp + flist[i] + ',' + GetFileSize(flist[i]) + ',' +
          BoolToStr(FileExists(MyPath + 'Thumbs\' +
          ChangeFileExt(ExtractFileName(flist[i]), '.thb')),true) + '|'; //Do some parsing
      end;
      AContext.Connection.IOHandler.WriteLn(tmp); //Send the string
    end
 finally
  CSection.Leave; //Leave critical section
 end;
end;

I am using TIdTCPClient to get a file list all works well but when i close the client application and open it again i don't receive any event on TCPServer onExecute though the TIdTCPClient successfully connects to it and i am not able to send the file list.

What am i doing wrong ?

Some code :

procedure TTaifun.csConnected(Sender: TObject);
begin
  cs.IOHandler.WriteLn('FILE_LIST|NOW'); //Sending a request to server for the files
  fname := cs.IOHandler.ReadLn(nil); //Got the file names
  files := ExplodeString(fname,'|'); // Parse the files
end;

procedure TTaifun.svExecute(AContext: TIdContext);
var
  cmds, flist: TStringList;
  i: Integer;
  tmp: string;
  FS : TFileStream;
begin
  CSection.Enter; //Enter critical section
  cmds := ExplodeString(AContext.Connection.IOHandler.ReadLn(nil), '|'); 
  try
    if cmds[0] = 'FILE_LIST' then //Check command received
    begin
      flist := TStringList.Create;
      flist.LoadFromFile(MyPath + 'files.dat');
      tmp := '';
      for i := 0 to flist.Count - 1 do
      begin
          tmp := tmp + flist[i] + ',' + GetFileSize(flist[i]) + ',' +
          BoolToStr(FileExists(MyPath + 'Thumbs\' +
          ChangeFileExt(ExtractFileName(flist[i]), '.thb')),true) + '|'; //Do some parsing
      end;
      AContext.Connection.IOHandler.WriteLn(tmp); //Send the string
    end
 finally
  CSection.Leave; //Leave critical section
 end;
end;

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

梦太阳 2025-01-01 07:25:42

您没有保护您的关键部分免受异常影响。当客户端断开连接时,ReadLn()WriteLn()(取决于时间)将引发异常,以终止该客户端的线程。下次为不同的线程调用 OnExecute 事件时,临界区仍将被锁定并且无法再次重新进入,从而使代码陷入死锁。在代码中添加 try/finally 来防止这种情况发生,例如:

procedure TTaifun.svExecute(AContext: TIdContext); 
var 
  ...
begin 
  CSection.Enter; //Enter critical section 
  try
    ...
  finally
    CSection.Leave; //Leave critical section 
  end;
end; 

话虽如此,为什么要使用临界区呢?您展示的代码本身是线程安全的,不需要保护它免受并发访问:

procedure TTaifun.svExecute(AContext: TIdContext);     
var     
  cmds, flist: TStringList;     
  i: Integer;     
  tmp: string;     
begin     
  cmds := ExplodeString(AContext.Connection.IOHandler.ReadLn, '|');      
  if cmds[0] = 'FILE_LIST' then //Check command received     
  begin     
    tmp := '';     
    flist := TStringList.Create;     
    try
      flist.LoadFromFile(MyPath + 'files.dat');     
      for i := 0 to flist.Count - 1 do     
      begin     
        tmp := tmp + flist[i] + ',' + GetFileSize(flist[i]) + ',' +     
          BoolToStr(FileExists(MyPath + 'Thumbs\' +     
          ChangeFileExt(ExtractFileName(flist[i]), '.thb')),true) + '|'; //Do some parsing     
      end;
    finally
      flist.Free;
    end;     
    AContext.Connection.IOHandler.WriteLn(tmp); //Send the string     
  end;
end;

或者:

procedure TTaifun.svExecute(AContext: TIdContext);     
var     
  cmds, flist: TStringList;     
  i: Integer;     
begin     
  cmds := ExplodeString(AContext.Connection.IOHandler.ReadLn, '|');      
  if cmds[0] = 'FILE_LIST' then //Check command received     
  begin     
    flist := TStringList.Create;     
    try
      flist.LoadFromFile(MyPath + 'files.dat');     
      for i := 0 to flist.Count - 1 do     
      begin     
        flist[i] := flist[i] + ',' + GetFileSize(flist[i]) + ',' +     
          BoolToStr(FileExists(MyPath + 'Thumbs\' +     
          ChangeFileExt(ExtractFileName(flist[i]), '.thb')),true); //Do some parsing     
      end;
      flist.Delimiter := '|';
      flist.StrictDelimiter := True;
      AContext.Connection.IOHandler.WriteLn(flist.DelimitedText); //Send the string     
    finally
      flist.Free;
    end;     
  end;
end;

You are not protecting your critical section from exceptions. When the client disconnects, an exception will be raised by either ReadLn() or WriteLn() (depending on timing) to terminate the thread for that client. The next time the OnExecute event is called for a different thread, the critical section will still be locked and cannot be re-entered again, deadlocking your code. Add a try/finally to your code to guard against that, eg:

procedure TTaifun.svExecute(AContext: TIdContext); 
var 
  ...
begin 
  CSection.Enter; //Enter critical section 
  try
    ...
  finally
    CSection.Leave; //Leave critical section 
  end;
end; 

With that said, why are you using a critical section to begin with? The code you showed is thread-safe by itself, it does not need to be protected from concurrent access:

procedure TTaifun.svExecute(AContext: TIdContext);     
var     
  cmds, flist: TStringList;     
  i: Integer;     
  tmp: string;     
begin     
  cmds := ExplodeString(AContext.Connection.IOHandler.ReadLn, '|');      
  if cmds[0] = 'FILE_LIST' then //Check command received     
  begin     
    tmp := '';     
    flist := TStringList.Create;     
    try
      flist.LoadFromFile(MyPath + 'files.dat');     
      for i := 0 to flist.Count - 1 do     
      begin     
        tmp := tmp + flist[i] + ',' + GetFileSize(flist[i]) + ',' +     
          BoolToStr(FileExists(MyPath + 'Thumbs\' +     
          ChangeFileExt(ExtractFileName(flist[i]), '.thb')),true) + '|'; //Do some parsing     
      end;
    finally
      flist.Free;
    end;     
    AContext.Connection.IOHandler.WriteLn(tmp); //Send the string     
  end;
end;

Alternatively:

procedure TTaifun.svExecute(AContext: TIdContext);     
var     
  cmds, flist: TStringList;     
  i: Integer;     
begin     
  cmds := ExplodeString(AContext.Connection.IOHandler.ReadLn, '|');      
  if cmds[0] = 'FILE_LIST' then //Check command received     
  begin     
    flist := TStringList.Create;     
    try
      flist.LoadFromFile(MyPath + 'files.dat');     
      for i := 0 to flist.Count - 1 do     
      begin     
        flist[i] := flist[i] + ',' + GetFileSize(flist[i]) + ',' +     
          BoolToStr(FileExists(MyPath + 'Thumbs\' +     
          ChangeFileExt(ExtractFileName(flist[i]), '.thb')),true); //Do some parsing     
      end;
      flist.Delimiter := '|';
      flist.StrictDelimiter := True;
      AContext.Connection.IOHandler.WriteLn(flist.DelimitedText); //Send the string     
    finally
      flist.Free;
    end;     
  end;
end;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文