1. Обзор
Для отправки и получения данных по сети мы часто используем сокеты. Сокеты-это не что иное, как комбинация IP-адреса и номера порта, которая может однозначно идентифицировать программу, запущенную на любой данной машине.
В этом уроке мы покажем, как мы можем считывать данные, которые отправляются нам через сокет.
2. Чтение Данных Из Сокета
Давайте предположим, что у нас есть базовое понимание программирования сокетов .
Теперь мы углубимся в чтение данных на порту, который прослушивает наш сервер.
Во-первых, нам нужно объявить и инициализировать ServerSocket, Сокет, и DataInputStream переменные:
ServerSocket server = new ServerSocket(port); Socket socket = server.accept(); DataInputStream in = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
Обратите внимание, что мы решили обернуть сокет InputStream в DataInputStream. Это позволяет нам читать строки текста и примитивные типы данных Java переносимым способом.
Это хорошо, так как теперь, если мы знаем тип данных, которые мы получим, мы можем использовать специализированные методы , такие как readChar (), readInt (), readDouble () и readLine().
Однако это может быть сложно, если тип и длина данных не известны заранее.
В этом случае мы получим поток байтов из сокета, вместо этого используя функцию нижнего уровня read () . Но в этом подходе есть небольшая проблема: Как мы узнаем длину и тип данных, которые мы получим?
Давайте рассмотрим этот сценарий в следующем разделе.
3. Чтение двоичных данных из сокета
При чтении данных в байтах нам необходимо определить наш собственный протокол для связи между сервером и клиентом. Самый простой протокол, который мы можем определить, называется TLV (Значение длины типа). Это означает, что каждое сообщение, записанное в сокет, имеет форму значения длины типа.
Поэтому мы определяем каждое отправленное сообщение как:
- 1 байт символ, представляющий тип данных, например s для Строки
- 4 байт целое число, указывающее длину данных
- А затем фактические данные, длина которых была только что указана
Как только клиент и сервер установят соединение, каждое сообщение будет следовать этому формату. Затем мы можем написать наш код для анализа каждого сообщения и чтения n байтов данных определенного типа.
Давайте посмотрим, как мы можем реализовать это, используя простой пример с сообщением String .
Во-первых, нам нужно вызвать функцию readChar () , чтобы прочитать тип данных, а затем вызвать функцию readInt () , чтобы прочитать его длину:
char dataType = in.readChar(); int length = in.readInt();
После этого нам нужно прочитать данные, которые мы получаем. Здесь важно отметить, что функция read() может быть не в состоянии прочитать все данные за один вызов. Итак, нам нужно вызвать read() в цикле while:
if(dataType == 's') { byte[] messageByte = new byte[length]; boolean end = false; StringBuilder dataString = new StringBuilder(length); int totalBytesRead = 0; while(!end) { int currentBytesRead = in.read(messageByte); totalBytesRead = currentBytesRead + totalBytesRead; if(totalBytesRead <= length) { dataString .append(new String(messageByte, 0, currentBytesRead, StandardCharsets.UTF_8)); } else { dataString .append(new String(messageByte, 0, length - totalBytesRead + currentBytesRead, StandardCharsets.UTF_8)); } if(dataString.length()>=length) { end = true; } } }
4. Код клиента для отправки данных
И как насчет кода на стороне клиента? На самом деле, это довольно просто:
char type = 's'; // s for string String data = "This is a string of length 29"; byte[] dataInBytes = data.getBytes(StandardCharsets.UTF_8); out.writeChar(type); out.writeInt(dataInBytes.length); out.write(dataInBytes);
Это все, что делает наш клиент!
5. Заключение
В этой статье мы обсудили, как считывать данные из сокета. Мы рассмотрели различные функции, которые помогают нам считывать данные определенного типа. Кроме того, мы видели, как читать двоичные данные.
Полную реализацию этого руководства можно найти на GitHub .