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.