Error message: “Bitmap Image is not valid” on received from Socket

╄→尐↘猪︶ㄣ 提交于 2019-11-29 17:58:26

There are a LOT of problems with the code you showed - ranging from a fundamental lack of understanding of how TClientSocket and TServerSocket actually work in general, to a lack of understanding of how to send/receive/parse over TCP/IP. I see very few things in your code that are correct.

You are creating multiple connections on the client side, making each one identify its type (command vs desktop), but your server code is not querying that type or even caring what the type is. It assumes every client is a desktop client and asks for its screen. So you can simplify your code on both sides by simply eliminating that second connection. It is not really needed anyway. You would keep your connections to a minimum to reduce overhead.

I would strongly suggest a re-write of your code.

Try something more like this instead:

Common:

unit UntSocketCommon;

uses
  System.Classes,
  System.Win.ScktComp;

interface

procedure ReadRawFromSocket(Socket: TWinSocketStream; Buf: Pointer; BufLen: Integer);
function ReadLineFromSocket(Socket: TWinSocketStream): String;
function ReadIntegerFromSocket(Socket: TWinSocketStream): Integer;
procedure ReadStreamFromSocket(Socket: TWinSocketStream; Stream: TStream);

procedure WriteRawFromSocket(Socket: TWinSocketStream; Buf: Pointer; BufLen: Integer);
procedure WriteLineToSocket(Socket: TWinSocketStream; const Value: String);
procedure WriteIntegerToSocket(Socket: TWinSocketStream; Value: Integer);
procedure WriteStreamToSocket(Socket: TWinSocketStream; Stream: TStream);

implementation

procedure ReadRawFromSocket(Socket: TWinSocketStream; Buf: Pointer; BufLen: Integer);
var
  PBuf: PByte;
  nBytesRead: Integer;
begin
  PBuf := PByte(Buf);
  while BufLen > 0 do
  begin
    nBytesRead := Socket.Read(PBuf^, BufLen);
    if nBytesRead < 1 then raise Exception.Create('Unable to read from socket');
    Inc(PBuf, nBytesRead);
    Dec(BufLen, nBytesRead);
  end;
end;

function ReadLineFromSocket(Socket: TWinSocketStream): String;
var
  Ch: AnsiChar;
  Buf: array[0..255] of AnsiChar;
  BufLen: Integer;
  S: UTF8String;

  procedure AppendBuf;
  var
    OldLen: Integer;
  begin
    OldLen := Length(S);
    SetLength(S, OldLen + BufLen);
    Move(Buf[0], S[OldLen], BufLen);
  end;

begin
  Result := '';
  BufLen := 0;
  repeat
    ReadRawFromSocket(Socket, @Ch, SizeOf(Ch));
    if Ch = #10 then Break;
    if BufLen = Length(Buf) then
    begin
      AppendBuf;
      BufLen := 0;
    end;
    Buf[BufLen] := Ch;
    Inc(BufLen);
  until False;
  if BufLen > 0 then AppendBuf;
  BufLen := Length(S);
  if BufLen > 0 then
  begin
    if S[BufLen] = #13 then
      SetLength(S, BufLen-1);
  end;
  Result := String(S);
end;

function ReadIntegerFromSocket(Socket: TWinSocketStream): Integer;
begin
  ReadRawFromSocket(Socket, @Result, SizeOf(Result));
  Result := ntohl(Result);
end;

procedure ReadStreamFromSocket(Socket: TWinSocketStream; Stream: TStream);
var
  Size: Integer;
  Buf: array[0..1023] of Byte;
  nBytes: Integer;
begin
  Size := ReadIntegerFromSocket(Socket);
  while Size > 0 do
  begin
    nBytes := Size;
    if nBytes > Length(Buf) then nBytes := Length(Buf);
    ReadRawFromSocket(Socket, Buf[0], nBytes);
    Stream.WriteBuffer(Buf[0], nBytes);
    Dec(Size, nBytes);
  end;
end;

procedure WriteRawToSocket(Socket: TWinSocketStream; Buf: Pointer; BufLen: Integer);
var
  PBuf: PByte;
  nBytesWritten: Integer;
begin
  PBuf := PByte(Buf);
  while BufLen > 0 do
  begin
    nBytesWritten := Socket.Write(PBuf^, BufLen);
    if nBytesWritten < 1 then raise Exception.Create('Unable to write to socket');
    Inc(PBuf, nBytesWritten);
    Dec(BufLen, nBytesWritten);
  end;
end;

procedure WriteLineToSocket(Socket: TWinSocketStream; const Value: String);
var
  S: UTF8String;
begin
  S := UTF8String(Value + #13#10);
  WriteRawToSocket(Socket, PAnsiChar(S), Length(S));
end;

procedure WriteIntegerToSocket(Socket: TWinSocketStream; Value: Integer);
begin
  Value := htonl(Value);
  WriteRawToSocket(Socket, @Value, SizeOf(Value));
end;

procedure WriteStreamToSocket(Socket: TWinSocketStream; Stream: TStream);
var
  Size: Integer;
  Buf: array[0..1023] of Byte;
  nBytes: Integer;
begin
  Size := Stream.Size - Stream.Position;
  WriteIntegerToSocket(Socket, Size);
  while Size > 0 do
  begin
    nBytes := Size;
    if nBytes > Length(Buf) then nBytes := Length(Buf);
    Stream.ReadBuffer(Buf[0], nBytes);
    WriteRawToSocket(Socket, Buf[0], nBytes);
    Dec(Size, nBytes);
  end;
end;

end.

Server:

unit UntThreadDesktop;

interface

uses
  System.Classes,
  System.Win.ScktComp,
  UntDesktopForm;

type
  TThreadController = class(TServerClientThread)
  private
    FDesktopForm: TDesktopForm;
  protected
    procedure ClientExecute; override;
  end;

implementation

uses
  System.SysUtils,
  WinApi.Windows,
  Vcl.Graphics,
  UntLibraries,
  UntSocketCommon;

{ TThreadDesktop }

procedure TThreadController.ClientExecute;
var
  fileSize: Integer;
  ms: TMemoryStream;
  buf: array[0..1023] of Byte;
  nBytes: Integer;
  SocketStrm: TWinSocketStream;
begin
  SocketStrm := TWinSocketStream.Create(ClientSocket, 5000);
  try
    // Init DesktopForm
    Synchronize(
      procedure
      begin
        FDesktopForm := TDesktopForm.Create;
        FDesktopForm.Show;
      end
    );

    try
      ms := TMemoryStream.Create;
      try
        while ClientSocket.Connected and (not Terminated) and (FDesktopForm <> nil) do
        begin
          ms.Clear;

          WriteLineToSocket(SocketStrm, '<|GetScreen|>');

          {
          ReadStreamFromSocket(SocketStrm, ms);
          ms.Position := 0;
          ms.SaveToFile('C:\Temp\Screen.bmp');
          ms.Position := 0;
          Synchronize(
            procedure
            begin
              if FDesktopForm <> nil then
                FDesktopForm.imgScreen.Picture.Bitmap.LoadFromStream(ms);
            end
          );
          }

          fileSize := ReadIntegerFromSocket(SocketStrm);

          while (ms.Size < fileSize) and ClientSocket.Connected and (not Terminated) and (FDesktopForm <> nil) do
          begin
            Synchronize(
            procedure
              begin
                if FDesktopForm <> nil then
                  FDesktopForm.panInfo.Caption := 'Total: ' + IntToStr(ms.Size) + ' de ' + IntToStr(fileSize);
              end
            );

            nBytes := fileSize - ms.Size;
            if nBytes > Length(Buf) then nBytes := Length(Buf);

            ReadRawFromSocket(SocketStrm, buf[0], nBytes);
            ms.WriteBuffer(buf[0], nBytes);

            if ms.Size = fileSize then
            begin
              ms.Position := 0;
              ms.SaveToFile('C:\Temp\Screen.bmp');
              ms.Position := 0;
              Synchronize(
                procedure
                begin
                  if FDesktopForm <> nil then
                    FDesktopForm.imgScreen.Picture.Bitmap.LoadFromStream(ms);
                end
              );
            end;
          end;
        end;
      finally
        ms.Free;
      end;
    finally
      Synchronize(
        procedure
        begin
          if FDesktopForm <> nil then
            FDesktopForm.Close;
        end
      );
    end;
  finally
    SocketStrm.Free;
  end;
end;

end.

procedure TMainForm.ServerSocketGetThread(Sender: TObject;
  ClientSocket: TServerClientWinSocket; var SocketThread: TServerClientThread);
begin
  SocketThread := TThreadController.Create(false, ClientSocket);
end;

Client:

unit UntThreadMain;

interface

uses
  System.Classes,
  System.Win.ScktComp;

type
  TThreadMain = class(TThread)
  private
    FClientSocket: TClientSocket;
    FSocketStrm: TWinSocketStream;
    procedure SendInfo;
    procedure SendScreen;
    procedure OnConnect(Sender: TObject; Socket: TCustomWinSocket);
    procedure OnDisconnect(Sender: TObject; Socket: TCustomWinSocket);
    procedure OnError(Sender: TObject; Socket: TCustomWinSocket; ErrorEvent: TErrorEvent; var ErrorCode: Integer);
  protected
    procedure Execute; override;
  public
    constructor Create(AHostname: string; APort: integer); reintroduce;
    destructor Destroy; override;
  end;

implementation

uses
  System.SysUtils,
  WinApi.Windows,
  Vcl.Graphics,
  UntClientMainForm,
  UntSocketCommon;

{ TThreadMain }

constructor TThreadMain.Create(AHostname: string; APort: integer);
begin
  inherited Create(false);
  FreeOnTerminate := false;

  FClientSocket := TClientSocket.Create(nil);
  FClientSocket.ClientType := ctBlocking;
  FClientSocket.Host := AHostname;
  FClientSocket.Port := APort;
  FClientSocket.OnConnect := OnConnect;
  FClientSocket.OnDisconnect := OnDisconnect;
  FClientSocket.OnError := OnError;
end;

destructor TThreadMain.Destroy;
begin
  FClientSocket.Free;
  inherited;
end;

procedure TThreadMain.Execute;
var
  SocketStrm: TWinSocketStream;
  cmd: String;
begin
  FClientSocket.Open;
  try 
    FSocketStrm := TWinSocketStream.Create(FClientSocket.Socket, 5000);
    try
      while FClientSocket.Socket.Connected and (not Terminated) do
      begin
        if SocketStrm.WaitForData(1000) then
        begin
          cmd := ReadLineFromSocket(SocketStrm);
          if cmd = '<|INFO|>' then
          begin
            SendInfo
          end
          else if cmd = '<|GetScreen|>' then
          begin
            SendScreen;
          end
        end;
      end;
    finally
      FSocketStrm.Free;
    end;
  finally
    FClientSocket.Close;
  end;
end;

procedure TThreadMain.OnConnect(Sender: TObject; Socket: TCustomWinSocket);
begin
  Synchronize(
    procedure
    begin
      ClientMainForm.stBar.Panels[1].Text := 'Conectado';
      ClientMainForm.btnConectar.Caption := 'Desconectar';
    end
  );
end;

procedure TThreadMain.OnDisconnect(Sender: TObject; Socket: TCustomWinSocket);
begin
  Synchronize(
    procedure
    begin
      ClientMainForm.stBar.Panels[1].Text := 'Desconectado';
      ClientMainForm.btnConectar.Caption := 'Conectar';
    end
  );
end;

procedure TThreadMain.OnError(Sender: TObject; Socket: TCustomWinSocket;
  ErrorEvent: TErrorEvent; var ErrorCode: Integer);
begin
  ErrorCode := 0;
  Socket.Close;
end;

procedure TThreadMain.SendInfo;
var
  cmd: string;
begin
  cmd := '<|INFO|>;NomePC=Tiago-PC;SO=Windows Seven Professiona 64-bit;CPU=Intel Core i7 3ª Geração';
  WriteLineToSocket(FSocketStrm, cmd);
end;

procedure TThreadMain.SendScreen;
var
  DC: HDC;
  bmp: TBitmap;
  ms: TMemoryStream;
begin
  ms := TMemoryStream.Create;
  try
    bmp := TBitmap.Create;
    try
      DC := GetDC(0);
      try
        //bmp.PixelFormat := pf8bit;
        bmp.Width := GetDeviceCaps(DC, HORZRES);
        bmp.Height := GetDeviceCaps(DC, VERTRES);
        //bmp.Width := Screen.Width;
        //bmp.Height := Screen.Height;
        BitBlt(bmp.Canvas.Handle, 0, 0, bmp.Width, bmp.Height, DC, 0, 0, SRCCOPY);
      finally
        ReleaseDC(0, DC);
      end;
      bmp.SaveToStream(ms);
    finally
      bmp.Free;
    end;
    ms.Position := 0;
    WriteStreamToSocket(FSocketStrm, ms);
  finally
    ms.Free;
  end;
end;

end.

procedure TClientMainForm.btnConectarClick(Sender: TObject);
begin
  if FThreadMain = nil then
  begin
    FThreadMain := TThreadMain.Create('localhost', 6550);
  end else
  begin
    FThreadMain.Terminate;
    FThreadMain.WaitFor;
    FThreadMain.Free;
    FThreadMain := nil;
  end;
end;
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!