Введение в сетевое программирование Java
Обзор сетевых API Java в java.net и java.net.http для подключения к удалённым сервисам.
Введение в сетевое программирование Java
Сетевое программирование — это то, как программа Java общается с другой программой — через комнату или через весь мир. JDK поставляет для этого полный стек «всё включено» в двух пакетах: давний java.net (URL-адреса, сокеты, адреса) и современный java.net.http (HttpClient, введённый в Java 11). Эта часть книги проходит по этому стеку от высокоуровневых удобств вплоть до сырых сокетов.
Слои, от высокого к низкому
Сетевые API Java образуют лестницу. Выбирайте самую высокую ступень, которая справляется с задачей:
| Ступень | API | Использовать для |
|---|---|---|
| Самая высокая | HttpClient (java.net.http) | Современный HTTP/HTTPS: вызовы REST, загрузки, асинхронность |
URL / URLConnection / HttpURLConnection | Устаревший HTTP и другие протоколы URL | |
Socket / ServerSocket | Собственные протоколы TCP, ваш собственный клиент/сервер | |
| Самая низкая | DatagramSocket | Обмен сообщениями UDP без установления соединения |
| Поддержка | InetAddress | Разрешение и представление IP-адресов |
Эмпирическое правило: если вы вызываете HTTP-API, тянитесь к HttpClient. Спускайтесь к сокетам только тогда, когда говорите на протоколе, отличном от HTTP, или строите собственный сервер.
TCP против UDP
Два транспортных протокола лежат в основе почти всего:
- TCP (
Socket,ServerSocket) — это соединение: надёжное, упорядоченное, потоковое. Байты, которые вы записываете, прибывают целыми и по порядку, либо вы получаете ошибку. HTTP, базы данных и большинство прикладных протоколов едут на TCP. - UDP (
DatagramSocket) — без установления соединения: вы выстреливаете независимыми пакетами («датаграммами») без рукопожатия, без упорядочивания и без гарантии доставки. Он обменивает надёжность на низкую задержку — используется для DNS, видеотрансляций и игр.
Клиент против сервера
Клиент инициирует соединение с известным адресом и портом. Сервер привязывается к порту и ждёт, чтобы принять входящие соединения. Один и тот же класс Socket представляет активное соединение на обоих концах; асимметрия лишь в том, кто начинает разговор. Последующие главы строят обе стороны.
По умолчанию блокирующие
Классические API java.net являются блокирующими: socket.getInputStream().read() паркует вызывающий поток, пока не прибудут данные, а serverSocket.accept() паркует, пока не подключится клиент. Об этом просто рассуждать, но это означает один поток на соединение. (Каналы java.nio и виртуальные потоки решают вопрос масштабирования; эта часть остаётся на блокирующих API, которые являются правильной отправной точкой.)
Проработанный пример: весь стек в одном файле
Чтобы сделать части конкретными, эта программа запускает крошечный HTTP-сервер на интерфейсе loopback, затем вызывает его современным HttpClient — полный кругооборот клиент/сервер в одной JVM, без необходимости во внешней сети.
Что вынести из запуска:
- Работающие клиент и сервер умещаются в одном коротком файле. Реальные приложения разносят их по машинам, но API идентичен —
HttpServerпринял соединение, аHttpClientоткрыл его, встретившись по TCP на интерфейсе loopback (127.0.0.1). Такова форма каждой сетевой программы: одна сторона ждёт, другая вызывает. - Привязка к порту 0 позволила ОС выбрать свободный порт, который
server.getAddress().getPort()сообщил обратно. Жёсткое кодирование порта рискует «address already in use»; порт 0 — стандартный приём для тестов и демонстраций, которым нужен просто какой-то порт. - Клиент никогда не касался сокета напрямую.
HttpClientобработал соединение, строку HTTP-запроса, заголовки и разбор ответа — в этом ценность подъёма на самую высокую ступень лестницы. Главы о сокетах далее покажут, что он скрыл. - Ответ нёс структурированные метаданные:
statusCode()(200),body()иversion(). HTTP-ответы — это больше, чем текст; клиент смоделировал их как типизированный объектHttpResponse, так что вы читаете поля, а не разбираете поток. server.stop(0)освободил порт. Сетевые ресурсы — сокеты, серверные порты, соединения — это скудные дескрипторы ОС; каждая глава в этой части закрывает их явно (или с помощью try-with-resources), чтобы они не утекали.
Что охватывает остаток этой части
URL и его классы соединений, современный HttpClient подробно, сырой TCP с Socket и ServerSocket, UDP без установления соединения с DatagramSocket и разрешение адресов с InetAddress. Следующая глава начинается с самого знакомого сетевого объекта из всех: URL.
Практика
Вы пишете сервис Java, который вызывает стороннее REST-API через HTTPS и нуждается как в синхронных, так и в асинхронных запросах на современном JDK. Какой API является наиболее подходящим первым выбором?