Ответ 1
Проблема, которую вы имеете, связана с природой потоков TCP.
Тот факт, что вы отправили 100 байт (например) с сервера, не означает, что вы впервые прочитаете 100 байтов в клиенте при первом чтении. Возможно, байты, отправленные с сервера, поступают в несколько сегментов TCP к клиенту.
Вам нужно реализовать цикл, в котором вы читаете, пока не будет получено все сообщение.
Позвольте мне привести пример DataInputStream
вместо BufferedinputStream
. Что-то очень просто дать вам просто пример.
Предположим, что вы знаете заранее, что сервер должен отправить 100 байт данных.
В клиенте вам нужно написать:
byte[] messageByte = new byte[1000];
boolean end = false;
String dataString = "";
try
{
DataInputStream in = new DataInputStream(clientSocket.getInputStream());
while(!end)
{
bytesRead = in.read(messageByte);
messageString += new String(messageByte, 0, bytesRead);
if (messageString.length == 100)
{
end = true;
}
}
System.out.println("MESSAGE: " + messageString);
}
catch (Exception e)
{
e.printStackTrace();
}
Теперь обычно размер данных, отправленных одним node (сервером здесь), неизвестен заранее. Затем вам нужно определить свой собственный небольшой протокол для связи между сервером и клиентом (или любыми двумя узлами), взаимодействующими с TCP.
Наиболее распространенным и простым является определение TLV: Тип, Длина, Значение. Таким образом, вы определяете, что каждое отправленное сообщение от сервера к клиенту поставляется с:
- 1 Тип указания байта (например, он может также быть 2 или любой другой).
- 1 Байт (или что-то еще) для длины сообщения
- N байтов для значения (N указывается по длине).
Итак, вы знаете, что вам нужно получить как минимум 2 байта, а со вторым байтом вы знаете, сколько последующих байтов вам нужно прочитать.
Это всего лишь предложение о возможном протоколе. Вы также можете избавиться от "Тип".
Итак, это будет что-то вроде:
byte[] messageByte = new byte[1000];
boolean end = false;
String dataString = "";
try
{
DataInputStream in = new DataInputStream(clientSocket.getInputStream());
int bytesRead = 0;
messageByte[0] = in.readByte();
messageByte[1] = in.readByte();
int bytesToRead = messageByte[1];
while(!end)
{
bytesRead = in.read(messageByte);
messageString += new String(messageByte, 0, bytesRead);
if (messageString.length == bytesToRead )
{
end = true;
}
}
System.out.println("MESSAGE: " + messageString);
}
catch (Exception e)
{
e.printStackTrace();
}
Следующий код компилируется и выглядит лучше. Он предполагает, что первые два байта обеспечивают длину, поступающую в двоичном формате, в сетевую систему (большой конец). Не обращайте внимания на разные типы кодирования для остальной части сообщения.
import java.nio.ByteBuffer;
import java.io.DataInputStream;
import java.net.ServerSocket;
import java.net.Socket;
class Test
{
public static void main(String[] args)
{
byte[] messageByte = new byte[1000];
boolean end = false;
String messageString = "";
try
{
Socket clientSocket;
ServerSocket server;
server = new ServerSocket(30501, 100);
clientSocket = server.accept();
DataInputStream in = new DataInputStream(clientSocket.getInputStream());
int bytesRead = 0;
messageByte[0] = in.readByte();
messageByte[1] = in.readByte();
ByteBuffer byteBuffer = ByteBuffer.wrap(messageByte, 0, 2);
int bytesToRead = byteBuffer.getShort();
System.out.println("About to read " + bytesToRead + " octets");
//The following code shows in detail how to read from a TCP socket
while(!end)
{
bytesRead = in.read(messageByte);
messageString += new String(messageByte, 0, bytesRead);
if (messageString.length() == bytesToRead )
{
end = true;
}
}
//All the code in the loop can be replaced by these two lines
//in.readFully(messageByte, 0, bytesToRead);
//messageString = new String(messageByte, 0, bytesToRead);
System.out.println("MESSAGE: " + messageString);
}
catch (Exception e)
{
e.printStackTrace();
}
}
}