我正在测试如何获取我的Android智能手机的定期屏幕截图以及此服务器从我的设备接收到大量屏幕截图,发生这些图片没有出现在TImage
中,我认为是因为TServerSocket
(接收图像的部分)不在线程中。是的,我正确发送这些屏幕截图:
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();
这是我的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.
上面的Delphi代码工作正常,但只能获得1个屏幕截图,而不是很大的变化。
更新
this是我在Android上获取定期截图的代码库。
PS :看到他使用无限循环。
答案 0 :(得分:2)
您显示的Delphi代码无法正确考虑TCP或多个客户端的流媒体特性:
它没有正确阅读FSize
。获取所有4个字节可能需要多次读取。
它不会使用FSize
来限制为PNG流读取的字节数。您需要准确读取FSize
指定的字节数,不多也不少。只要客户端仍在发送字节,即使它们属于后续消息,它也在读取。它需要在到达流的末尾时停止读取,然后重置以显示下一条消息。
它不会处理多个客户端同时发送屏幕截图的可能性。它与多个客户共享变量,从而允许它们破坏彼此的消息。
简而言之,无论多线程如何,代码都完全被破坏了。如果您在非阻塞模式下使用服务器,那么BTW不是一个因素(代码可能是,因为这是服务器的默认模式,并且代码没有使用任何服务器&#39;与线程相关的事件)。
代码不需要多线程才能正常工作。需要重写它才能正常运行。
尝试更像这样的事情:
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;