Socket.getInputStream: примеры (JAVA)

Чтение сетевых данных через getInputStream
Раздел: Сокеты (сетевые)
Socket.getInputStream: InputStream

Общее описание

Метод Socket.getInputStream() возвращает объект java.io.InputStream, позволяющий читать входящие байты из установленного TCP-соединения. Использование актуально в клиентских и серверных приложениях при работе с потоковой передачей данных по протоколу TCP.

Ключевые свойства и поведение:

  • Аргументы: метод не принимает аргументов.
  • Возвращаемое значение: InputStream - поток для чтения байт из сокета.
  • Исключения: бросает IOException, если сокет закрыт или возникли ошибки ввода/вывода при получении потока.
  • Блокировка: чтение из возвращенного потока блокирует вызывающий поток до появления данных или EOF, если не установлен таймаут (setSoTimeout).
  • Повторные вызовы: несколько вызовов getInputStream() обычно возвращают один и тот же объект потока, соответствующий сокету.
  • Закрытие: закрытие возвращенного InputStream приведет к закрытию соответствующего сокета. Аналогично, закрытие сокета закроет поток.
  • Совместимость: для неблокирующих операций применяются NIO API (SocketChannel), а не getInputStream().

Рекомендуемые практики: чтение в цикл до возврата -1 (EOF), использование буферов (например, BufferedInputStream), явное закрытие ресурсов в блоке try-with-resources и обработка SocketTimeoutException при установленном таймауте.

Короткие примеры

Пример 1. Простой клиент, читающий строку от сервера (байтово-кодировка UTF-8):

import java.io.*;
import java.net.*;

public class SimpleClient {
  public static void main(String[] args) throws Exception {
    try (Socket s = new Socket("localhost", 12345);
         InputStream in = s.getInputStream();
         InputStreamReader isr = new InputStreamReader(in, "UTF-8");
         BufferedReader br = new BufferedReader(isr)) {
      String line = br.readLine();
      System.out.println(line);
    }
  }
}
Пример вывода (если сервер отправил "Hello\n"):
Hello

Пример 2. Чтение бинарных данных в буфер до EOF:

import java.io.*;
import java.net.*;

public class ReadBytes {
  public static void main(String[] args) throws Exception {
    try (Socket s = new Socket("example.com", 80);
         InputStream in = s.getInputStream()) {
      byte[] buf = new byte[4096];
      int read;
      while ((read = in.read(buf)) != -1) {
        // обработка buf[0..read-1]
      }
    }
  }
}
Если соединение закроет удаленная сторона, цикл завершится (EOF).

Пример 3. Таймаут чтения с обработкой SocketTimeoutException:

Socket s = new Socket();
s.connect(new InetSocketAddress("host", 9000), 2000); // connect timeout
s.setSoTimeout(3000); // read timeout
try (InputStream in = s.getInputStream()) {
  int b = in.read(); // бросит SocketTimeoutException если данных нет 3 сек
} catch (java.net.SocketTimeoutException e) {
  System.out.println("read timed out");
}
read timed out

Похожие API в Java

  • Socket.getOutputStream() - для записи в соединение; часто используется вместе с getInputStream().
  • SocketChannel (NIO) - поддерживает неблокирующие операции, селекторы и масштабируемые сервера. Предпочтительнее при высокой нагрузке и необходимости неблокирующего ввода-вывода.
  • InputStream-обертки (BufferedInputStream, DataInputStream, ObjectInputStream) - облегчают чтение примитивов, объектов или повышают производительность за счет буферизации.
  • SSLSocket - для защищенных соединений; предоставляет тот же метод getInputStream(), но с шифрованием и установкой TLS.

Выбор между ними зависит от требований: для простоты и синхронного кода достаточно getInputStream(); для асинхронной или масштабируемой обработки - NIO; для защищенных каналов - SSL.

Аналоги в других языках

  • Python: socket.recv() или файл-объект socket.makefile().read().
    import socket
    s = socket.create_connection(("example.com", 80))
    s.sendall(b"GET / HTTP/1.0\r\nHost: example.com\r\n\r\n")
    print(s.recv(1024))
    b'HTTP/1.0 200 OK\r\n...'
  • JavaScript (Node.js): net.Socket и событие 'data':
    const net = require('net');
    const socket = net.createConnection(80, 'example.com');
    socket.on('data', (chunk) => console.log(chunk.toString()));
    HTTP-ответ в консоли
  • PHP: fsockopen / stream_socket_client и fread / stream_get_contents.
    $s = stream_socket_client("tcp://example.com:80");
    fwrite($s, "GET / HTTP/1.0\r\nHost: example.com\r\n\r\n");
    echo fread($s, 1024);
    HTTP-ответ
  • C#: System.Net.Sockets.NetworkStream через TcpClient.GetStream() или Socket.Receive().
    using (var client = new TcpClient("example.com", 80)) {
      using (var stream = client.GetStream()) {
        byte[] buf = new byte[1024];
        int read = stream.Read(buf, 0, buf.Length);
      }
    }
    HTTP-ответ в буфере
  • Go: интерфейс net.Conn и метод Read:
    conn, _ := net.Dial("tcp", "example.com:80")
    fmt.Fprint(conn, "GET / HTTP/1.0\r\nHost: example.com\r\n\r\n")
    b := make([]byte, 1024)
    n, _ := conn.Read(b)
    fmt.Println(string(b[:n]))
    HTTP-ответ
  • Lua: библиотека LuaSocket - tcp:receive().
    local socket = require('socket')
    local c = assert(socket.tcp())
    c:connect('example.com', 80)
    c:send('GET / HTTP/1.0\r\nHost: example.com\r\n\r\n')
    local s = c:receive('*l')
    print(s)
    HTTP-строка

Отличия от Java: в разных языках API бывает синхронным событийным или блокирующим. Java предоставляет как блокирующие потоки (getInputStream()), так и более низкоуровневую NIO-модель для неблокирующего ввода-вывода.

Типичные ошибки и примеры

Ниже несколько распространенных проблем при работе с getInputStream().

Ошибка 1. Ожидание данных без таймаута (висит навсегда).

Socket s = new Socket("remote", 9000);
InputStream in = s.getInputStream();
int b = in.read(); // блокируется, если сервер ничего не отправляет
Программа может зависнуть бесконечно, если удаленная сторона не отправляет данные.

Ошибка 2. Некорректная проверка available() для чтения всех данных.

int avail = in.available();
byte[] buf = new byte[avail];
in.read(buf); // может прочитать меньше байт или 0
// неверное предположение: available() == полный размер сообщения
available() возвращает количество байт, которые можно прочитать без блокировки, но не гарантию полного сообщения.

Ошибка 3. Закрытие только InputStream без явного управления ресурсами в старом коде (утечка при исключениях).

InputStream in = s.getInputStream();
try {
  // чтение
} finally {
  in.close(); // если сокет открыт иначе возможны ошибки
}
// надежнее: try-with-resources при создании сокета и потока
Риск оставить сокет открытым или удвоенно закрыть ресурс. Лучше использовать try-with-resources.

Ошибка 4. Попытка прочитать после shutdownOutput/закрытия сокета.

s.shutdownOutput();
int x = s.getInputStream().read(); // поведение зависит от удаленной стороны
// возможно EOF или IOException
Если удаленная сторона закрыла соединение, read вернет -1; если локальный сокет закрыт - IOException.

Изменения и примечания по версиям

Сигнатура Socket.getInputStream() остается стабильной с ранних версий Java. Основные улучшения в экосистеме связаны не с самим методом, а с сопутствующими API:

  • Добавлены удобные методы в InputStream начиная с Java 9: readNBytes, transferTo, readAllBytes, которые упрощают обработку потоков, возвращаемых getInputStream().
  • NIO и AsynchronousSocketChannel предоставляют альтернативы для неблокирующего и асинхронного ввода-вывода.
  • В Java 9+ с модульной системой зависимости модулей могут влиять на доступность некоторых классов в ограниченных окружениях, но базовый метод остается доступным в стандартных модулях.

Расширенные и нестандартные примеры

1) Использование readNBytes (Java 9+) для точного чтения фиксированного размера сообщения:

Пример java
try (Socket s = new Socket("host", 7000);
     InputStream in = s.getInputStream()) {
  byte[] msg = in.readNBytes(1024); // вернет ровно 1024 или меньше при EOF
  System.out.println("прочитано: " + msg.length);
}
прочитано: 1024

2) Пример протокола с длиной в начале (length-prefix) и обработкой частичных чтений:

Пример java
// Чтение сообщения: сначала 4 байта длины (big-endian), затем тело
try (Socket s = new Socket("host", 8000);
     InputStream in = s.getInputStream()) {
  byte[] lenBuf = in.readNBytes(4);
  if (lenBuf.length < 4) throw new EOFException();
  int len = ((lenBuf[0] & 0xFF) << 24) | ((lenBuf[1] & 0xFF) << 16)
          | ((lenBuf[2] & 0xFF) << 8) | (lenBuf[3] & 0xFF);
  byte[] body = in.readNBytes(len);
  // обработка body
}
Если удаленная сторона отправила корректный пакет, body.length == len.

3) Комбинация с SSL: чтение зашифрованных данных через SSLSocket:

Пример java
SSLSocketFactory f = (SSLSocketFactory) SSLSocketFactory.getDefault();
try (SSLSocket ss = (SSLSocket) f.createSocket("secure.example", 443);
     InputStream in = ss.getInputStream()) {
  // чтение как обычно, TLS-расшифровка внутри SSLSocket
  byte[] buf = new byte[4096];
  int r = in.read(buf);
}
Данные приходят уже в расшифрованном виде.

4) Half-close: явный shutdownOutput на стороне клиента и продолжение чтения ответа сервера:

Пример java
try (Socket s = new Socket("host", 8080)) {
  OutputStream out = s.getOutputStream();
  out.write(requestBytes);
  s.shutdownOutput(); // сигнал энд-клиента
  InputStream in = s.getInputStream();
  // читать ответ сервера, пока не EOF
}
Сервер получает EOF на входе и может отправить ответ; клиент читает до -1.

5) Использование InputStream.transferTo для простой переадресации в файл (Java 9+):

Пример java
try (Socket s = new Socket("host", 9001);
     InputStream in = s.getInputStream();
     FileOutputStream fos = new FileOutputStream("out.bin")) {
  in.transferTo(fos);
}
Весь поток записи сокета сохраняется в файле до EOF.

6) Комбинация NIO и потоков: когда нужен неблокирующий режим, использовать SocketChannel и ByteBuffer, а не getInputStream(). Пример: мультиплексирование через Selector для множества соединений.

джава Socket.getInputStream function comments

En
Socket.getInputStream Returns an input stream for this socket