0

Given the following function definitions in Python:

class NetworkClient:
    def __init__(self, ip):
        self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP)
        self.s.connect((str(ip), 7331))

    def readmem(self, address, length):
        if length == 0: raise BaseException, "Why are you giving me no length" #Please don't do this to me
        if not self.ValidMemory().validrange(address, length): return
        if not self.ValidMemory().validaccess(address, length, "read"): return
        self.s.send("\x04") #cmd_readmem
        request = struct.pack(">II", address, address + length)
        self.s.send(request)
        status  = self.s.recv(1)
        if status == "\xbd": #Non-zero memory was found
            response = self.s.recv(length)
        elif status == "\xb0": #All zeroes
            response = "\x00" * length
        else: return #Something went terribly wrong
        return response

I now want to do the same in Java. As example I want to read the memory at address 0x10000003 with length 0x1 e.g. 1 byte.

So far I wrote the following code:

import java.io.*;
import java.net.Socket;
import java.nio.charset.StandardCharsets;

public class NetworkClient
{
    public static void main(String[] arguments) throws IOException, InterruptedException
    {
        try (Socket clientSocket = new Socket("192.168.178.35", 7331);
             PrintWriter outputWriter =
                     new PrintWriter(clientSocket.getOutputStream(), true);
             BufferedReader inputReader =
                     new BufferedReader(
                                               new InputStreamReader(clientSocket.getInputStream())))
        {
            System.out.println("Connected");

            byte readMemoryCommand = (byte) 4;
            int memoryAddress = 0x10000003;
            int length = 0x1;

            outputWriter.print(readMemoryCommand);
            outputWriter.print(memoryAddress);
            outputWriter.println(memoryAddress + length);

            System.out.println("Receiving");
            System.out.println(inputReader.read());
            System.out.println("Received");
        }
    }

    public static byte[] toByteArray(String letters)
    {
        return letters.getBytes(StandardCharsets.US_ASCII);
    }
}

Establishing the connection works but then after sending the data, there is no response. The server does not send anything back and my Java program is stuck on readLine(). The output is as follows:

Connected
Receiving

Replacing readLine() with read() does not help. Using a DataOutputStream with write and writeInt calls does not work either.

The difficulty I'm having here seems to be to prepare and send the request data correctly. For your information, the Python function struct.pack produces the following result in an interactive Python session:

>>> import struct
>>> struct.pack(">II", 10000003, 10000004)
b'\x00\x98\x96\x83\x00\x98\x96\x84'
>>>

Both integers are converted to hexadecimal and "packed" into a byte array.

The readmem command String is converted to an ASCII byte array I believe:

>>> print("\x04")
*Box with questionmark in it*
>>> s = "04"
>>> ":".join("{:02x}".format(ord(c)) for c in s)
'30:34'
>>>

The latter code for printing the bytes is taken from here.

So what is wrong with the Java code?

Community
  • 1
  • 1
BullyWiiPlaza
  • 17,329
  • 10
  • 113
  • 185
  • Actually, python's `"\x04"` is simply a single byte with the value 4. So sending a Java `"04"` is very different. – RealSkeptic Sep 15 '15 at 16:44
  • @RealSkeptic So I would rather convert the integer 4 to a byte and send that? In Java I'm doing this: `outputWriter.print((byte) 4);` However, this still didn't solve the problem – BullyWiiPlaza Sep 15 '15 at 16:47
  • 1
    No, to send bytes in Java, you do not use a `Writer` and you do not use `print`. You use a byte-based `OutputStream` and use `write`. Better yet, use a channel rather than a stream and use a `ByteBuffer`. But you'll have to do some reading of the Java documentation. – RealSkeptic Sep 15 '15 at 16:52
  • @RealSkeptic I tried `OutputStream outputStream = clientSocket.getOutputStream();` and `write()` calls for the 3 arguments and finally a `flush()` but it also didn't work – BullyWiiPlaza Sep 15 '15 at 16:57
  • You should be using [DataOutputStream](http://docs.oracle.com/javase/8/docs/api/java/io/DataOutputStream.html) instead of PrintWriter. Pay close attention to the documentation—the `write` and `writeByte` methods send *one byte,* while `writeInt` sends four bytes. Similarly, reading should be done with a [DataInputStream](http://docs.oracle.com/javase/8/docs/api/java/io/DataInputStream.html), specifically the `readByte` and `readFully` methods, rather than BufferedReader. – VGR Sep 15 '15 at 18:15
  • @VGR Thanks a lot, now it is working. Feel free to write an answer to have it accepted – BullyWiiPlaza Sep 16 '15 at 13:02

1 Answers1

1

You should be using DataOutputStream instead of PrintWriter. Pay close attention to the documentation—the write and writeByte methods send one byte, while writeInt sends four bytes. Similarly, reading should be done with a DataInputStream, specifically the readByte and readFully methods, rather than BufferedReader.

Writers and Readers deal with text, not bytes. PrintWriter.print sends an ASCII representation of a number, rather than the byte itself. BufferedReader (and most Readers) will try to decode the incoming bytes into text, instead of reading the bytes directly.

VGR
  • 40,506
  • 4
  • 48
  • 63