[英]How make a TServerSocket multithread?
我正在測試如何獲取我的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個屏幕截圖,而不是很大的流量。
更新:
這是我在Android上獲取定期屏幕截圖的代碼庫。
PS :看到他使用了無限循環。
您顯示的Delphi代碼不能正確說明TCP或多個客戶端的流性質:
它沒有正確讀取FSize
。 可能需要讀取1次以上才能獲得全部4個字節。
它不使用FSize
來限制為PNG流讀取的字節數。 您需要准確地讀取FSize
指定的字節FSize
,不要多也不少。 只要客戶端仍在發送字節(即使它們屬於后續消息),它就會讀取。 當到達流的末尾時,它需要停止讀取,然后為下一條消息重置。
它不能處理多個客戶端同時發送屏幕截圖的可能性。 它與多個客戶端共享變量,從而允許它們破壞彼此的消息。
簡而言之,無論多線程如何,代碼都是完全損壞的。 如果在非阻塞模式下使用服務器,BTW並不是一個因素(代碼可能是服務器的默認模式,而代碼沒有使用任何與服務器線程相關的事件)。
該代碼不需要多線程即可正常工作。 需要重寫它才能正常運行。
嘗試更多類似這樣的方法:
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.