Непоследовательный результат от DataInputStream - PullRequest
0 голосов
/ 10 декабря 2018

У меня возникла проблема с использованием DataInputStream и DataOutputStream в Java, которую я не смог решить без каких-либо действительно глупых мер, и мне было интересно, если кто-нибудь знает о лучшем способе (я ОЧЕНЬ уверен, что есть один, но у меня нет 'я не смог его найти).

Справочная информация. Я хочу иметь возможность отправлять строку Json на сервер, чтобы сервер анализировал эту строку как объект.Это должно быть независимым от языка, так как проект, над которым я работаю для изучения, требует наличия гетерогенной системы.

Ради простейшего возможного примера, показывающего мою проблему, я исключу создание Gson / Jsonи т.д., как это может быть воссоздано с любой строкой.Код выглядит следующим образом:

public class ServerSide {
    private final int PORT = 5001;
    private ServerSocket servSock;
    private Socket sock;
    private DataInputStream dis;
    private DataOutputStream dos;

    public ServerSide() throws IOException {
        servSock = new ServerSocket(PORT);
    }

    public void startListening() {
        try {
            sock = servSock.accept();
            dis = new DataInputStream(sock.getInputStream());
            byte[] bytes = new byte[1024];

            dis.read(bytes);

            String receivedMessage = new String(bytes);
            System.out.println("Message received: " + receivedMessage);
            sock.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

}

Простой тестовый класс, который создает ServerSide () и вызывает startListening через некоторое время (true).

public class ClientSide {
    private final int PORT = 5001;
    private Socket socket;
    private DataOutputStream dos;

    public ClientSide() {

    }
    public void sendMessage(String m) {
        try {
            socket = new Socket("127.0.0.1", PORT);
            dos = new DataOutputStream(socket.getOutputStream());
            dos.writeBytes(m);
            System.out.println("Message sent: " + m);

        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}

Простой тестовый класс, который создаетClientSide () объект и вызывает sendMessage («это тестовое сообщение»);

У меня проблема в том, что я получаю только частичное сообщение на стороне сервера.Я не уверен, что это проблема с потоком ввода или вывода, и мне удалось найти обходные пути, записывая и считывая данные несколько раз и обрезая пропуски между ними.Моя первая проблема с этим была при попытке отправить строку Json в программу, которую я написал на c #, где он всегда отправлял первый символ ("{"), а затем оставшуюся часть строки при следующем чтении.Я противостоял этому, отправив один пробел и проигнорировав это на стороне сервера.Проблема усугубляется в java-java, когда кажущееся случайное количество строки читается при первом чтении.

Несколько примеров выходных данных приведенного выше кода для серверной стороны:

Message received: This is a tes

Message received: T

Message received: T

Message received: T

Ответы [ 2 ]

0 голосов
/ 10 декабря 2018

Благодаря помощи, предоставленной @Illya Kysil, я нашел решение, изменив свой код на:

public class ClientSide {
    private final int PORT = 5001;
    private Socket socket;
    private DataOutputStream dos;

    public ClientSide() {

    }
    public void sendMessage(String m) {
        try {
            socket = new Socket("127.0.0.1", PORT);
            dos = new DataOutputStream(socket.getOutputStream());
            byte[] mBytes = m.getBytes();
            dos.writeInt(mBytes.length);
            dos.write(mBytes);
            System.out.println("Message sent: " + m);
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}

и

public class ServerSide {
    private final int PORT = 5001;
    private ServerSocket servSock;
    private Socket sock;
    private DataInputStream dis;
    private DataOutputStream dos;

    public ServerSide() throws IOException {
        servSock = new ServerSocket(PORT);
    }

    public void startListening() {
        try {
            sock = servSock.accept();
            dis = new DataInputStream(sock.getInputStream());
            int length = dis.readInt();
            byte[] bytes = new byte[length];
            dis.readFully(bytes);
            String receivedMessage = new String(bytes);
            System.out.println("Message received: " + receivedMessage.trim());
            dis.close();
            sock.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

}

Сначала будет отправлено целое число, равноеразмер байта Массив отправляемого сообщения.На стороне сервера может быть создан буфер такой длины, а для заполнения этого буфера можно использовать метод readFully объекта DataInputStream.

0 голосов
/ 10 декабря 2018

is.read(bytes); может возвращать любое количество байтов - от одного до bytes.length.

Считывает некоторое количество байтов из входного потока и сохраняет их в буферемассив б.Количество фактически прочитанных байтов возвращается как целое число.Этот метод блокируется до тех пор, пока не будут доступны входные данные, не обнаружен конец файла или не сгенерировано исключение.

Повторные вызовы этого метода требуются до тех пор, пока он не вернет -1, что означает конец потока.

Чтение и запись в сокет предоставляет пример того, как читать все данные в линейно-ориентированных обменах.Для двоичных данных (которые фактически генерирует DataStream) вам необходимо использовать комбинацию ByteArrayOutputStream, ByteArrayInputStream и DataInputStream:

InputStream sis = sock.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
while (int len = sis.read(bytes) > 0) {
    baos.write(bytes, 0, len);
}
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
DataInputStream dis = new DataInputStream(bais);
byte[] vBytes = new byte[baos.size()];
int sLen = dis.read(vBytes);
String receivedMessage = new String(vBytes, 0, sLen);
System.out.println("Message received: " + receivedMessage);

Примечание: приведенный выше код отвечает на конкретный вопрос.Не пускай в производство :) 1020 *

...