I want a connection where both terminals wait reading for commands, and from time to time, one send some data to the other. Of course, I know how to implement my protocol, but if I do a Socket.ReadUInt32
it will block my execution path and I can't send commands when needed. So I thought, what if I send commands from another thread, using the same socket ? I write a minimal example... and it seems it is working. But I don't know for sure if this is correct way to do it, or it was jus luck this time... Is this technique threadsafe ? The extra thread will use the socket just for sending a UInt32 value, to wake the other side, and then, the connection will take place in the main threads.
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, IdTCPConnection,
IdTCPClient, IdBaseComponent, IdComponent, IdCustomTCPServer, IdTCPServer,
IdContext, IdThreadComponent;
const
WM_LOGEVENT = WM_USER + 1;
type
TForm1 = class(TForm)
Server: TIdTCPServer;
Client: TIdTCPClient;
BStartServer: TButton;
BConnectClient: TButton;
ClientWriteThread: TIdThreadComponent;
Memo: TMemo;
BSendCommand: TButton;
ClientReadThread: TIdThreadComponent;
procedure BStartServerClick(Sender: TObject);
procedure ServerExecute(AContext: TIdContext);
procedure BConnectClientClick(Sender: TObject);
procedure ClientWriteThreadRun(Sender: TIdThreadComponent);
procedure BSendCommandClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure ClientReadThreadRun(Sender: TIdThreadComponent);
private
procedure LogEvent(var Msg: TMessage); message WM_LOGEVENT;
procedure SendEvent(const Msg: String);
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.LogEvent(var Msg: TMessage);
var PStr: PString;
begin
PStr:= PString(Msg.WParam);
Memo.Lines.Add(Copy(PStr^, 1, Length(PStr^)));
end;
procedure TForm1.SendEvent(const Msg: String);
begin
SendMessage(Handle, WM_LOGEVENT, WPARAM(@Msg), 0);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Server.Bindings.Clear;
Server.Bindings.Add.SetBinding('192.168.0.3', 60200);
Client.Host:= '192.168.0.3';
Client.Port:= 60200;
end;
procedure TForm1.BStartServerClick(Sender: TObject);
begin
Server.Active:= True;
Caption:= IntToStr(Byte(Server.Active));
end;
procedure TForm1.BConnectClientClick(Sender: TObject);
begin
Client.Connect;
ClientReadThread.Start;
end;
procedure TForm1.BSendCommandClick(Sender: TObject);
begin
ClientWriteThread.Start;
end;
procedure TForm1.ClientWriteThreadRun(Sender: TIdThreadComponent);
var Data: Cardinal;
begin
Data:= 1234;
SendEvent('Client send: '+ IntToStr(Data));
Client.Socket.Write(Data);
Sender.Terminate;
end;
procedure TForm1.ClientReadThreadRun(Sender: TIdThreadComponent);
var Data: Cardinal;
begin
SendEvent('Client listening...');
Data:= Client.Socket.ReadUInt32;
SendEvent('Client received: '+ IntToStr(Data));
Client.Disconnect;
SendEvent('Client stopped.');
Sender.Terminate;
end;
procedure TForm1.ServerExecute(AContext: TIdContext);
var Data: Cardinal;
Srv: String;
begin
Srv:= 'Server '+IntToStr(Random(100));
SendEvent(Srv+' listening...');
Data:= AContext.Connection.Socket.ReadUInt32;
SendEvent(Srv+' received: '+IntToStr(Data));
Data:= 9999;
SendEvent(Srv+' Send: '+IntToStr(Data));
AContext.Connection.Socket.Write(Data);
AContext.Connection.Disconnect;
SendEvent(Srv+' stopped.');
end;
end.