运行压力测试时出现tcpserver x tcpclient问题

时间:2014-12-26 20:34:29

标签: delphi delphi-xe2 tcpclient indy10 ttcpserver

我需要有关TCPServer和TcpClient问题的帮助。我正在使用Delphi XE2和Indy 10.5。

我根据流行的屏幕捕获程序制作了服务器和客户端程序:

ScreenThief - stealing screen shots over the Network

我的客户端程序将.zip文件和一些数据发送到服务器。这通常可以单独工作几次,但是如果我把它放到压力测试中,通过定时器在5秒钟内执行5次传输,完全在尝试#63时,客户端无法再连接到服务器:

  

套接字错误#10053
  软件导致中止连接。

显然,似乎服务器资源耗尽,无法再接受客户端连接。

在出现错误消息后,我无法以任何方式连接到服务器 - 不是在单独的测试中,而是在压力测试中。即使我退出并重新启动客户端,错误仍然存​​在。我必须退出并重新启动服务器,然后客户端才能再次连接。

有时套接字错误#10054发生在客户端,这会使服务器完全崩溃并且必须重新启动。

我不知道发生了什么事。我只知道如果必须不时重新启动服务器,它就不是一个强大的服务器。

以下是客户端和服务器的来源,以便您可以测试它们:

http://www.mediafire.com/download/m5hjw59kmscln7v/ComunicaTest.zip

运行服务器,运行客户端,然后选中"只需检查运行无限"。在测试中,服务器在localhost中运行。

任何人都可以帮助我吗?雷米·勒博(Remy Lebeau)?

1 个答案:

答案 0 :(得分:3)

我发现您的客户端代码存在问题。

  1. 您在调用TCPClient.OnConnected后分配TCPClient.OnDisconnectedTCPClient.Connect()个事件处理程序。您应该在致电Connect()之前分配它们。

  2. 您在发送所有数据后分配TCPClient.IOHandler.DefStringEncoding 。您应该在发送任何数据之前进行设置。

  3. 您将.zip文件大小作为字节发送,但随后使用TStringStream发送实际文件内容。您需要使用TFileStreamTMemoryStream代替。此外,您可以从流中获取文件大小,在创建流之前,您不必查询文件大小。

  4. 您完全没有错误处理。如果在btnRunClick()运行时引发任何异常,则表示您正在泄漏TIdTCPClient对象,而不是将其与服务器断开连接。

  5. 我也看到了服务器代码的一些问题:

    1. 您的OnCreate事件在创建Clients列表之前激活了服务器。

    2. TThread.LockList()TThreadList.Unlock()的各种滥用。

    3. 不必要地使用InputBufferIsEmpty()TRTLCriticalSection

    4. 缺乏错误处理。

    5. 使用TIdAntiFreeze,这对服务器没有影响。

    6. 请改为尝试:

      客户端:

      unit ComunicaClientForm;
      
      interface
      
      uses
        Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
        Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, IdBaseComponent,
        IdAntiFreezeBase, Vcl.IdAntiFreeze, Vcl.Samples.Spin, Vcl.ExtCtrls,
        IdComponent, IdTCPConnection, IdTCPClient,  idGlobal;
      
      type
        TfrmComunicaClient = class(TForm)
          memoIncomingMessages: TMemo;
          IdAntiFreeze: TIdAntiFreeze;
          lblProtocolLabel: TLabel;
          Timer: TTimer;
          grp1: TGroupBox;
          grp2: TGroupBox;
          btnRun: TButton;
          chkIntervalado: TCheckBox;
          spIntervalo: TSpinEdit;
          lblFrequencia: TLabel;
          lbl1: TLabel;
          lbl2: TLabel;
          lblNumberExec: TLabel;
          procedure FormCreate(Sender: TObject);
          procedure TCPClientConnected(Sender: TObject);
          procedure TCPClientDisconnected(Sender: TObject);
          procedure TimerTimer(Sender: TObject);
          procedure chkIntervaladoClick(Sender: TObject);
          procedure btnRunClick(Sender: TObject);
        private
          { Private declarations }
        public
          { Public declarations }
        end;
      
      var
        frmComunicaClient: TfrmComunicaClient;
      
      implementation
      
      {$R *.dfm}
      
      const
        DefaultServerIP = '127.0.0.1';
        DefaultServerPort = 7676;
      
      procedure TfrmComunicaClient.FormCreate(Sender: TObject);
      begin
        memoIncomingMessages.Clear;
      end;
      
      procedure TfrmComunicaClient.TCPClientConnected(Sender: TObject);
      begin
        memoIncomingMessages.Lines.Insert(0,'Connected to Server');
      end;
      
      procedure TfrmComunicaClient.TCPClientDisconnected(Sender: TObject);
      begin
        memoIncomingMessages.Lines.Insert(0,'Disconnected from Server');
      end;
      
      procedure TfrmComunicaClient.TimerTimer(Sender: TObject);
      begin
        Timer.Enabled := False;
        btnRun.Click;
        Timer.Enabled := True;
      end;
      
      procedure TfrmComunicaClient.chkIntervaladoClick(Sender: TObject);
      begin
        Timer.Interval := spIntervalo.Value * 1000;
        Timer.Enabled := True;
      end;
      
      procedure TfrmComunicaClient.btnRunClick(Sender: TObject);
      var
        Size        : Int64;
        fStrm       : TFileStream;
        NomeArq     : String;
        Retorno     : string;
        TipoRetorno : Integer; // 1 - Anvisa, 2 - Exception
        TCPClient   : TIdTCPClient;    
      begin
        memoIncomingMessages.Lines.Clear;
      
        TCPClient := TIdTCPClient.Create(nil);
        try
          TCPClient.Host := DefaultServerIP;
          TCPClient.Port := DefaultServerPort;
          TCPClient.ConnectTimeout := 3000;
          TCPClient.OnConnected := TCPClientConnected;
          TCPClient.OnDisconnected := TCPClientDisconnected;
      
          TCPClient.Connect;
          try
            TCPClient.IOHandler.DefStringEncoding := TIdTextEncoding.UTF8;
      
            TCPClient.IOHandler.WriteLn('SendArq'); // Sinaliza Envio
            TCPClient.IOHandler.WriteLn('1'); // Envia CNPJ
            TCPClient.IOHandler.WriteLn('email@gmail.com'); // Envia Email
            TCPClient.IOHandler.WriteLn('12345678'); // Envia Senha
            TCPClient.IOHandler.WriteLn('12345678901234567890123456789012'); // Envia hash
            memoIncomingMessages.Lines.Insert(0,'Write first data : ' + DateTimeToStr(Now));
      
            NomeArq := ExtractFilePath(Application.ExeName) + 'arquivo.zip';
            fStrm := TFileStream.Create(NomeArq, fmOpenRead or fmShareDenyWrite);
            try
              Size := fStrm.Size;
              TCPClient.IOHandler.WriteLn(IntToStr(Size));
              if Size > 0 then begin
                TCPClient.IOHandler.Write(fStrm, Size, False);
              end;
            finally
              fStrm.Free;
            end;
            memoIncomingMessages.Lines.Insert(0,'Write file: ' + DateTimeToStr(Now) + ' ' +IntToStr(Size)+ ' bytes');
            memoIncomingMessages.Lines.Insert(0,'************* END *********** ' );
            memoIncomingMessages.Lines.Insert(0,'  ');
      
            // Recebe Retorno da transmissão
            TipoRetorno := StrToInt(TCPClient.IOHandler.ReadLn);
            Retorno := TCPClient.IOHandler.ReadLn;
      
            //making sure!
            TCPClient.IOHandler.ReadLn;
          finally
            TCPClient.Disconnect;
          end;
        finally
          TCPClient.Free;
        end;
      
        lblNumberExec.Caption := IntToStr(StrToInt(lblNumberExec.Caption) + 1);
      end;
      
      end.
      

      服务器:

      unit ComunicaServerForm;
      
      interface
      
      uses
        Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
        Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls,
        IdCustomTCPServer, IdTCPServer, IdScheduler, IdSchedulerOfThread,
        IdSchedulerOfThreadPool, IdBaseComponent, IdSocketHandle, Vcl.ExtCtrls,
        IdStack, IdGlobal, Inifiles, System.Types, IdContext, IdComponent;
      
      
      type
        TfrmComunicaServer = class(TForm)
          txtInfoLabel: TStaticText;
          mmoProtocol: TMemo;
          grpClientsBox: TGroupBox;
          lstClientsListBox: TListBox;
          grpDetailsBox: TGroupBox;
          mmoDetailsMemo: TMemo;
          lblNome: TLabel;
          TCPServer: TIdTCPServer;
          ThreadManager: TIdSchedulerOfThreadPool;
          procedure lstClientsListBoxClick(Sender: TObject);
          procedure FormCreate(Sender: TObject);
          procedure FormClose(Sender: TObject; var Action: TCloseAction);
          procedure TCPServerConnect(AContext: TIdContext);
          procedure TCPServerDisconnect(AContext: TIdContext);
          procedure TCPServerExecute(AContext: TIdContext);
        private
          { Private declarations }
          procedure RefreshListDisplay;
          procedure RefreshListBox;
        public
          { Public declarations }
        end;
      
      var
        frmComunicaServer: TfrmComunicaServer;
      
      implementation
      
      {$R *.dfm}
      
      type
        TClient = class(TIdServerContext)
        public
          PeerIP      : string;            { Client IP address }
          HostName    : String;            { Hostname }
          Connected,                       { Time of connect }
          LastAction  : TDateTime;         { Time of last transaction }
        end;
      
      const
        DefaultServerIP = '127.0.0.1';
        DefaultServerPort = 7676;
      
      procedure TfrmComunicaServer.FormCreate(Sender: TObject);
      begin
        TCPServer.ContextClass := TClient;
      
        TCPServer.Bindings.Clear;
        with TCPServer.Bindings.Add do
        begin
          IP := DefaultServerIP;
          Port := DefaultServerPort;
        end;
      
        //setup TCPServer
        try
          TCPServer.Active := True;
        except
          on E: Exception do
            ShowMessage(E.Message);
        end;
      
        txtInfoLabel.Caption := 'Aguardando conexões...';
        RefreshListBox;
      
        if TCPServer.Active then begin
          mmoProtocol.Lines.Add('Comunica Server executando em ' + TCPServer.Bindings[0].IP + ':' + IntToStr(TCPServer.Bindings[0].Port));
        end;
      end;
      
      procedure TfrmComunicaServer.FormClose(Sender: TObject; var Action: TCloseAction);
      var
        ClientsCount : Integer;
      begin
        with TCPServer.Contexts.LockList do
        try
          ClientsCount := Count;
        finally
          TCPServer.Contexts.UnlockList;
        end;
      
        if ClientsCount > 0 then
        begin
          Action := caNone;
          ShowMessage('Há clientes conectados. Ainda não posso sair!');
          Exit;
        end;
      
        try
          TCPServer.Active := False;
        except
        end;
      end;
      
      procedure TfrmComunicaServer.TCPServerConnect(AContext: TIdContext);
      var
        DadosConexao : TClient;
      begin
        DadosConexao := TClient(AContext);
      
        DadosConexao.PeerIP      := AContext.Connection.Socket.Binding.PeerIP;
        DadosConexao.HostName    := GStack.HostByAddress(DadosConexao.PeerIP);
        DadosConexao.Connected   := Now;
        DadosConexao.LastAction  := DadosConexao.Connected;
      
        (*
        TThread.Queue(nil,
          procedure
          begin
            MMOProtocol.Lines.Add(TimeToStr(Time) + ' Abriu conexão de "' + DadosConexao.HostName + '" em ' + DadosConexao.PeerIP);
          end
        );
        *)
      
        RefreshListBox;
        AContext.Connection.IOHandler.DefStringEncoding := TIdTextEncoding.UTF8;
      end;    
      
      procedure TfrmComunicaServer.TCPServerDisconnect(AContext: TIdContext);
      var
        DadosConexao : TClient;
      begin
        DadosConexao := TClient(AContext);
      
        (*
        TThread.Queue(nil,
          procedure
          begin
            MMOProtocol.Lines.Add(TimeToStr(Time) + ' Desconnectado de "' + DadosConexao.HostName + '"');
          end
        );
        *)
      
        RefreshListBox;
      end;    
      
      procedure TfrmComunicaServer.TCPServerExecute(AContext: TIdContext);
      var
        DadosConexao : TClient;
        CNPJ         : string;
        Email        : string;
        Senha        : String;
        Hash         : String;
        Size         : Int64;
        FileName     : string;
        Arquivo      : String;
        ftmpStream   : TFileStream;
        Cmd          : String;
        Retorno      : String;
        TipoRetorno  : Integer;   // 1 - Anvisa, 2 - Exception
      begin
        DadosConexao := TClient(AContext);
      
        Cmd := AContext.Connection.IOHandler.ReadLn;
      
        if Cmd = 'SendArq' then
        begin
          CNPJ  := AContext.Connection.IOHandler.ReadLn;
          Email := AContext.Connection.IOHandler.ReadLn;
          Senha := AContext.Connection.IOHandler.ReadLn;
          Hash  := AContext.Connection.IOHandler.ReadLn;
          Size  := StrToInt64(AContext.Connection.IOHandler.ReadLn);
      
          // Recebe Arquivo do Client
          FileName := ExtractFilePath(Application.ExeName) + 'Arquivos\' + CNPJ + '-Arquivo.ZIP';
          fTmpStream := TFileStream.Create(FileName, fmCreate);
          try
            if Size > 0 then begin
              AContext.Connection.IOHandler.ReadStream(fTmpStream, Size, False);
            end;
          finally
            fTmpStream.Free;
          end;
      
          // Transmite arquivo para a ANVISA
          Retorno     := 'File Transmitted with sucessfull';
          TipoRetorno := 1;
      
          // Grava Log
          fTmpStream := TFileStream.Create(ExtractFilePath(Application.ExeName) + 'Arquivos\' + CNPJ + '.log', fmCreate);
          try
            WriteStringToStream(ftmpStream, Retorno, TIdTextEncoding.UTF8);
          finally
            fTmpStream.Free;
          end;    
      
          // Envia Retorno da ANVISA para o Client
          AContext.Connection.IOHandler.WriteLn(IntToStr(TipoRetorno));  // Tipo do retorno (Anvisa ou Exception)
          AContext.Connection.IOHandler.WriteLn(Retorno);                // Msg de retorno
      
          // Sinaliza ao Client que terminou o processo
          AContext.Connection.IOHandler.WriteLn('DONE');
        end;
      end;
      
      procedure TfrmComunicaServer.lstClientsListBoxClick(Sender: TObject);
      var
        DadosConexao: TClient;
        Index: Integer;
      begin
        mmoDetailsMemo.Clear;
      
        Index := lstClientsListBox.ItemIndex;
        if Index <> -1 then
        begin
          DadosConexao := TClient(lstClientsListBox.Items.Objects[Index]);
          with TCPServer.Contexts.LockList do
          try
            if IndexOf(DadosConexao) <> -1 then
            begin
              mmoDetailsMemo.Lines.Add('IP : ' + DadosConexao.PeerIP);
              mmoDetailsMemo.Lines.Add('Host name : ' + DadosConexao.HostName);
              mmoDetailsMemo.Lines.Add('Conectado : ' + DateTimeToStr(DadosConexao.Connected));
              mmoDetailsMemo.Lines.Add('Ult. ação : ' + DateTimeToStr(DadosConexao.LastAction));
            end;
          finally
            TCPServer.Contexts.UnlockList;
          end;
        end;
      end;
      
      procedure TfrmComunicaServer.RefreshListDisplay;
      var
        Client : TClient;
        i: Integer;
      begin
        lstClientsListBox.Clear;
        mmoDetailsMemo.Clear;
      
        with TCPServer.Contexts.LockList do
        try
          for i := 0 to Count-1 do
          begin
            Client := TClient(Items[i]);
            lstClientsListBox.AddItem(Client.HostName, Client);
          end;
        finally
          TCPServer.Contexts.UnlockList;
        end;
      end;    
      
      procedure TfrmComunicaServer.RefreshListBox;
      begin
        if GetCurrentThreadId = MainThreadID then
          RefreshListDisplay
        else
          TThread.Queue(nil, RefreshListDisplay);
      end;
      
      end.