Delphi 什么是简单的TCP客户端服务器
我被卡住了。我以前使用过服务器和客户机套接字(非indy),它们非常异步且易于使用。我正在尝试制作一个简单的客户机-服务器。一旦连接到客户机,服务器就会在计时器事件上发送小数据块。时间为77ms,数据块约为100字节。 我已经尽了最大的努力从这里那里的点点滴滴和人们的例子中找出了印第。(很明显,如果人们能够快速地让它工作起来,网络上就不会有那么多关于它的内容了)。下面我从我的客户机和服务器中提取了相关部分 我将ip设置为localhost,并尝试首先调试服务器端。我在调试器中启动客户机和服务器。客户端连接,我可以在onconnect事件中设置一个断点,然后程序就会转到那里。现在应该发生的是定时器被启用,然后每次它跳闸时,我都会发送我的数据块。(至少在调试器中不会发生这种情况) 编译器强迫我为服务器添加onexecute事件,我现在不知道该怎么办。我尝试了一个空白事件和一个虚拟readln。 在客户端。我从别处复制了一个读取线程,一旦有了连接,它就应该启动并读取数据块。现在我没有做任何错误检查或自动检测连接丢失和尝试重新连接 我当然会感谢任何帮助,无论是在让这个工作,也在如何处理角落案件的提示 谢谢 罗伯特 服务器端应该每77毫秒发送一块数据 徖Delphi 什么是简单的TCP客户端服务器,delphi,client-server,firemonkey,indy10,Delphi,Client Server,Firemonkey,Indy10,我被卡住了。我以前使用过服务器和客户机套接字(非indy),它们非常异步且易于使用。我正在尝试制作一个简单的客户机-服务器。一旦连接到客户机,服务器就会在计时器事件上发送小数据块。时间为77ms,数据块约为100字节。 我已经尽了最大的努力从这里那里的点点滴滴和人们的例子中找出了印第。(很明显,如果人们能够快速地让它工作起来,网络上就不会有那么多关于它的内容了)。下面我从我的客户机和服务器中提取了相关部分 我将ip设置为localhost,并尝试首先调试服务器端。我在调试器中启动客户机和服务器。
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