繁体   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