Как узнать, пришли ли данные с сервера, и как передать массив байтов?

Я недавно начал изучать сетевое программирование и язык Java. У меня есть сервер, который отправляет клиенту пакет данных в виде массива byte[]. Для отправки данных я использовал PrintWriter, а на клиентской стороне – Scanner. Однако я сталкиваюсь с проблемой: вместо массива byte[] с 53 элементами я получаю массив только из 10 элементов. 

Я понимаю, что Scanner удобен благодаря методу hasNext(), но в других аналогах не нашёл ничего подобного. В связи с этим я задумал преобразовать пакет в byte[], а затем в String для его передачи. На клиенте я планирую преобразовать String обратно в byte[] и восстановить исходный пакет с помощью метода parse. Однако этот подход не сработал.

BufferedInputStream и BufferedOutputStream справляются с задачей корректно, но я теряю возможность работы с hasNext() и не могу обеспечить бесперебойную работу программы. 

Что именно я делаю неправильно? 

### Клиент
```java
while (true) {
    if (input.hasNext()) {
        byte[] message = input.nextLine().getBytes(StandardCharsets.UTF_8);
        MSPacket pct = MSPacket.parse(message);
        System.out.println(new String(pct.senderName, StandardCharsets.UTF_8) +
                new String(pct.msg, StandardCharsets.UTF_8));
    }
}
```

### Сервер
```java
MSPacket msgToClient = new MSPacket("Сервер", "Hello!!!", 0x01);
output.println(msgToClient.toPacket().toString());
output.flush();
```
  • 27 сентября 2024 г. 20:53
Ответы на вопрос 2
Ваш вопрос касается передачи байтовых массивов между клиентом и сервером в Java. Давайте разберем вашу проблему пошагово и предложим подход, который будет работать корректно.

### Проблема

Вы используете `PrintWriter` для отправки данных и `Scanner` для их получения. Однако `PrintWriter` и `Scanner` предназначены в первую очередь для работы со строками и текстом, а вы пытаетесь передавать и обрабатывать байтовые данные, что ведет к проблемам, таким как потеря данных или их неполучение.

### Решение

Чтобы правильно передать массив байтов, вам следует использовать потоки ввода и вывода байтов, такие как `DataOutputStream` и `DataInputStream`. Этот подход позволит вам передавать и получать массивы байтов и не потерять никаких данных.

#### Серверная часть

Вы можете изменить код сервера для использования `DataOutputStream`:

```java
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class MyServer {
    public static void main(String[] args) {
        try (ServerSocket serverSocket = new ServerSocket(1234)) {
            while (true) {
                Socket clientSocket = serverSocket.accept(); // ожидание подключения
                try (DataOutputStream output = new DataOutputStream(clientSocket.getOutputStream())) {
                    MSPacket msgToClient = new MSPacket("Сервер", "Hello!!!", 0x01);
                    byte[] packetData = msgToClient.toPacket(); // Получите пакет в формате byte[]
                    output.writeInt(packetData.length); // Сначала отправляем длину пакета
                    output.write(packetData); // Затем отправляем сам пакет
                    output.flush();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
```

#### Клиентская часть

Клиент также должен использовать `DataInputStream` для корректного чтения данных:

```java
import java.io.DataInputStream;
import java.io.IOException;
import java.net.Socket;

public class MyClient {
    public static void main(String[] args) {
        try (Socket socket = new Socket("localhost", 1234);
             DataInputStream input = new DataInputStream(socket.getInputStream())) {

            while (true) {
                // Считываем длину сообщения
                int length = input.readInt();
                if (length > 0) {
                    byte[] message = new byte[length];
                    input.readFully(message); // Считываем все байты сообщения
                    MSPacket pct = MSPacket.parse(message);
                    System.out.println(new String(pct.senderName, StandardCharsets.UTF_8) +
                                       new String(pct.msg, StandardCharsets.UTF_8));
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
```

### Объяснение изменений

1. **Типы потоков**: Вместо `PrintWriter` и `Scanner` используются `DataOutputStream` и `DataInputStream` для работы с байтовыми массивами.

2. **Передача длины сообщения**: Перед тем как отправить массив байтов, сервер отправляет длину этого массива. Это поможет клиенту точно знать, сколько байтов следует прочитать.

3. **Чтение полного пакета**: На стороне клиента используется `readFully()` для считывания всего сообщения.

Теперь ваш код будет правильно обрабатывать и передавать массивы байтов, и вы избежите проблем, связанных с потерей данных или ошибками при чтении.
Наконец-то исправил. Проблема была в кодировке. При передаче byte array надо конвертировать в String с использованием кодировки "windows-1251" с помощью new String(msgToClient.ToPacket(), "windows-1251"), на клиенте принять с помощью input.nextLine().getBytes("windows-1251").
Похожие вопросы