2017-12-28 15 views
0

Ich teste, wie regelmäßig Screenshots von meinem Android-Smartphone und bereits, dass dieser Server einen großen Fluss von Screenshots von meinem Gerät erhält, passiert, dass diese Bilder nicht in TImage erscheinen, ich denke, das ist, weil TServerSocket (Teil wo Bilder empfängt) ist nicht in einem Thread. Und ja, ich schicke diese Screenshots richtig:Wie machen Sie einen TServerSocket Multithread?

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(); 

Und hier ist mein Delphi-Code, wo muß die periodischen Screenshots erhalten:

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. 

enter image description here

Dieser Delphi-Code oben funktioniert gut, aber nur 1 Screenshot, kein großer Fluss zu erhalten.

UPDATE:

this ist mein Code-Basis für periodische Screenshots auf Android erhalten.

PS: Siehe, dass er eine Endlosschleife verwendet.

+0

Mögliche Duplikat [Fehlermeldung: "Bitmap ist ungültig" auf von Socket empfangen] (https://stackoverflow.com/questions/20292986/error- message-bitmap-image-ist-nicht-gültig-bei-empfangen-von-socket) –

+0

@RemyLebeau, 'Dieser Code muss nicht Multi-Threading sein, um ordnungsgemäß zu funktionieren. Es muss repariert werden, selbst wenn das eine Endlosschleife von Screenshots ausführt und das Senden auf der Client-Seite funktioniert? –

+0

@RemyLebeau, siehe meine Ausgabe oben. –

Antwort

2

Der Delphi-Code, den Sie gezeigt haben, nicht berücksichtigt für die Streaming-Natur von TCP korrekt, oder für mehrere Clients:

  • es NICHT FSize richtig liest. Mehr als 1 Lesevorgang kann erforderlich sein, um alle 4 Bytes zu erhalten.

  • Es verwendet FSize NICHT, um die Anzahl der gelesenen Bytes für den PNG-Stream zu begrenzen. Sie müssen genau lesen, wie viele Bytes FSize angibt, nicht mehr und nicht weniger. Es wird gelesen, solange der Client noch Bytes sendet, auch wenn sie zu nachfolgenden Nachrichten gehören. Es muss aufhören zu lesen, wenn es das Ende des Streams erreicht, und dann für die nächste Nachricht zurückgesetzt.

  • Es verarbeitet nicht die Möglichkeit, mehrere Clients Screenshots gleichzeitig senden. Es teilt Variablen mit mehreren Clients, so dass sie gegenseitig ihre Nachrichten beschädigen können.

Kurz gesagt, ist der Code völlig gebrochen, und zwar unabhängig von Multi-Threading. Was, BTW, ist kein Faktor, wenn Sie den Server im nicht blockierenden Modus verwenden (was der Code wahrscheinlich ist, da dies der Standardmodus des Servers ist und der Code keine Thread-bezogenen Ereignisse des Servers verwendet).

Der Code muss nicht multi-threaded sein, um ordnungsgemäß zu funktionieren. Es muss neu geschrieben werden, um korrekt zu funktionieren.

etwas mehr wie die Sie interessieren:

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; 
+0

Ihr Code empfängt den Screenshot, aber nach einigen Sekunden tritt dieser Fehler auf: [Fehler] (https://image.printscr.com/image/2A8i50HFQdyfXfuFunHhqA.png) und [Line] (https: //image.prntscr. com/image/ooqMXa-rSI_dF2dVmPhkgg.png) –

+0

Dies kann wegen großen Datenfluss der Endlosschleife von Android-Code sein? –

+0

@JeffersonFarias meine schlechte, ich habe vergessen, "BufferPtr" zu erhöhen, während Sie den PNG-Stream lesen. Ich habe den Code in meiner Antwort optimiert. –