xR34P3Rx
xR34P3Rx

Reputation: 395

Java send screenshot over socket

Im working on a prank program but is somewhat useful anyway.

What im currently trying to do is get screenshots from the client and open the screenshot on the server as a JFrame.

After numerous attempts, i found this unresolved question How to send images through sockets in java?.

The screenshot comes through but its chopped and only about 120-135kb make it through. In the top voted answer he has Thread.sleep() on the client side before it closes the connection but i tried it without it and it came out chopped so i tried it as he put it but it still came back chopped. My client Class that reads input data is on loop and runs every 100ms so i thought that it was looping too fast so i increased it to 1000ms and it still came chopped.

I tried something different on that same thread with another answer and changed on the server side the ImageIO.read(new ByteArrayInputStream(imageAr)) to ImageIO.read(new ByteArrayInputStream(imageAr)) but that time no image came in.

This was one of the original methods that i tried when i started but this did not work at all! https://javabelazy.blogspot.com/2013/10/sending-screenshot-from-client-to.html.

Server

package UI;

import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.ByteBuffer;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;

public class ViewScreen implements Runnable{
private static Thread t;
@Override
public void run(){
    //Screenshot Frame
    final JFrame frame = new JFrame();
    frame.setSize(500, 500);
    frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
    t = new Thread(){
        public void run(){
            try{
                int port = 6667;
                ServerSocket serverSocket = new ServerSocket(port);
                Socket server = serverSocket.accept();
                TrojanUI.console.append("[*]Waiting for screenshot on port " + serverSocket.getLocalPort() + "...\n");
                DataInputStream in = new DataInputStream(server.getInputStream());
                DataOutputStream out = new DataOutputStream(server.getOutputStream());
                byte[] sizeAr = new byte[4];
                in.read(sizeAr);
                int size = ByteBuffer.wrap(sizeAr).asIntBuffer().get();

                byte[] imageAr = new byte[size];
                in.read(imageAr);

                BufferedImage image = ImageIO.read(new ByteArrayInputStream(imageAr));
                ImageIO.write(image, "jpg", new File("screen.jpg"));
                server.close();
                JLabel label = new JLabel();
                label.setIcon(new ImageIcon(image));
                frame.getContentPane().add(label);
            }catch(IOException e){
                e.printStackTrace();
            }
        }
    };
    t.start();
    frame.setVisible(true);
}
}

This part of the server code is a new thread, creating a new socket and a new JFrame to display the screenshot. Why am i doing this? On another thread i read that the image would only send if the socket closes. So since i dont want the main socket to close or else i lose connection, i created another socket that will open temporarily only to stream the screenshot.

Client

package tc;

import java.awt.AWTException;
import java.awt.HeadlessException;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.ByteBuffer;
import javax.imageio.ImageIO;

public class SendScreen{
public SendScreen(){
    try{
        //creates new socket on port 6667
        Socket sclient = new Socket("192.168.0.5",6667);
        OutputStream sOutToServer = sclient.getOutputStream();
        DataOutputStream out = new DataOutputStream(sOutToServer);
        InputStream sInFromServer = sclient.getInputStream();
        DataInputStream in = new DataInputStream(sInFromServer);

        //Takes screenshot and sends it to server as byte array
        BufferedImage screencap = new Robot().createScreenCapture(new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()));
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ImageIO.write(screencap,"jpg",baos);

        //Gets screenshot byte size and sends it to server and flushes then closes the connection
        byte[] size = ByteBuffer.allocate(4).putInt(baos.size()).array();
        out.write(size);
        out.write(baos.toByteArray());
        out.flush();
        sclient.close();
    }catch(HeadlessException | AWTException | IOException e){
        e.printStackTrace();
    }
}
}

On the server side i added another line that creates the image locally just to troubleshoot if anything comes through.

Thanks in advance

Upvotes: 3

Views: 2023

Answers (1)

user207421
user207421

Reputation: 310909

You're assuming that read() fills the buffer. Nothing in the specification that says so. Try this:

int size = in.readInt();
byte[] imageAr = new byte[size];
in.readFully(imageAr);

readInt() and readFully() are guaranteed to read the correct amount, or fail in the attempt.

You're also giving yourself a hard time at the sending end. Try this:

out.writeInt(size);
out.write(baos.toByteArray());
out.close();

Note that flush() before close() is redundant, but you should close the outermost output stream around the socket, not the socket, to get it auto-flushed.

In fact as you are closing the connection after each image you could get rid of the size word and the byte array input/output streams and just do ImageIO.read/write directly from/to the socket. It would save a lot of memory.

Upvotes: 3

Related Questions