Indy TCP Socket 可以在两个线程之间共享吗?

Can a Indy TCP Socket be shared between two threads?

我想要一个连接,两个终端都在等待读取命令,并且不时地向另一个发送一些数据。当然,我知道如何实现我的协议,但是如果我执行 Socket.ReadUInt32 它会阻塞我的执行路径并且我无法在需要时发送命令。所以我想,如果我使用同一个套接字从另一个线程发送命令怎么办?我写了一个最小的例子......它似乎正在工作。但是我不确定这是否是正确的方法,或者这次只是运气......这种技术是线程安全的吗?额外的线程将使用套接字发送一个 UInt32 值,唤醒另一端,然后,连接将在主线程中进行。

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.

是的,在一个线程中发送一个连接而在另一个线程中从同一连接读取是安全的。套接字对入站和出站数据有单独的内部缓冲区。

请确保不要尝试同时从两个线程发送,或同时从两个线程读取,而不同步对连接的访问​​。