简体   繁体   English

如何在已经运行的线程中运行代码以安全地发送/接收数据 [TidTCPServer]

[英]How to run code in already running thread to safely send/recv data [TidTCPServer]

UPDATE Problem Still Exists.更新问题仍然存在。

Is it possible to run code in already running thread?是否可以在已经运行的线程中运行代码? for example: thread1 is running some code & i want to run code from thread2 in thread1.例如:线程 1 正在运行一些代码 & 我想从线程 1 中的线程 2 运行代码。

I want to run code in idTCPServer thread to send some data to client我想在 idTCPServer 线程中运行代码以向客户端发送一些数据

Edit: After research seems that my problem is that when Client data is received or is receiving same time another thread is trying to write to that socket.编辑:经过研究,我的问题似乎是当接收到或正在接收客户端数据时,另一个线程正在尝试写入该套接字。

Edit:编辑:

procedure TMainFrm.UserSRVExecute(AContext: TIdContext);
var
 Command        : String;
 msSize         : Int64;
 ms             : TMemoryStream;
 decompressedMS : TMemoryStream;
 H              : TIdNotify;
begin
// Application.ProcessMessages;
 Command := AContext.Connection.Socket.ReadLn;
// messagebox(0,'snd','',$40);
 if logb then mainfrm.mconnections.Lines.Add(command + ' - BEGIN');

 if Command <> '' then   // keepalive 
  begin
  //Application.ProcessMessages;
   msSize         := AContext.Connection.Socket.ReadInt64;
   ms             := TMemoryStream.Create;
   decompressedMS := TMemoryStream.Create;
   try
    AContext.Connection.Socket.ReadStream(ms, msSize);
    ms.Position := 0;
    DecompressStream(MS,decompressedMS);
    decompressedMS.Position := 0;
    Client_ProcessData(AContext,Command,decompressedMS);
   finally
    ms.Free;
    decompressedMS.Free;
    if logb then mainfrm.mconnections.Lines.Add(command + ' - END');
   end;
  end;
end;


procedure Client_ProcessData(AContext: TIdContext; cmd : String; data : TMemoryStream);
var
 Hnd     : THandle;
 clData  : TStringArray;
 TmpStr1 : String;
 Tmp     : String;
 TN      : TIdNotify;

 Sync    : TMySync;
 I,I2    : Integer;
begin
 Hnd := AContext.Connection.Socket.Binding.Handle;

 if cmd = 'scr' then  // RECEIVE COMMAND TO SEND TO CLIENT TO RECEIVE DATA FROM CLIENT
  begin
   Tmp := StreamToString(data);
   {Sync := TMySync2.Create(True);
   try
    Sync.cmd := cmd;
    Sync.hnd := Hnd;
    Sync.tmp := TmpStr1;
    Sync.Resume;
   finally
    //Sync.Free;
   end;  }
   log('>>> CLFROMAS: '+IntToStr(HND)+':::'+cmd+':::');
 //  SendCMDToSocket(MainFrm.UserSRV,StrToInt(Trim(Tmp)),'scr'+IntToStr(Hnd));
   I2 := StrToInt(Trim(Tmp));
  for I := 0 to 100 do
  if USRVData[i].hnd = I2 then
   begin
   // cs.Acquire;
    USRVData[i].CTX.Connection.Socket.WriteLn('scr'+IntToStr(Hnd));  // PLACED ALL CONTEXTs IN GLOBAL VARIABLE + ALL SOCKET HANDLES. <--- HERE IS THE PROBLEM
   // cs.Release;
    Break;
   end;
 //  log('>>> CLFROMAS: '+IntToStr(HND)+':::'+cmd+':::'+streamtostring(data));
   Exit;
  end;

   if Copy(cmd,1,Length('scr4u')) = 'scr4u' then // RECEIVE DATA FROM CLIENT TO SEND IT TO ADMIN CLIENT REQUEST ABOVE
   begin
    if Length(cmd) > Length('scr4u') then
     begin
      Delete(cmd,1,Length('scr4u'));
      Data.Position := 0;
     { Sync := TMySync.Create;
      try
       Sync.cmd := cmd;
       Sync.hnd := Hnd;
       Sync.data := TMemoryStream.Create;
       Sync.data.CopyFrom(data,data.Size);
       Sync.data.Position := 0;
       Sync.DoNotify;
      finally
       Sync.data.Free;
       Sync.Free;
      end;  }
      SendStreamToSocket(MainFrm.UserSRV,strtoint(cmd),'scr4u',Data);

      log('>>>>> ADMIN: '+IntToStr(HND)+':::'+cmd+':::'{+streamtostring(data)});
     end else TmpStr1 := '';
    Exit;
   end;

   ...

UPDATE更新

procedure TMainFrm.UserSRVExecute(AContext: TIdContext);
var
 Command        : String;
 msSize         : Int64;
 ms             : TMemoryStream;
 decompressedMS : TMemoryStream;
 H              : TIdNotify;
 I              : Integer;
 List, Messages : TStringList;
begin
  Messages := nil;
  try
   List := TMyContext(AContext).OutgoingMessages.Lock;
   try
    if List.Count > 0 then
     begin
      Messages := TStringList.Create;
      Messages.Assign(List);
      List.Clear;
     end;
   finally
    TMyContext(AContext).OutgoingMessages.Unlock;
   end;
   if Messages <> nil then
    begin
     for I := 0 to Messages.Count-1 do
      begin
       AContext.Connection.IOHandler.WriteLn(Messages.Strings[I]);
      end;
    end;
  finally
   Messages.Free;
  end;

  if AContext.Connection.IOHandler.InputBufferIsEmpty then
   begin
    AContext.Connection.IOHandler.CheckForDataOnSource(100);
    AContext.Connection.IOHandler.CheckForDisconnect;
    if AContext.Connection.IOHandler.InputBufferIsEmpty then
      Exit;
   end;

 Command := AContext.Connection.Socket.ReadLn;

 if logb then mainfrm.mconnections.Lines.Add(command + ' - BEGIN');

 if Command <> '' then
  begin
   msSize         := AContext.Connection.Socket.ReadInt64;
   ms             := TMemoryStream.Create;
   decompressedMS := TMemoryStream.Create;
   try
    AContext.Connection.Socket.ReadStream(ms, msSize);
    ms.Position := 0;
    DecompressStream(MS,decompressedMS);
    decompressedMS.Position := 0;
    Client_ProcessData(AContext,Command,decompressedMS);
   finally
    ms.Free;
    decompressedMS.Free;
    if logb then mainfrm.mconnections.Lines.Add(command + ' - END');
   end;
  end;
end;

Is it possible to run code in already running thread?是否可以在已经运行的线程中运行代码? for example: thread1 is running some code & i want to run code from thread2 in thread1.例如:线程 1 正在运行一些代码 & 我想从线程 1 中的线程 2 运行代码。

No. Thread1 needs to be explicitly coded to stop what it is currently doing, do something else, and then go back to what it was previous doing.不可以。需要对 Thread1 进行显式编码以停止它当前正在执行的操作,执行其他操作,然后返回到它之前执行的操作。 All Thread2 can do is signal Thread1 to perform that stop+continue at its earliest convenience. Thread2 所能做的就是通知 Thread1 尽早执行停止+继续。

I want to run code in idTCPServer thread to send some data to client我想在 idTCPServer 线程中运行代码以向客户端发送一些数据

Your TIdTCPServer.OnExecute event handler needs to check for that data periodically and send it when it is available.您的TIdTCPServer.OnExecute事件处理程序需要定期检查该数据并在可用时发送它。

You can use the TIdContext.Data property, or derive a custom class from TIdServerContext and assign it to the TIdTCPServer.ContextClass property, to provide a per-client thread-safe buffer for your outbound data.您可以使用TIdContext.Data属性,或从TIdServerContext派生自定义类并将其分配给TIdTCPServer.ContextClass属性,为您的出站数据提供每个客户端的线程安全缓冲区。 Your OnExecute handler can then access that buffer when needed.然后,您的OnExecute处理程序可以在需要时访问该缓冲区。

For example:例如:

type
  TMyContext = class(TIdServerContext)
  public
    constructor Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TIdContextThreadList = nil); override;
    destructor Destroy; override;
    OutgoingMessages: TIdThreadSafeStringList;
  end;

constructor TMyContext.Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TIdContextThreadList = nil);
begin
  inherited;
  OutgoingMessages := TIdThreadSafeStringList.Create;
end;

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

procedure TMyForm.FormCreate(Sender: TObject);
begin
  // this must be set before activating the server...
  IdTCPServer1.ContextClass := TMyContext;
end;

procedure TMyForm.IdTCPServer1Execute(AContext: TIdContext);
var
  List, Messages: TStringList;
begin
  // check for outgoing data...

  Messages := nil;
  try
    List := TMyContext(AContext).OutgoingMessages.LockList;
    try
      if List.Count > 0 then
      begin
        Messages := TStringList.Create;
        Messages.Assign(List);
        List.Clear;
      end;
    finally
      TMyContext(AContext).OutgoingMessages.UnlockList;
    end;
    if Messages <> nil then
    begin
      // send Messages using AContext.Connection.IOHandler as needed...
    end;
  finally
    Messages.Free;
  end;

  // check for incoming data...

  if AContext.Connection.IOHandler.InputBufferIsEmpty then
  begin
    AContext.Connection.IOHandler.CheckForDataOnSource(100);
    AContext.Connection.IOHandler.CheckForDisconnect;
    if AContext.Connection.IOHandler.InputBufferIsEmpty then
      Exit;
  end;

  // process incoming data as needed...
end;

procedure TForm1.SomeProcedure;
var
  List: TIdContextList;
  Context: TMyContext;
begin
  List := IdTCPServer1.Contexts.LockList;
  try
    Context := TMyContext(List[SomeIndex]);
    Context.OutgoingMessages.Add('something');
  finally
    IdTCPServer1.Contexts.UnlockList;
  end;
end;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM