Delphi 什么是简单的TCP客户端服务器

Delphi 什么是简单的TCP客户端服务器,delphi,client-server,firemonkey,indy10,Delphi,Client Server,Firemonkey,Indy10,我被卡住了。我以前使用过服务器和客户机套接字(非indy),它们非常异步且易于使用。我正在尝试制作一个简单的客户机-服务器。一旦连接到客户机,服务器就会在计时器事件上发送小数据块。时间为77ms,数据块约为100字节。 我已经尽了最大的努力从这里那里的点点滴滴和人们的例子中找出了印第。(很明显,如果人们能够快速地让它工作起来,网络上就不会有那么多关于它的内容了)。下面我从我的客户机和服务器中提取了相关部分 我将ip设置为localhost,并尝试首先调试服务器端。我在调试器中启动客户机和服务器。

我被卡住了。我以前使用过服务器和客户机套接字(非indy),它们非常异步且易于使用。我正在尝试制作一个简单的客户机-服务器。一旦连接到客户机,服务器就会在计时器事件上发送小数据块。时间为77ms,数据块约为100字节。 我已经尽了最大的努力从这里那里的点点滴滴和人们的例子中找出了印第。(很明显,如果人们能够快速地让它工作起来,网络上就不会有那么多关于它的内容了)。下面我从我的客户机和服务器中提取了相关部分

我将ip设置为localhost,并尝试首先调试服务器端。我在调试器中启动客户机和服务器。客户端连接,我可以在onconnect事件中设置一个断点,然后程序就会转到那里。现在应该发生的是定时器被启用,然后每次它跳闸时,我都会发送我的数据块。(至少在调试器中不会发生这种情况)

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

我当然会感谢任何帮助,无论是在让这个工作,也在如何处理角落案件的提示

谢谢 罗伯特

服务器端应该每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;

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;
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;

所述,因为服务器只希望有1个客户端,所以可以考虑使用<代码> TIDSimeSeServer < /Cord>,这不是多线程的,所以如果您愿意,可以从主UI线程运行它。但如果您想为多个客户端提供服务,请坚持使用

TIdTCPServer

在客户端,您显示的所有内容也都是错误的。您误用了
TThread
IOHandler
TIdNotify
。尝试类似以下内容:

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;
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;

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;
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;

所述,因为服务器只希望有1个客户端,所以可以考虑使用<代码> TIDSimeSeServer < /Cord>,这不是多线程的,所以如果您愿意,可以从主UI线程运行它。但如果您想为多个客户端提供服务,请坚持使用

TIdTCPServer

在客户端,您显示的所有内容也都是错误的。您误用了
TThread
IOHandler
TIdNotify
。尝试类似以下内容:

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;
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;

readingthread.execute//这一定是错误的
是的,它是错误的。大错特错。这个网站上有很多关于如何正确使用线程的问题,我建议你从这里开始:你没有提供一个“完整的”或“最小的”或易于验证的;您也没有费心尽最小的努力来格式化代码以使其易读。因此,我不想再做进一步的调查了。
readingthread.execute//这一定是错误的
是的,它是错误的。大错特错。这个网站上有很多关于如何正确使用线程的问题,我建议你从这里开始:你没有提供一个“完整的”或“最小的”或易于验证的;您也没有费心尽最小的努力来格式化代码以使其易读。所以我不想投入更多的精力去做进一步的调查,我在代码开始编译的时候尽可能地投入到Remy的代码中。我不得不在读取时将append set添加到false,当我使用get-elapsedticks函数时,indy运行时不断给出错误。所以我使用了一个外部计数器的状态标志..127.0.0.1的一切都很顺利..只要我把它放到一个真正的网络上(只有它上面的两台计算机),它就毫无价值了..它正在丢失记录。我将清理我的代码的基本内容,并张贴它。。。但是我放弃了。在我看来,Indy和10.21 studio确实存在一些基本的bug。可能是线程同步问题,例如我正在通过读卡器和发送线程从主线程访问变量。但我已经设置了基本的标志,以防止这种情况在一级发生。但这就是为什么在切换到真正的以太网硬件和本地主机时性能下降的原因吗?我很感激雷米的一切help@rebible:关于
Append
参数的好理解。但是在
GetElapsedTicks()
函数上会出现什么错误?TCP不能丢失记录,因此必须进行其他操作。要么您没有正确发送数据包,要么您没有正确读取数据包。印地可能有一些复杂性/灵活性,你是n