Проблема с сокетом Java: пакеты объединяются на стороне получателя

#java #sockets #client

#java #Сокеты #клиент

Вопрос:

У меня проблема с сокетом. Эта проблема возникает, когда я запускаю сервер и клиент на одном компьютере, т.Е. с использованием параметра «localhost». Но проблема не видна, когда используются разные компьютеры. Клиент отправляет файл с этими кодами:

 output_local.write(buffer, 0, bytesRead);
output_local.flush();
  

И после этого другим способом я отправляю команду с этими:

 outputStream.write(string);
outputStream.flush();
  

Сервер добавляет команду в конец файла. Поэтому он думает, что еще не получил команду от клиента. у вас есть идея, что может вызвать эту проблему? Как я могу устранить дефект? ниже приведен метод получения файла на сервере:

     while (true) {
        try {
            bytesReceived = input.read(buffer);
        } catch (IOException ex) {
            Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
            System.out.println("exception occured");
            break;
        }
        System.out.println("received:"   bytesReceived);
        try {
            /* Write to the file */
            wr.write(buffer, 0, bytesReceived);
        } catch (IOException ex) {
            Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
        }
        total_byte = total_byte   bytesReceived;
        if (total_byte >= filesizeInt) {
            break;
        }
    }
  

Ответ №1:

Если вам нужна поддержка, подобная сообщениям, вам необходимо создать протокол, чтобы уточнить, что вы собираетесь отправлять и получать.

В TCP вы не можете полагаться на получение отдельных «пакетов» отдельно (например, отправка 4 блоков по 10 байт может быть получена как 1 фрагмент из 40, или из 2 блоков по 20, или один фрагмент из 39 и один фрагмент из 1). TCP гарантирует доставку заказа, но не какую-либо конкретную «пакетизацию» ваших данных.

Так, например, если вы отправляете строку, вам нужно сначала отправить длину строки, а затем ее байты. Логика в псевдокоде будет примерно такой:

Клиент:

  1. Отправьте индикатор команды
  2. Отправьте длину полезной нагрузки
  3. Отправьте полезную нагрузку

Сервер:

  1. Прочитайте индикатор команды
  2. Прочитайте длину полезной нагрузки
  3. Цикл считывания полезной нагрузки до тех пор, пока не будет прочитана полная длина

Ответ №2:

Дефект заключается в том, что вы обрабатываете протокол на основе потоков (TCP), как если бы это был протокол, ориентированный на сообщения. Это не так. Вы должны предположить, что это может произойти.

Если вам нужно разбить ваш поток на отдельные сообщения, вы должны использовать либо разделители, либо (предпочтительно IMO) префикс длины для каждого сообщения. Вы также должны предвидеть, что любое прочитанное вами сообщение может не получить столько данных, сколько вы запросили — другими словами, сообщения могут не только объединяться, если вы не будете осторожны, но их можно легко разделить.

Я упоминал, что предпочитаю добавлять префикс длины к разделителям. Плюсы и минусы:

  • Преимущество использования разделителя сообщений заключается в том, что вам не нужно знать размер сообщения перед началом отправки.
  • Преимущества использования префикса длины:
    • Коду для чтения сообщения вообще не нужно заботиться о данных в сообщении — ему нужно только знать, какой он длины. Вы считываете длину сообщения, считываете данные сообщения (циклически, пока не прочитаете все), а затем передаете сообщение для обработки. Просто.
    • Вам не нужно беспокоиться о «экранировании» разделителя, если вы хотите, чтобы он отображался в обычном сообщении.

Ответ №3:

Поскольку TCP является потоковым соединением, такое поведение является нормальным, если устройство записи записывает быстрее, чем считывает устройство чтения, или чем стек TCP отправляет пакеты.

Вы должны добавить разделитель для разделения частей потоков, например, с помощью поля длины для подпакетов или с помощью разделителей, таких как новая строка ( n , код символа 10).

Другим вариантом может быть использование UDP (или даже SCTP), но это зависит от выполняемой задачи.