Multithreading 从OnRead事件传递到分离的线程

Multithreading 从OnRead事件传递到分离的线程,multithreading,sockets,delphi,client-server,delphi-10.3-rio,Multithreading,Sockets,Delphi,Client Server,Delphi 10.3 Rio,我在一个项目中工作,希望接收实时网络摄像头的连续帧,我发现在我的测试中效果很好。现在想知道如何在TThread(套接字非阻塞)内进行类似于服务器多客户端/多线程的接收?我尝试了这个,但是服务器没有从客户端收到任何帧。我希望你能帮助我 服务器: uses System.Win.ScktComp, Winapi.WinSock, Vcl.Imaging.jpeg, System.Math; type TMyThread = class(TThread) private Sock

我在一个项目中工作,希望接收实时网络摄像头的连续帧,我发现在我的测试中效果很好。现在想知道如何在
TThread
(套接字非阻塞)内进行类似于服务器多客户端/多线程的接收?我尝试了这个,但是服务器没有从客户端收到任何帧。我希望你能帮助我

服务器

uses
  System.Win.ScktComp, Winapi.WinSock, Vcl.Imaging.jpeg, System.Math;

type
  TMyThread = class(TThread)
  private
    Socket: TCustomWinSocket;
  protected
    procedure Execute; override;
  public
    constructor Create(aSocket: TCustomWinSocket);
  end;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Image1: TImage;
    ServerSocket1: TServerSocket;
    procedure ServerSocket1Accept(Sender: TObject; Socket: TCustomWinSocket);
    procedure ServerSocket1ClientConnect(Sender: TObject; Socket: TCustomWinSocket);
    procedure ServerSocket1ClientDisconnect(Sender: TObject; Socket: TCustomWinSocket);
    procedure ServerSocket1ClientError(Sender: TObject; Socket: TCustomWinSocket; ErrorEvent: TErrorEvent; var ErrorCode: Integer);
    procedure Button1Click(Sender: TObject);
    procedure ServerSocket1Listen(Sender: TObject; Socket: TCustomWinSocket);
  private
    { Private declarations }
    MyThread: TMyThread;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

constructor TMyThread.Create(aSocket: TCustomWinSocket);
begin
  inherited Create(True);
  Socket := aSocket;
  FreeOnTerminate := True;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  ServerSocket1.Port := 1234;
  ServerSocket1.Active := true;
end;

procedure TForm1.ServerSocket1Accept(Sender: TObject; Socket: TCustomWinSocket);
begin
  MyThread := TMyThread.Create(Socket);
  MyThread.Start;
end;

procedure TForm1.ServerSocket1ClientConnect(Sender: TObject; Socket: TCustomWinSocket);
begin
  Socket.Data := nil;
end;

procedure TForm1.ServerSocket1ClientDisconnect(Sender: TObject; Socket: TCustomWinSocket);
begin
  if Socket.Data <> nil then
    TMemoryStream(Socket.Data).Free;
end;

procedure TForm1.ServerSocket1ClientError(Sender: TObject; Socket: TCustomWinSocket; ErrorEvent: TErrorEvent; var ErrorCode: Integer);
begin
  ErrorCode := 0;
end;

procedure TForm1.ServerSocket1Listen(Sender: TObject; Socket: TCustomWinSocket);
begin
  ShowMessage('Server listen on port: ' + IntToStr(Socket.LocalPort));
end;

procedure TMyThread.Execute;
var
  Stream: TMemoryStream;
  BytesReceived: Integer;
  StreamSize, TempSize: Int32;
  BytesRemaining: Int64;
  P: PByte;
  ChunkSize: Integer;
  jpg: TJpegImage;
const
  MaxChunkSize: Int64 = 8192;
begin
  while Socket.Connected do
  begin
    Stream := TMemoryStream(Socket.Data);

    if Stream = nil then
    begin
      if Socket.ReceiveLength < SizeOf(TempSize) then
        Exit;
      BytesReceived := Socket.ReceiveBuf(TempSize, SizeOf(TempSize));
      if BytesReceived <= 0 then
        Exit;
      StreamSize := ntohl(TempSize);
      Stream := TMemoryStream.Create;
      Socket.Data := Stream;
      Stream.Size := StreamSize;
      BytesRemaining := StreamSize;
    end
    else
      BytesRemaining := Stream.Size - Stream.Position;

    if BytesRemaining > 0 then
    begin
      P := PByte(Stream.Memory);
      if Stream.Position > 0 then
        Inc(P, Stream.Position);
      repeat
        ChunkSize := Integer(Min(BytesRemaining, MaxChunkSize));
        BytesReceived := Socket.ReceiveBuf(P^, ChunkSize);
        if BytesReceived <= 0 then
          Exit;
        Inc(P, BytesReceived);
        Dec(BytesRemaining, BytesReceived);
        Stream.Seek(BytesReceived, soCurrent);
      until BytesRemaining = 0;
    end;

    try
      jpg := TJpegImage.Create;
      try
        Stream.Position := 0;
        jpg.LoadFromStream(Stream);
        Synchronize(
          procedure
          begin
            Form1.Image1.Picture.Assign(jpg);
          end);
      finally
        jpg.Free;
      end;
    finally
      Socket.Data := nil;
      Stream.Free;
    end;
  end;
end;

end.
使用
System.Win.ScktComp、Winapi.WinSock、Vcl.Imaging.jpeg、System.Math;
类型
TMyThread=class(TThread)
私有的
Socket:TCustomWinSocket;
受保护的
程序执行;推翻
公众的
构造函数创建(aSocket:TCustomWinSocket);
结束;
类型
TForm1=类(TForm)
按钮1:t按钮;
图1:TImage;
ServerSocket1:TServerSocket;
过程ServerSocket1Accept(发送方:TObject;套接字:TCustomWinSocket);
过程ServerSocket1ClientConnect(发送方:TObject;套接字:TCustomWinSocket);
过程ServerSocket1ClientDisconnect(发送方:TObject;套接字:TCustomWinSocket);
过程ServerSocket1ClientError(发送方:ToObject;套接字:TCustomWinSocket;ErrorEvent:TerroEvent;var错误代码:Integer);
程序按钮1点击(发送方:ToObject);
过程ServerSocket1Listen(发送方:TObject;套接字:TCustomWinSocket);
私有的
{私有声明}
神话阅读:TMyThread;
公众的
{公开声明}
结束;
变量
表1:TForm1;
实施
{$R*.dfm}
构造函数TMyThread.Create(aSocket:TCustomWinSocket);
开始
继承创建(True);
插座:=A插座;
FreeOnTerminate:=真;
结束;
程序TForm1.按钮1单击(发送方:TObject);
开始
ServerSocket1.端口:=1234;
ServerSocket1.Active:=true;
结束;
过程TForm1.ServerSocket1Accept(发送方:TObject;套接字:TCustomWinSocket);
开始
MyThread:=TMyThread.Create(套接字);
阅读。开始;
结束;
过程TForm1.ServerSocket1ClientConnect(发送方:TObject;套接字:TCustomWinSocket);
开始
Socket.Data:=nil;
结束;
过程t用于m1.ServerSocket1ClientDisconnect(发送方:TObject;套接字:TCustomWinSocket);
开始
如果Socket.Data为零,则
TMemoryStream(Socket.Data).Free;
结束;
过程TForm1.ServerSocket1ClientError(发送方:ToObject;套接字:TCustomWinSocket;ErrorEvent:TerroEvent;变量ErrorCode:Integer);
开始
错误代码:=0;
结束;
过程TForm1.ServerSocket1Listen(发送方:TObject;套接字:TCustomWinSocket);
开始
ShowMessage('Server listen on port:'+IntToStr(Socket.LocalPort));
结束;
程序TMyThread.Execute;
变量
流:TMemoryStream;
接收字节数:整数;
StreamSize、TempSize:Int32;
剩余字节数:Int64;
P:PByte;
ChunkSize:整数;
jpg:tjpeg图像;
常数
MaxChunkSize:Int64=8192;
开始
而插座。连接了吗
开始
流:=TMemoryStream(Socket.Data);
如果Stream=nil,则
开始
如果Socket.ReceiveLength0,则
公司(P,流位置);
重复
ChunkSize:=整数(最小(剩余字节数,最大ChunkSize));
BytesReceived:=Socket.ReceiveBuf(P^,ChunkSize);

如果BytesReceived,则需要在线程阻塞模式下使用
TServerSocket
,以便有效地将工作线程与其接受的客户端一起使用。非阻塞模式和工作线程不能很好地混合在一起

非阻塞模式的发明是为了能够在主UI线程中使用
TClientSocket
TServerSocket
,而不阻塞它。但当在主UI线程之外使用套接字时,非阻塞模式几乎没有什么用处(只是一些不适用于您的情况的特殊情况)。在内部,
TCustomWinSocket
分配一个
HWND
来检测在非阻塞中使用的套接字活动,并且
HWND
需要一个消息循环。但是,由于每个接受的客户端套接字都是在工作线程之外创建的,因此线程中运行的任何消息循环都无法为它们的
HWND
s提供服务。因此,无论怎样,您都需要使用线程阻塞模式

另外,使用线程阻塞模式将大大简化套接字I/O代码

尝试类似以下内容:

单元1;
接口
使用
…,System.Win.ScktComp;
类型
TForm1=类(TForm)
按钮1:t按钮;
图1:TImage;
ServerSocket1:TServerSocket;
程序按钮1点击(发送方:ToObject);
过程ServerSocket1ClientError(发送方:ToObject;套接字:TCustomWinSocket;ErrorEvent:TerroEvent;var错误代码:Integer);
过程ServerSocket1GetThread(发送方:TObject;ClientSocket:TServerClientWinSocket;var SocketThread:TServerClientThread);
过程ServerSocket1Listen(发送方:TObject;套接字:TCustomWinSocket);
私有的
{私有声明}
公众的
{公开声明}
结束;
变量
表1:TForm1;
实施
使用
Winapi.WinSock,Vcl.Imaging.jpeg,System.Math;
{$R*.dfm}
类型
TMyThread=class(TServerClientThread)
受保护的
程序客户执行;推翻
结束;
程序TForm1.按钮1单击(发送方:TObject);
开始
//这可以在设计时设置,如果需要。。。
ServerSocket1.ServerType:=TServerType.stThreadBlocking;
//所以这可以。。。
ServerSocket1.端口:=1234;
ServerSocket1.Active:=True;
结束;
过程TForm1.ServerSocket1ClientError(发送方:ToObject;套接字:TCustomWinSocket;ErrorEvent:TerroEvent;变量ErrorCode:Integer);
开始
错误代码:=0;
结束;
过程TForm1.ServerSocket1GetThread(发送方:TObject;ClientSocket:TServerClientWinSocket;var SocketThread:TServerClientThread);
开始
SocketThread:=TMyThread.Create(False,ClientSocket);
结束;
过程TForm1.ServerSocket1Listen(发送方:TObject;套接字:TCustomWinSocket);
开始
ShowMessage('服务器侦听端口:'+Int
unit Unit1;

interface

uses
  ..., IdContext, IdTCPServer;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Image1: TImage;
    IdTCPServer1: TIdTCPServer;
    procedure Button1Click(Sender: TObject);
    procedure IdTCPServer1Connect(AContext: TIdContext);
    procedure IdTCPServer1Execute(AContext: TIdContext);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

uses
  Vcl.Imaging.jpeg, System.Math;

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  IdTCPServer1.DefaultPort := 1234;
  IdTCPServer1.Active := True;
  ShowMessage('Server listen on port: ' + IntToStr(IdTCPServer1.DefaultPort));
end;

procedure TForm1.IdTCPServer1Connect(AContext: TIdContext);
begin
  // tell ReadStream() to read the stream size as an Int32 and not as an Int64...
  AContext.Connection.IOHandler.LargeStream := False;
end;

procedure TForm1.IdTCPServer1Execute(AContext: TIdContext);
var
  Stream: TMemoryStream;
  jpg: TJpegImage;
begin
  // OnExecute is a looped event, it is called in a continuous
  // loop for the lifetime of the TCP connection...

  jpg := TJpegImage.Create;
  try
    Stream := TMemoryStream.Create;
    try
      // ReadStream() can read the stream size first, then read the stream data...
      AContext.Connection.IOHandler.ReadStream(Stream, -1, False);

      Stream.Position := 0;
      jpg.LoadFromStream(Stream);
    finally
      Stream.Free;
    end;
    TThread.Synchronize(nil,
      procedure
      begin
        Form1.Image1.Picture.Assign(jpg);
      end
    );
  finally
    jpg.Free;
  end;
end;

end.