[英]How make a TServerSocket multithread?
I'm testing how get periodical screenshots of my Android smartphone and already that this server is receiving a big flux of screenshots form my device, happens that these images not appear in TImage
, i think that is because TServerSocket
(part where receives images) not is in a thread. 我正在测试如何获取我的Android智能手机的定期屏幕快照,并且该服务器已经从我的设备接收了大量的屏幕快照,碰巧这些图像未出现在
TImage
,我认为这是因为TServerSocket
(接收图像的部分)没有在线程中。 And yes, i'm sending these screenshots correctly: 是的,我正确发送了这些屏幕截图:
Java (Android): Java(Android):
bitmap = Bitmap.createBitmap(mWidth + rowPadding / pixelStride, mHeight, Bitmap.Config.ARGB_8888);
bitmap.copyPixelsFromBuffer(buffer);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, bos);
byte[] array = bos.toByteArray();
DataOutputStream dos = new DataOutputStream(clientSocket.getOutputStream());
dos.writeInt(array.length);
dos.write(array, 0, array.length);
dos.flush();
And here is my Delphi code where must receive the periodical screenshots: 这是我的Delphi代码,必须在其中接收定期屏幕截图:
var
Form1: TForm1;
stSize: integer;
Stream: TMemoryStream;
Receiving: boolean;
png: TPngImage;
FSize: Integer;
writing: Boolean;
implementation
{$R *.dfm}
procedure TForm1.ServerSocket1ClientConnect(Sender: TObject; Socket: TCustomWinSocket);
var
Item: TListItem;
begin
Item := ListView1.Items.Add;
Item.Caption := IntTostr(socket.Handle);
Item.SubItems.Add(Socket.RemoteAddress);
Item.SubItems.Add(socket.RemoteHost);
Item.Data := Socket.Data;
end;
procedure TForm1.ServerSocket1ClientDisconnect(Sender: TObject; Socket: TCustomWinSocket);
var
Item: TListItem;
begin
Item:= ListView1.FindCaption(0, inttostr(socket.Handle), false, true, false);
if item <> nil then
Item.Delete;
end;
procedure TForm1.ServerSocket1ClientError(Sender: TObject; Socket: TCustomWinSocket; ErrorEvent: TErrorEvent; var ErrorCode: Integer);
begin
showmessage('socket erro');
ErrorCode := 0;
end;
procedure TForm1.Activate1Click(Sender: TObject);
begin
ServerSocket1.Active := true;
end;
procedure TForm1.Deactive1Click(Sender: TObject);
begin
ServerSocket1.Active := false;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Stream:= TMemoryStream.Create;
writing:= False;
end;
procedure TForm1.SendMyReqst1Click(Sender: TObject);
begin
if ListView1.Selected = nil then exit;
ServerSocket1.Socket.Connections[ListView1.ItemIndex].SendText('screencapture' + #13#10);
end;
procedure TForm1.ServerSocket1ClientRead(Sender: TObject; Socket: TCustomWinSocket);
var
BytesReceived: Longint;
CopyBuffer: Pointer;
ChunkSize: Integer;
TempSize: Integer;
const
MaxChunkSize: Longint = 8192;
begin
If FSize=0 then
begin
begin
Socket.ReceiveBuf(TempSize,SizeOf(TempSize));
TempSize := ntohl(TempSize);
Stream.SetSize(TempSize);
FSize:= TempSize
End;
End;
If (FSize>0) and not(writing) then
begin
GetMem(CopyBuffer, MaxChunkSize);
writing:= True;
While Socket.ReceiveLength>0 do
Begin
ChunkSize:= Socket.ReceiveLength;
If ChunkSize > MaxChunkSize then ChunkSize:= MaxChunkSize;
BytesReceived:= Socket.ReceiveBuf(CopyBuffer^,ChunkSize);
Stream.Write(CopyBuffer^, BytesReceived);
Dec(FSize,BytesReceived);
End;
end;
If FSize=0 then begin
Stream.Position := 0;
png:=TPngImage.Create;
png.LoadFromStream(Stream);
img1.Picture.Assign(png);
img1.Refresh;
Stream.SetSize(0);
png.Free;
FSize:= 0;
end;
FreeMem(CopyBuffer, MaxChunkSize);
Writing:= False;
end;
end.
This Delphi code above works fine, but to receive only 1 screenshot, not a big flux. 上面的此Delphi代码工作正常,但仅接收1个屏幕截图,而不是很大的流量。
UPDATE: 更新:
this is my code base for obtain periodical screenshots on Android. 这是我在Android上获取定期屏幕截图的代码库。
PS : See that he use a infinite loop. PS :看到他使用了无限循环。
The Delphi code you have shown DOES NOT correctly account for the streaming nature of TCP, or for multiple clients: 您显示的Delphi代码不能正确说明TCP或多个客户端的流性质:
it is NOT reading FSize
correctly. 它没有正确读取
FSize
。 More than 1 read may be needed to get all 4 bytes. 可能需要读取1次以上才能获得全部4个字节。
it DOES NOT use FSize
to limit the number of bytes read for the PNG stream. 它不使用
FSize
来限制为PNG流读取的字节数。 You need to read exactly how many bytes FSize
specifies, no more, no less. 您需要准确地读取
FSize
指定的字节FSize
,不要多也不少。 It is reading as long as the client is still sending bytes, even if they belong to subsequent messages. 只要客户端仍在发送字节(即使它们属于后续消息),它就会读取。 It needs to stop reading when it reaches the end of the stream, and then reset for the next message.
当到达流的末尾时,它需要停止读取,然后为下一条消息重置。
it DOES NOT handle the possibility of multiple clients sending screenshots at the same time. 它不能处理多个客户端同时发送屏幕截图的可能性。 It is sharing variables with multiple clients, thus allowing them to corrupt each other's messages.
它与多个客户端共享变量,从而允许它们破坏彼此的消息。
In short, the code is COMPLETELY broken, regardless of multi-threading. 简而言之,无论多线程如何,代码都是完全损坏的。 Which, BTW, is not a factor if you use the server in non-blocking mode (which the code likely is, as that is the server's default mode, and the code is not using any of the server's thread-related events).
如果在非阻塞模式下使用服务器,BTW并不是一个因素(代码可能是服务器的默认模式,而代码没有使用任何与服务器线程相关的事件)。
The code does not need to be multi-threaded to work correctly. 该代码不需要多线程即可正常工作。 It needs to be rewritten to operate correctly.
需要重写它才能正常运行。
Try something more like this: 尝试更多类似这样的方法:
type
TInt32Bytes = record
case Integer of
0: (Bytes: array[0..SizeOf(Int32)-1] of Byte);
1: (Value: Int32);
end;
TSocketState = (ReadingSize, ReadingStream);
TSocketData = class
public
Stream: TMemoryStream;
Png: TPngImage;
State: TSocketState;
Size: TInt32Bytes;
Offset: Integer;
constructor Create;
destructor Destroy; override;
end;
constructor TSocketData.Create;
begin
Stream := TMemoryStream.Create;
Png := TPngImage.Create;
end;
destructor TSocketData.Destroy;
begin
Stream.Free;
Png.Free;
end;
procedure TForm1.ServerSocket1ClientConnect(Sender: TObject; Socket: TCustomWinSocket);
var
Item: TListItem;
begin
Socket.Data := TSocketData.Create;
Item := ListView1.Items.Add;
Item.Data := Socket;
Item.Caption := IntToStr(Socket.Handle);
Item.SubItems.Add(Socket.RemoteAddress);
Item.SubItems.Add(Socket.RemoteHost);
end;
procedure TForm1.ServerSocket1ClientDisconnect(Sender: TObject; Socket: TCustomWinSocket);
var
Item: TListItem;
begin
Item := ListView1.FindData(0, Socket, true, false);
if Item <> nil then Item.Delete;
TSocketData(Socket.Data).Free;
end;
procedure TForm1.ServerSocket1ClientError(Sender: TObject; Socket: TCustomWinSocket; ErrorEvent: TErrorEvent; var ErrorCode: Integer);
begin
ErrorCode := 0;
Socket.Close;
end;
procedure TForm1.Activate1Click(Sender: TObject);
begin
ServerSocket1.Active := true;
end;
procedure TForm1.Deactive1Click(Sender: TObject);
begin
ServerSocket1.Active := false;
end;
procedure TForm1.SendMyReqst1Click(Sender: TObject);
var
Index: Integer;
begin
Index := ListView1.ItemIndex;
if Index = -1 then Exit;
ServerSocket1.Socket.Connections[Index].SendText('screencapture' + #13#10);
end;
procedure TForm1.ServerSocket1ClientRead(Sender: TObject; Socket: TCustomWinSocket);
var
BytesReceived: Integer;
BufferPtr: PByte;
SD: TSocketData;
Item: TListItem;
begin
SD := TSocketData(Socket.Data);
if SD.State = ReadingSize then
begin
while SD.Offset < SizeOf(Int32) do
begin
BytesReceived := Socket.ReceiveBuf(SD.Size.Bytes[SD.Offset], SizeOf(Int32) - SD.Offset);
if BytesReceived <= 0 then Exit;
Inc(SD.Offset, BytesReceived);
end;
SD.Size.Value := ntohl(SD.Size.Value);
SD.State := ReadingStream;
SD.Offset := 0;
SD.Stream.Size := SD.Size.Value;
end;
if SD.State = ReadingStream then
begin
if SD.Offset < SD.Size.Value then
begin
BufferPtr := PByte(SD.Stream.Memory);
Inc(BufferPtr, SD.Offset);
repeat
BytesReceive := Socket.ReceiveBuf(BufferPtr^, SD.Size.Value - SD.Offset);
if BytesReceived <= 0 then Exit;
Inc(BufferPtr, BytesReceived);
Inc(SD.Offset, BytesReceived);
until SD.Offset = SD.Size.Value;
end;
try
SD.Stream.Position := 0;
SD.Png.LoadFromStream(SD.Stream);
except
SD.Png.Assign(nil);
end;
Item := ListView1.Selected;
if (Item <> nil) and (Item.Data = Socket) then
img1.Picture.Assign(SD.Png);
SD.State := ReadingSize;
SD.Offset := 0;
end;
end;
procedure TForm1.ListView1SelectItem(Sender: TObject; Item: TListItem; Selected: Boolean);
begin
if (Item <> nil) and Selected then
img1.Picture.Assign(TSocketData(TCustomWinSocket(Item.Data).Data).Png);
end;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.