Search code examples
javasocketsnioeofexception

Inputstream readUTF can't read UTF


I am a total noob at Java, so apologies if this is a rookie mistake. I was trying out Java NIO, I still haven't gotten to a stage where I am using the non-blocking feature. I just am not able to get the server to read a string, I understand its not easy to send a Bytebuffer from one side and trying to interpret it as String on the other side, but i still can't figure out where I am going wrong. Here is the code

*****************************SERVER SIDE**************************************

class MyBlockingServer extends Thread
{
    private int M_PortNumber;
    private ServerSocket M_ServerSocket;

    MyBlockingServer(int PortNumber)
    {
        M_PortNumber = PortNumber;
        try {
            M_ServerSocket = new ServerSocket(M_PortNumber);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public void run()
    {
        int my_number = 0;
        while(true)
        {
            try {
                Socket Server = M_ServerSocket.accept();


                DataInputStream inputStream = new DataInputStream(Server.getInputStream());
                System.out.println("[SERVER]" +inputStream.readUTF());


                DataOutputStream outputStream = new DataOutputStream(Server.getOutputStream());
                outputStream.writeUTF("Thanks for connection, you suck tata" + " "+ my_number);

                my_number++;
                Server.close();

            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

    void socket_close()
    {
        try {
            M_ServerSocket.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}

public class JavaBlocking
{

    public static void main(String []args)
    {
        MyBlockingServer Server = new MyBlockingServer(8000);
        try {
            Server.start();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }
}

*******************************CLIENT SIDE*********************************

public class JavaChannels 
{

    public static void main(String []args)
    {
        SocketChannel client_channel = null;

        try {
            client_channel = SocketChannel.open();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        System.out.println("[Client] Socket channel open");

        try {
            client_channel.connect(new InetSocketAddress("127.0.0.1",8000));
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        System.out.println("[Client] Socket channel connected");

        ByteBuffer my_buffer = ByteBuffer.allocate(48);
        my_buffer.clear();

        try {
            my_buffer.put("WHY_YOU_NO_WORK".getBytes("UTF-8"));
        } catch (UnsupportedEncodingException e2) {
            // TODO Auto-generated catch block
            e2.printStackTrace();
        }

        my_buffer.flip();

        try {
            int bytes_written = client_channel.write(my_buffer);

            while(my_buffer.hasRemaining())
            {
                bytes_written = client_channel.write(my_buffer);
            }

            System.out.println("[Client] Wrote "+ bytes_written +" bytes");
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        System.out.println("[Client] Socket channel write finished");

        my_buffer.clear();
        my_buffer.flip();


        try {
            client_channel.read(my_buffer);
        } catch (IOException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }

        System.out.println("[Client] server says" + new String(my_buffer.array()));

        try {
            client_channel.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }


}

The error i keep getting is

java.io.EOFException at java.io.DataInputStream.readFully(DataInputStream.java:197) at java.io.DataInputStream.readUTF(DataInputStream.java:609) at java.io.DataInputStream.readUTF(DataInputStream.java:564) at netty_tutorial.blocking.MyBlockingServer.run(JavaBlocking.java:39)

This somehow indicates to me that readUTF is not reading UTF format but something else.

In summary what i am doing is

Server --> ReadUTF

Client --> String -->Byte Array in UTF-8 --> ByteBuffer -->Write

As I am explicitly encoding the byte array into UTF-8. why can't readUTF read it?


Solution

  • The DataInput.readUTF method doesn't read a UTF-8 encoded string, it reads data in the specific format that DataOutput.writeUTF creates, which is similar to but not the same as true UTF-8:

    • it starts with a 16 bit unsigned integer giving the number of following bytes that make up the string
    • these following bytes are a modified form of UTF-8, where U+0000 is represented in two bytes rather than 1 (so the binary representation of the string cannot contain any 0 bytes) and supplementary characters above U+FFFF are represented as a surrogate pair, with the high and low surrogate encoded to UTF-8 separately in 3 bytes each (true UTF-8 would encode the whole supplementary code point in one go using a total of four bytes).

    If you are writing true UTF-8 then you need to read true UTF-8, if you want to readUTF then you must writeUTF.

    If you want to writeUTF to a ByteBuffer it's pretty straightforward to implement an OutputStream wrapper around the buffer, which you can in turn wrap in a DataOutputStream:

    class ByteBufferBackedOutputStream extends OutputStream{
      ByteBuffer buf;
      ByteBufferBackedOutputStream( ByteBuffer buf){
        this.buf = buf;
      }
      public synchronized void write(int b) throws IOException {
        buf.put((byte) b);
      }
    
      public synchronized void write(byte[] bytes, int off, int len) throws IOException {
        buf.put(bytes, off, len);
      }
    
    }
    

    (source)