如何使用关键部分保护我的Indy套接字写入?

waz*_*123 -3 delphi multithreading mutex

cs.Acquire;
try
   AContext.Connection.Socket.Write(packet);
finally
   cs.Release;
end;
Run Code Online (Sandbox Code Playgroud)

要么

EnterCriticalSection(cs);
   AContext.Connection.Socket.Write(packet);
LeaveCriticalSection(cs);
Run Code Online (Sandbox Code Playgroud)

我试图在线程中将我的数据包发送到服务器,但我有20个线程,它也将数据发送到同一个连接套接字.我尝试使用Critical Section或Mutex,它们都不起作用,我收到所有线程发送时的垃圾.

这都是关于我之前的问题

数据包看起来像这样:

LengthData

0000000010HelloWorld

服务器接收数据:

ReadBytes(10);

len := (Then remove zeros from begining);

ReadBytes(len); // data.
Run Code Online (Sandbox Code Playgroud)

有时我会在ReadBytes(10)中收到垃圾,它是长度+数据的混合,例如:"10Hellowor"

如果我只使用一个线程将数据发送到服务器,那么每次都可以正常工作.如果发送了很多线程,那么一切都会出错.

Rem*_*eau 5

如果使用得当,CS /互斥锁可以正常工作.确保您的线程锁定相同的 CS/mutex实例,而不是单独的实例.由于您是从服务器端发送数据,我建议使用该OnConnect事件创建每个连接的CS并将其存储在TIdContext.Data属性中,并将OnDisconnect事件释放到该属性中,例如:

procedure TForm1.IdTCPServer1Connect(AContext: TIdContext);
begin
  AContext.Data := TCriticalSection.Create;
end;

procedure TForm1.IdTCPServer1Disconnect(AContext: TIdContext);
begin
  AContext.Data.Free;
  AContext.Data := nil;
end;
Run Code Online (Sandbox Code Playgroud)

然后你可以在需要时这样做:

TCriticalSection(AContext.Data).Acquire;
try 
   AContext.Connection.Socket.Write(packet); 
finally 
  TCriticalSection(AContext.Data).Release;
end; 
Run Code Online (Sandbox Code Playgroud)

稍微更封装的用法是从中派生出一个新类TIdServerContext,例如:

type
  TMyContext = class(TIdServerContext)
  private
    CS: TCriticalSection;
  public
    constructor Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TThreadList = nil); override;
    destructor Destroy; override;
    procedure SendPacket(const AData: TIdBytes); // or whatever parameters you need
  end;

constructor TMyContext.Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TThreadList = nil);
begin
  inherited;
  CS := TCriticalSection.Create;
end;

destructor TMyContext.Destroy;
begin
  CS.Free;
  inherited;
end;

procedure TMyContext.SendPacket(const AData: TIdBytes);
begin
  CS.Acquire;
  try
    Connection.IOHandler.Write(AData);
  finally
    CS.Release;
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  IdTCPServer1.ContextClass := TMyContext;
end;
Run Code Online (Sandbox Code Playgroud)

然后你可以在需要时这样做:

TMyContext(AContext).SendPacket(packet); 
Run Code Online (Sandbox Code Playgroud)