簡體   English   中英

如何使TServerSocket多線程?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM