什么应该是一个简单的TCP客户端服务器,STUCK

时间:2017-09-02 04:02:45

标签: delphi client-server firemonkey indy10

我被困住了。我以前使用过服务器和客户端套接字(非indy),它们很好地异步并且易于使用。我正在尝试制作一个简单的客户端服务器。连接到客户端后,服务器会在计时器事件上发送小块数据。时间是77ms,数据块大约是100字节。 我尽力在这里和那里以及人们的例子中找出indy。 (显然,如果人们能够迅速开展工作,那么在网络上就不会有太多关于它的内容)。下面我从客户端和服务器中提取了相关部分。

我将ip设置为localhost,我试图首先调试服务器端。我启动客户端,我在调试器中启动服务器。客户端连接,我可以在onconnect事件上设置一个断点,程序就在那里。现在应该发生的是计时器已启用,然后每次跳闸我发送我的数据块。 (这至少在调试器中没有发生)

编译器强迫我为服务器添加onexecute事件,我现在不知道如何处理它。我尝试了一个空白事件,我尝试了一个虚拟读取。 在客户端。一旦有连接它就应该从别处复制一个读线程,并在数据块发生时读取它们。现在我没有进行任何错误检查或自动检测连接丢失和尝试重新连接。

我当然感谢任何帮助,无论是在开展这项工作还是提示如何处理角落案件。

感谢 罗伯特

服务器端这应该每77毫秒发送一个数据块 .................................................. ..................................

type
  TForm8 = class(TForm)
    SendButton: TButton;
    SendWaveFormTimer: TTimer;
    IdUDPClient1: TIdUDPClient;
    IdTCPServer1: TIdTCPServer;

    procedure SendWaveFormTimerTimer(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure IdTCPServer1Connect(AContext: TIdContext);
    procedure IdTCPServer1Execute(AContext: TIdContext);

  private
    connectedto:TIdContext;
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form8: TForm8;


procedure TForm8.IdTCPServer1Connect(AContext: TIdContext);
// this is not in the main ui thread and can't call showmessage
begin
  connectedto:=acontext; // save the connected TIDcontext for use elsewhere?
  self.SendWaveFormTimer.Enabled:=true; {set the send timer}
end;

procedure TForm8.SendWaveFormTimerTimer(Sender: TObject);

var tempbyte:tidbytes;

begin
  tempbyte:=RawToBytes(WaveFormSample,sizeof(TWaveFormSample));
  form8.connectedto.Connection.IOHandler.Write(tempbyte, sizeof(TWaveFormSample));
end;

procedure TForm8.IdTCPServer1Execute(AContext: TIdContext);
// again this in second thread
var recv:string;
begin
  //I don't know what to put here I am not expecting anything right now
  recv := AContext.Connection.Socket.ReadLn;

end;

客户端

type
  TForm3 = class(TForm)
    IdUDPServer1: TIdUDPServer;
    Label1: TLabel;
    IdTCPClient1: TIdTCPClient;
    Button1: TButton;
    procedure IdUDPServer1UDPRead(AThread: TIdUDPListenerThread;
    const AData: TIdBytes; ABinding: TIdSocketHandle);
    procedure FormActivate(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;


TReadingThread = class(TThread)
protected
  FConn: TIdTCPConnection;
  procedure Execute; override;
public
  constructor Create(ACon: TIdTCPConnection); reintroduce;
end;

type
  TMyNotify = class(TidNotify)
  private
    recvd_block:TWaveFormSample;
  protected
    procedure DoNotify; override;
  end;

var
  Form3: TForm3;
  Udp_message_ID:tmessageid;
  UDp_WaveFormSample:twaveformsample;

  ReadingThread: TReadingThread = nil;


implementation

constructor TReadingThread.Create(ACon: TIdTCPConnection);
begin
  FConn := ACon;
  inherited Create(False);
end;



procedure TForm3.IdTCPClient1Connected(Sender: TObject);
begin
  readingthread.Create(IdTCPClient1);
  readingthread.execute; //this must be wrong
end;


procedure tform3.Button1Click(Sender: TObject);
begin
  IdTCPClient1.Connect; //this works on the other end
end;


procedure TReadingThread.Execute;
var
  TCP_TWaveFormSample:twaveformsample;
  AData: TIdBytes;
  data_rdy:boolean;
  MyNotify: TMyNotify;
begin
  MyNotify := TMyNotify.Create;

  MyNotify.Notify;
  data_rdy:=false;

  while not Terminated do
  begin

    FConn.IOHandler.CheckForDataOnSource(1000);
    FConn.IOHandler.CheckForDisconnect;
    if not FConn.IOHandler.InputBufferIsEmpty and not data_rdy then
    begin
      fconn.iohandler.ReadBytes(Adata,sizeof(TWaveFormSample));
      Idglobal.BytesToRaw(AData, TCP_message_ID, sizeof(tmessageid)); //global load
      Idglobal.BytesToRaw(AData, TCP_TWaveFormSample, sizeof(twaveformsample));
      data_rdy:=true;
    end; // data
    If not Drawing and data_rdy then //make sure the other thread can take it
    begin
      MyNotify.recvd_block :=Udp_TWaveFormSample;
      MyNotify.Notify;
      data_rdy:=false;
    end;

  end;
end ;

procedure TMyNotify.DoNotify;
begin
  waveformunit.writenewdata (recvd_block.WaveformIndex,recvd_block.WaveformData,
  samples_per_send);
  drawing:=false;
end;

1 个答案:

答案 0 :(得分:5)

TIdTCPServer是一个多线程组件。它的事件在工作线程中被触发。您的计时器不起作用,因为TTimer是基于消息的计时器,并且这些线程中没有消息循环来为其提供服务。

您应该在服务器的OnExecute事件中与客户端进行I / O工作,该事件在管理客户端连接的线程中触发,而不是在主UI线程中触发。

您在服务器端显示的所有内容都是错误的。尝试更像这样的东西:

interface

...

type
  TForm8 = class(TForm)
    IdTCPServer1: TIdTCPServer;
    procedure FormCreate(Sender: TObject);
    procedure IdTCPServer1Connect(AContext: TIdContext);
    procedure IdTCPServer1Execute(AContext: TIdContext);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form8: TForm8;

implementation

type
  TMyContext = class(TIdServerContext)
    LastSend: TIdTicks;
  end;

procedure TForm8.FormCreate(Sender: TObject);
begin
  IdTCPServer1.ContextClass := TMyContext;
end;

procedure TForm8.IdTCPServer1Connect(AContext: TIdContext);
begin
   TMyConext(AContext).LastSend := Ticks64;
end;

procedure TForm8.IdTCPServer1Execute(AContext: TIdContext);
var
  Ctx: TMyContext;
  temp: TIdBytes;
begin
  Ctx := TMyContext(AContext);

  if GetElapsedTicks(Ctx.LastSend) >= 77 then
  begin
    // grab the latest sample and send it... 
    temp := RawToBytes(Waveformsample, sizeof(TWaveFormSample));
    AContext.Connection.IOHandler.Write(temp);
    Ctx.LastSend := Ticks64;
  end;

  // in case the client sends something, just ignore it for now...
  AContext.Connection.IOHandler.InputBuffer.Clear;

  Sleep(0);
end;

话虽如此,由于您的服务器只需要一个客户端,您可能会考虑使用TIdSimpleServer,而不是多线程,因此如果您愿意,可以从主UI线程运行它。但是,如果您想为多个客户提供服务,请坚持使用TIdTCPServer

在客户端,你所展示的一切也都是错的。您滥用TThreadIOHandlerTIdNotify。尝试更像这样的东西:

interface

...

type
  TForm3 = class(TForm)
    IdTCPClient1: TIdTCPClient;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
    private
      { Private declarations }
    public
      { Public declarations }
    end;

var
  Form3: TForm3;

implementation

type
  TReadingThread = class(TThread)
  protected
    FConn: TIdTCPConnection;
    procedure Execute; override;
  public
    constructor Create(ACon: TIdTCPConnection); reintroduce;
  end;

  TMyNotify = class(TIdNotify)
  private
    recvd_block: TWaveFormSample;
  protected
    procedure DoNotify; override;
  end;

var
  ReadingThread: TReadingThread = nil;

constructor TReadingThread.Create(ACon: TIdTCPConnection);
begin
  FConn := ACon;
  inherited Create(False);
end;

procedure TForm3.IdTCPClient1Connected(Sender: TObject);
begin
  ReadingThread := TReadingThread.Create(IdTCPClient1);
end;

procedure Tform3.Button1Click(Sender: TObject);
begin
  IdTCPClient1.Connect;
end;

procedure TReadingThread.Execute;
var
  WaveFormSample: TWaveFormSample;
  AData: TIdBytes;
  MyNotify: TMyNotify;
begin
  while not Terminated do
  begin
    FConn.IOHandler.ReadBytes(AData, sizeof(TWaveFormSample), False);
    BytesToRaw(AData, WaveFormSample, sizeof(TWaveFormSample));
    MyNotify := TMyNotify.Create;
    MyNotify.recvd_block := WaveFormSample;
    MyNotify.Notify;
  end;
end;

procedure TMyNotify.DoNotify;
begin
  // use recvd_block as needed...
end;