0

I am attempting to retrieve the byte values from an InputStream which is being sent to the socket. I have used many ways but it always prints me the address of the byte array instead of its contents.

Below is my code for Client and Server. When a packet is sent from the client to the server, the server instantiates a new Thread to handle the connection. So slaveSocket is the socket I want to use for this.

public class TCPClient {

    public static void main(String[] args) throws IOException{
        Socket socket;
        String address;
        int port;
        String userInput;
        String serverResponse;

        PrintWriter out;
        BufferedReader in;

        //read characters from user
        BufferedReader stdIn;

        if (args.length != 2) {
            System.err.println("Usage: java EchoClient <address> <port>");
            System.exit(1);
        }

        byte[] mode = "octet".getBytes(Charset.forName("UTF-8"));
        address = args[0];
        port = Integer.parseInt(args[1]);

        try{
            //connect socket to server
            socket = new Socket(address, port);

            //Construct PrintWriter to write objects to the socket
            out = new PrintWriter(socket.getOutputStream(), true);

            //Construct BufferedReader to read input from the socket
            in = new BufferedReader(new InputStreamReader(socket.getInputStream()));

            //Another reader to read characters typed by the user
            stdIn = new BufferedReader(new InputStreamReader(System.in));

            //scanner for menu option
            Scanner scanner = new Scanner(System.in);
            int menuOption;

            System.out.println("Press 1 to read from file or 2 to write to file");
            menuOption = scanner.nextInt();

            if (menuOption == 1){
                String filename = "";
                String text = "";

                System.out.println("Enter file name");
                filename = scanner.next();
                byte[] packet = new byte[512];

                //Constructing the RRQ Packet
                //Ading the OPCODE
                packet[0] = 1;
                //adding the filename
                filename.getBytes(Charset.forName("UTF-8"));
                byte[] filenameB = filename.getBytes(Charset.forName("UTF-8"));
                System.arraycopy(filenameB,0,packet,1, filenameB.length);
                //adding a 0
                packet[filenameB.length +1] = 0;
                //adding the mode
                System.arraycopy(mode,0,packet,1+filenameB.length+1,mode.length);
                //adding the last 0
                packet[1+filenameB.length+1+mode.length+1] = 0;



                out.println(packet);

            }else if(menuOption == 2){

            }

            socket.close();
        }catch(UnknownHostException e){
            System.err.println("Dont know about host" + address);
            System.exit(1);
        }catch(IOException e){
            System.err.println("Couldnt get I/O for the connection to " + address);
            System.exit(1);
        }
    }
}

public class TCPServer {

    public static void main(String[] args) throws IOException{

        //port of the server
        int port = 10000;

        //Socket objects
        ServerSocket masterSocket;
        Socket slaveSocket;

        //instantiate the server socket
        masterSocket = new ServerSocket(port);
        System.out.println("Server Started");

        boolean flag1 = true;

        while(true){

            slaveSocket = masterSocket.accept();
            System.out.println("Accepted TCP connection from: " +
                    slaveSocket.getInetAddress() + ", " + slaveSocket.getPort() + "...");
            System.out.println("Initialising new Thread...");

            new TCPServerThread(slaveSocket).start();
        }
    }
}

public class TCPServerThread extends Thread{

    private Socket slaveSocket = null;

    public TCPServerThread(Socket socket){
        super("TCPServerThread");
        this.slaveSocket = socket;
    }

    public void run(){

        byte[] ClientPacket = new byte[512];

        PrintWriter socketOutput;
        InputStream socketInput;
        try{
            //send packet to client
            socketOutput = new PrintWriter((slaveSocket.getOutputStream()), true);
            //read packet from client
            socketInput = new DataInputStream(slaveSocket.getInputStream());

            ClientPacket = socketInput.readAllBytes();
            System.out.println(new String(ClientPacket, StandardCharsets.UTF_8));

        }catch (IOException e){
            System.err.println(e);
        }

    }
}
1
  • out.println(Arrays.toString(packet)); Commented Apr 7, 2022 at 15:25

1 Answer 1

1

You've hopelessly overengineered this.

Writer and Reader do character input and output. InputStream and OutputStream do byte input and output.

You turn byte-based stuff (and in the end, network ports are byte based, not character based) into character based stuff in dangerous ways and then are attempting to read and write bytes into and out of the char-based things.

The solution is simple. Just stop doing that. You have byte-based stuff, there is absolutely no need to involve Reader and Writer.

A bunch of lines that cause problems:

out.println(packet);

PrintStreams are debug aids. You can't use them for any of this. For example, this line will print newlines (definitely not something you'd want in a byte based stream system!), and will print 'objects' - it does that by invoking the .toString() method, and the toString method of arrays are mostly useless. That explains why you see what you see. This is not how you send bytes. You cannot send bytes to a PrintStream (which is a confused mess, as it tries to let you send characters to a byte based system. As I said, you use it for debugging and nothing else. You should not be using it here at all).

new InputStreamReader(socket.getInputStream())

This is dangerous. You're turning a byte based system (InputStream) into a char-based one (Reader) and this always means somebody is making an explicit, 'out of band' (not based on the data in that stream) decision about charset encoding. In this case, as per the docs of InputStreamReader, you get the 'platform default'. Starting with JDK18, it's guaranteed to be UTF-8 fortunately, but before that, who knows what it is. You never want to call this constructor to avoid the confusion. new InputStreamReader(socket.getInputStream, StandardCharsets.UTF_8).

Mostly, though, don't make a reader in the first place. You have no interest whatsoever in reading streams of characters, you just want bytes.

If you have smallish strings and the information about where they 'end' is done 'out of band' (example: The size in bytes (not characters) is sent first, then X bytes that are the string, UTF_8 encoded), you can just read that in as bytes, and then make a string off of that, bypassing any need for Readers and Writers. Reader and Writer is useful only if the entire stream is all character based, or if you have huge strings (hundreds of megabytes) where their end can only be surmised by interpreting the data as characters first. (Mostly, those are horrible protocols that shouldn't be used).

//Construct PrintWriter to write objects to the socket

No, you can't write objects to sockets. Objects aren't bytes. You can write bytes to a socket; some objects will let themselves be turned into bytestreams but this is decidedly not a trivial job, and PrintWriter can't do it at all.

   catch (IOException e) { System.err.println(e);

Most code has no reasonable route to 'deal' with them, but the solution to that is to throw them onwards. Not to catch the exception, print a note of despair, and just keep going on like nothing happened. Doing it right is also less code, so, win-win.

stdIn = new BufferedReader(new InputStreamReader(System.in));

        //scanner for menu option
        Scanner scanner = new Scanner(System.in);

You're making 2 different ways to read standard input. That makes no sense. Pick one.

I tried to fix it for you:

public class TCPClient {
  public static void main(String[] args) throws Exception { // always throw Exception from `main`.

    if (args.length != 2) {
        System.err.println("Usage: java EchoClient <address> <port>");
        System.exit(1);
        return; // Always return after System.exit.
    }

    byte[] mode = "octet".getBytes(Charset.forName("UTF-8"));
    String address = args[0];
    int port = Integer.parseInt(args[1]);

    Scanner scanner = new Scanner(System.in);
    scanner.useDelimiter("\\R"); // split on newlines, not spaces. So much more logical.
    // resources need to be safe-closed - use try-with!
    try (var socket = new Socket(address, port);
         var out = new BufferedOutputStream(socket.getOutputStream());
         var in = socket.getInputStream()) { 

        System.out.println("Press 1 to read from file or 2 to write to file");
        int menuOption = scanner.nextInt();

        if (menuOption == 1) {
            System.out.println("Enter file name");
            String filename = scanner.next();

            //Constructing the RRQ Packet
            //Adding the OPCODE
            out.write(1);
            out.write(filename.getBytes(StandardCharsets.UTF_8));
            out.write(0);
            // The above is dangerous; NUL (0) is actually a valid char.
            // A proper way to send strings is to send length in bytes
            // first. I'll leave it to you to fix your protocol.
            // If it can't be fixed, scan for `\0` chars and get rid of em.
            //adding the mode
            out.write(mode);
            out.write(0);
        }else if (menuOption == 2) {

        }
    }
}

Sending bytes one at a time can be slow (as it ends up sending an entire packet) but can also be useful - the data is just sent, instead of waiting perhaps for a long time for more data. In your case, you send it all in one go, so sending it all off very quickly is not a good idea. Hence, why the outputstream is wrapped in a BufferedOutputStream, which fixes that. You can always use flush() to force sending now, in case you want to keep the connection open (close(), naturally, also flushes).

It's fine if you want to use a byte[] packet instead, but it seems convoluted and unneccessary here. out.write(someByteArray), where out is an OutputStream of some sort, works fine. out.println(byteArray), where out is a Writer of some sort, or a PrintStream - doesn't work at all. (It would take the array, call toString() on it which isn't useful, then convert those bytes using some unknown charset and send that, and none of that is what you want).

You'll need to similarly eliminate PrintStream and the like from your server code.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.