简体   繁体   English

如何使TServerSocket多线程?

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM