-1

I want to receive a screenshot on server inside a thread and tried to use code that was suggested in this answer of a experient member (Delphi programmer) that second he, this code only works in ThreadBlocking mode.

I already put TServerSocket (Delphi component) in this mode, but when the component is in this mode, any connection cannot be received on server from a client Android made in Java (that also use Socket to establish this connection).

Now I want to know (mainly of a Android programmer) if on client side (Android) Socket also must be in ThreadBlocking to establish this connection with success, or what's missing to all this work fine like it should?

Here are the two part of my code:

Java (Android):

    private Socket clientSocket;

    private final int SERVERPORT = 60;
    private final String SERVER_IP = "192.168.15.13";

    /////////////////////////////////////////////// CLIENTSOCKET //////////////////////////////////////////////////////
    class ClientThread implements Runnable {

        @Override
        public void run() {

            try {

                InetAddress serverAddr = InetAddress.getByName(SERVER_IP);

                clientSocket = new Socket(serverAddr, SERVERPORT);

                new Thread(new CommsThread()).start();

            } catch (Exception e1) {
                System.out.println(e1.toString());
            }

        }
    }

    class CommsThread implements Runnable {

        @Override
        public void run() {

            try {
                System.out.println("Waiting for server request");

                while(clientSocket.isConnected()){

                    BufferedReader reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
                    PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream())),true);

                    if (reader.ready()) {
                        String line;
                        while ((line = reader.readLine()) != null) {
                            System.out.println(line);

                            if(line != null && !line.trim().isEmpty()) {

                                if(line.equalsIgnoreCase("screencapture")){

                                    // Take screenshot with MediaProjection api

                                    // Send to server =>

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

                                }

                                if(line.equalsIgnoreCase("exit"))

                                    // Do something
                                    break;
                            }
                        }

                    }
                }
                System.out.println("Shutting down Socket!!");
                clientSocket.close();
            } catch (Exception e1) {
                System.out.println(e1.toString());
            }

        }

    }
  ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

   @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        new Thread(new ClientThread()).start();

    }

Delphi (Object Pascal):

uses
 ScktComp, Winsock, pngimage;

type
  TSock_Thread = class(TServerClientThread)
  private
    Png: TPngImage;
    procedure PngReceived;
  protected
    procedure ClientExecute; override;
  end;

type
  TInt32Bytes = record
    case Integer of
      0:
        (Bytes: array [0 .. SizeOf(Int32) - 1] of Byte);
      1:
        (Value: Int32);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

// ===============================================================================

// ===============================================================================

procedure TSock_Thread.PngReceived;
var
  Item: TListItem;
begin
  Item := Form1.ListView1.Selected;
  if (Item <> nil) and (Item.Data = ClientSocket) then
    Form1.img1.Picture.Graphic := Png;
end;

procedure TSock_Thread.ClientExecute;
var
  SocketStrm: TWinSocketStream;
  Buffer: TMemoryStream;
  Size: TInt32Bytes;
  Offset: Integer;
  BytesReceived: Integer;
  BufferPtr: PByte;
begin
  SocketStrm := TWinSocketStream.Create(ClientSocket, 60);
  try
    Buffer := TMemoryStream.Create;
    try
      Png := TPngImage.Create;
      try
        while ClientSocket.Connected do
        begin
          if not SocketStrm.WaitForData(100) then
            Continue;

          Offset := 0;
          while Offset < SizeOf(Int32) do
          begin
            BytesReceived := SocketStrm.Read(Size.Bytes[Offset],
              SizeOf(Int32) - Offset);
            if BytesReceived <= 0 then
              Exit;
            Inc(Offset, BytesReceived);
          end;
          Size.Value := ntohl(Size.Value);
          Buffer.Size := Size.Value;
          BufferPtr := PByte(Buffer.Memory);

          Offset := 0;
          while Offset < Size.Value do
          begin
            BytesReceived := SocketStrm.Read(BufferPtr^, Size.Value - Offset);
            if BytesReceived <= 0 then
              Exit;
            Inc(BufferPtr, BytesReceived);
            Inc(Offset, BytesReceived);
          end;

          Buffer.Position := 0;
          try
            Png.LoadFromStream(Buffer);
          except
            Png.Assign(nil);
          end;

          Synchronize(PngReceived);
        end;
      finally
        Png.Free;
      end;
    finally
      Buffer.Free;
    end;
  finally
    SocketStrm.Free;
  end;
end;

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);
  stat1.Panels.Items[1].Text := 'Conectado';
  Item.Data := Socket.Data;
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;
  stat1.Panels.Items[1].Text := 'Desconectado';
end;

procedure TForm1.ServerSocket1ClientError(Sender: TObject;
  Socket: TCustomWinSocket; ErrorEvent: TErrorEvent; var ErrorCode: Integer);
begin
  ErrorCode := 0;
  stat1.Panels.Items[1].Text := 'Erro na conexão';
  Socket.Close;
end;

procedure TForm1.ServerSocket1GetThread(Sender: TObject;
  ClientSocket: TServerClientWinSocket; var SocketThread: TServerClientThread);
begin
  SocketThread := TSock_Thread.Create(false, ClientSocket);
end;

procedure TForm1.Ativar1Click(Sender: TObject);
begin
  ServerSocket1.Active := true;
end;

procedure TForm1.Desativar1Click(Sender: TObject);
begin
  ServerSocket1.Active := false;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Form1.DoubleBuffered := true;
end;

procedure TForm1.Foto1Click(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.ListView1SelectItem(Sender: TObject; Item: TListItem;
  Selected: Boolean);
begin
  { if (Item <> nil) and Selected then
    img1.Picture.Assign(TSocketData(TCustomWinSocket(Item.Data).Data).Png); }
end;

procedure TForm1.ServerSocket1Listen(Sender: TObject; Socket: TCustomWinSocket);
begin
  stat1.Panels.Items[1].Text := 'Aguardando conexões na porta: ' +
    IntToStr(ServerSocket1.Port);
end;

end.
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • 2
    Why do people still insist on using `ScktComp`? It's extremely old. Also, are you expecting to use this code on Android? `ScktComp` is heavily Windows based, as it was written loooooong before Firemonkey was ever dreamed of. – Jerry Dodge Dec 31 '17 at 19:28
  • @JerryDodge, on **ThreadBlocking** mode, `ServerSocket1ClientConnect` never is executed here. Why? –  Dec 31 '17 at 21:22
  • Seems that this is a [trouble never solved](http://www.codenewsfast.com/cnf/article/0/permalink.art-ng50q2877) –  Dec 31 '17 at 22:49
  • But this can be a possible solution: So, based in [this answer](https://pastebin.com/nauBhDw1) how implement this separate thread to receive connections? [Reference](https://groups.google.com/forum/#!msg/borland.public.cppbuilder.internet.socket/T3YHBxuahTc/HdSsONSOT7sJ) –  Dec 31 '17 at 23:11
  • The question question more specific is: how know if a client connect or disconnect on `stThreadBlocking` mode of `TServerSocket` component? –  Jan 01 '18 at 01:44
  • @JeffersonFarias `TServerSocket` uses `OnConnect` and `OnDisconnect` in both modes – Remy Lebeau Jan 01 '18 at 04:11
  • @Remy Lebeau, ok, now explain why my `OnConnect` event (`TServerSocket`) not is executed (in **stThreadBlocking** mode) when some client estabilish a connection! So, because if not is executed, how i will know if someone had connected to server? –  Jan 01 '18 at 05:04
  • @JeffersonFarias I can't answer that without seeing your project, and I don't have access to the VCL code right this moment to review it (I'd have to wait until later next week). Last I recall, `On(Dis)connect` work in `stThreadBlocking` mode. But worse case scenario, you could simply write your `OnConnect` logic at the beginning of your `TServerClientThread.ClientExecute()` override, and your `OnDisconnect` logic at the end of it – Remy Lebeau Jan 01 '18 at 05:39
  • @RemyLebeau, [here](https://www.sendspace.com/file/hbrzfr) is the project of Delphi code. I'm using DXE5 to compile. –  Jan 01 '18 at 13:02
  • @JeffersonFarias Are you expecting one of us to write your code for you? Remy answered your question below, and clarified what you need to do above. – Jerry Dodge Jan 01 '18 at 14:41
  • @JerryDodge, read please: `I can't answer that without seeing your project` –  Jan 01 '18 at 14:43
  • Then you should please read here: https://stackoverflow.com/help/mcve Specifically *Minimal*. Nobody wants to download your entire project, especially not from a third-party website. – Jerry Dodge Jan 01 '18 at 14:45
  • @JerryDodge, `Specifically Minimal`, then this already is present on own question. The project linked to download is only this same code mounted on IDE. –  Jan 01 '18 at 14:51
  • @JeffersonFarias Sure, but you seem to be missing the point. Remy is not going to download your entire source. None of us are. – Jerry Dodge Jan 01 '18 at 14:54
  • 2
    @JerryDodge, ok. Sorry by arrogance but if you no can, not want, or **don't know** 1 solution to help, then stop with your comments please. I made a question to you in my second comment and you not answed. Then, stop with your comments that nothing help to this question. –  Jan 01 '18 at 15:26
  • Jefferson: This is a public site, and understand that you have no say over whether anyone comments your question or not, other than to flag for moderation any comments that are blatantly rude or abusive or that are "chatty" and off topic (such as this one). Otherwise if a comment doesn't agree with you, then simply ignore it. Either that or better still use the comments to improve your question. A [mcve] as @JerryDodge suggests, is almost never a bad idea. – Hovercraft Full Of Eels Jan 01 '18 at 15:33
  • All project (Delphi and Android) is present on question, now if none of you is able to understand, already is other case. –  Jan 01 '18 at 16:49
  • ... and @RemyLebeau has answered your question so all is good, no? – Hovercraft Full Of Eels Jan 01 '18 at 18:35
  • Not. answed only half of question. Read my question with more attention.`I already put TServerSocket (Delphi component) in this mode, but when the component is in this mode, any connection cannot be received on server from a client Android ` –  Jan 01 '18 at 20:59
  • @JeffersonFarias and what I keep saying is what you claim is wrong. I know for a fact that `TServerSocket` does not behave the way you describe, so there is something else going on. Did you verify that your Android device has a network connection to your PC, and is connecting to the correct IP/port that `TServerSocket` is bound to and listening on? I'm on vacation, I can't verify any code until next week. You have to debug your code yourself – Remy Lebeau Jan 01 '18 at 22:47
  • @RemyLebeau, ok. i'm seeing [this question](https://stackoverflow.com/questions/20292986/error-message-bitmap-image-is-not-valid-on-received-from-socket) and trying discover a possible solution. Have nice vacation ;-) –  Jan 01 '18 at 23:01
  • 1
    @JeffersonFarias: I have now tested `TServerSocket`, and sure enough, when a client connects in `stThreadBlocking` mode, `TServerSocket` fires the `OnGetSocket`, `OnAccept`, `OnGetThread`, and `OnThreadStart` events (in that order), whereas in `stNonBlocking` mode, it fires `OnGetSocket`, `OnClientConnect`, `OnAccept`, and `OnClientWrite` instead. Apparently the `OnClient...` events are only hooked up in `stNonBlocking` mode. I thought it was in both modes. Just goes to show how much one can forget when not using something for a long time. – Remy Lebeau Jan 04 '18 at 23:24
  • @JeffersonFarias: so, you will have to do with I said earlier in reply to [another question](https://stackoverflow.com/questions/48066248/) - in `stThreadBlocking` mode, override `TServerClientThread.ClientExecute()` and handle your connect/disconnect logic from there. You *can* use the `OnAccept` event, just know that it is triggered immediately after the client is accepted and associated with a `TServerClientWinSocket`, but before it is associated with a `TServerClientThread` – Remy Lebeau Jan 04 '18 at 23:29
  • @RemyLebeau, i already solved this trouble based on yours last comments (here and on other question). Thank you. –  Jan 04 '18 at 23:40
  • @RemyLebeau, i had created [**other question**](https://stackoverflow.com/questions/48088922/how-assign-the-same-socketthread-to-several-threads) with a diferent subject, i think that only you can answer of better way. –  Jan 04 '18 at 23:42

1 Answers1

1

Now i want to know (mainly of a Android programmer) if on client side (Android) Socket also must be in ThreadBlocking to estabilish this connection with success

No. Blocking vs non-blocking is strictly a local design choice, it has no effect on the underlying communications at all. A non-blocking client can communicate with a blocking server, and vice versa.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770