Входной поток readUTF не может читать UTF

Я полный нуб в Java, поэтому извиняюсь, если это ошибка новичка. Я пробовал Java NIO, я еще не дошел до стадии, когда я использую неблокирующую функцию. Я просто не могу заставить сервер читать строку, я понимаю, что нелегко отправить байтовый буфер с одной стороны и попытаться интерпретировать его как строку с другой стороны, но я до сих пор не могу понять, куда я иду неправильно. Вот код

*****************************СЕРВЕРНАЯ СТОРОНА********************* *******************

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

    }
}

*******************************СТОРОНА КЛИЕНТА***************** ****************

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


}

Ошибка, которую я продолжаю получать,

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

Это как-то указывает мне, что readUTF читает не формат UTF, а что-то другое.

В общем, что я делаю

Сервер --> ЧитатьUTF

Клиент --> Строка --> Массив байтов в UTF-8 --> ByteBuffer --> Запись

Поскольку я явно кодирую массив байтов в UTF-8. почему readUTF не может его прочитать?


person Desert Ice    schedule 23.01.2015    source источник
comment
Если вы используете readUTF, другая сторона должна использовать writeUTF. Если вы не изучите этот своеобразный формат и не будете следовать мелкому шрифту: длина в два байта, затем символы и т. д. и т. д., но на самом деле не преобразованы в UTF-8 (хотя это может не вызвать никаких проблем, если ваши символы юникода не очень диковинные. )   -  person laune    schedule 23.01.2015
comment
ByteBuffer на самом деле не имеет метода writeUTF, какие-либо обходные пути?   -  person Desert Ice    schedule 23.01.2015
comment
DataOutputStream имеет.   -  person laune    schedule 23.01.2015
comment
Я попробую это. Тем не менее, я немного не понимаю, почему именно он терпит неудачу, если строка преобразуется в байтовый буфер с кодировкой UTF-8, не должна работать readUTF?   -  person Desert Ice    schedule 23.01.2015
comment
Длина, короткий спереди.   -  person laune    schedule 23.01.2015


Ответы (2)


Метод DataInput.readUTF не считывает строку в кодировке UTF-8, он считывает данные в специальном формате, созданном DataOutput.writeUTF, который похож на истинный UTF-8, но не совпадает с ним:

  • он начинается с 16-битного целого числа без знака, указывающего количество следующих байтов, составляющих строку
  • эти следующие байты представляют собой модифицированную форму UTF-8, где U+0000 представлен двумя байтами, а не 1 (поэтому двоичное представление строки не может содержать 0 байтов), а дополнительные символы выше U+FFFF представлены как суррогат пара, при этом старший и младший суррогат кодируются в UTF-8 отдельно по 3 байта каждый (настоящий UTF-8 будет кодировать всю дополнительную кодовую точку за один раз, используя в общей сложности четыре байта).

Если вы пишете истинную UTF-8, вам нужно прочитать истинную UTF-8, если вы хотите readUTF, вы должны writeUTF.

Если вы хотите преобразовать writeUTF в ByteBuffer, довольно просто реализовать оболочку OutputStream вокруг буфера, которую вы, в свою очередь, можете обернуть в 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);
  }

}

(источник)

person Ian Roberts    schedule 23.01.2015
comment
Спасибо, ваше объяснение мне больше всего понравилось. Как-то название функции не указывало на это :). - person Desert Ice; 23.01.2015
comment
Это не тот случай, когда вы должны использовать DataOutputStream.writeUTF, есть несколько способов решить эту проблему, я включил самый простой в свой ответ. - person Peter Lawrey; 23.01.2015
comment
@PeterLawrey, если вы не используете никаких дополнительных символов, да. - person Ian Roberts; 23.01.2015
comment
@IanRoberts изменил readUTF на read. Преобразовал результирующий массив байтов в строку, теперь он работает. Спасибо - person Desert Ice; 23.01.2015
comment
Если вы записываете дополнительный символ, используя кодировку UTF-8, readUTF правильно их декодирует. Если вы пишете дополнительные символы с помощью DataOutputStream.writeUTF, они не будут действительными в кодировке UTF-8. - person Peter Lawrey; 23.01.2015
comment
@PeterLawrey, ты уверен в этом? Глядя на код для DataInputStream в моих версиях Java (7u72 и 8u25), он может обрабатывать только до трех байтов на символ. - person Ian Roberts; 23.01.2015
comment
@DesertIce Каким-то образом Javadoc метода делает указывает на это. Все это. - person user207421; 27.01.2015
comment
@EJP Я просто имел в виду, что имя могло бы быть более понятным. В любом случае документ упоминает об этом. Очищает этот буфер. Позиция установлена ​​на ноль, установлен предел емкости, а отметка сброшена.. Я не вижу ничего, что говорит о том, что режим изменен на чтение - person Desert Ice; 27.01.2015

Вам нужно написать формат, ожидаемый DataInputStream.

e.g.

public static void writeUTF(ByteBuffer bb, String text) {
    byte[] bytes = text.getBytes("UTF-8");
    if (bytes.length > 1 << 16)
        throw new IllegalArgumentException();
    bb.putShort((short) bytes.length);
    bb.write(bytes);
}

Примечание: в то время как writeUTF запишет \0 как два байта вместо одного, readUTF примет его как 1 или 2 байта.

person Peter Lawrey    schedule 23.01.2015