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

Обзор возможностей класса java.net.Socket
Раздел: Сокеты (сетевые)
Socket(String host, int port)

Общее описание класса Socket

Класс java.net.Socket представляет собой TCP-клиентский сокет, используемый для установления соединения с удалённым хостом по протоколу TCP. Применяется при реализации клиентской части сетевых приложений, обмене потоковыми данными и создании протоколов поверх TCP.

Конструкторами и методами задаются адреса, параметры соединения и поведение сокета. Класс обычно работает вместе с ServerSocket для серверной части и с потоками InputStream/OutputStream для ввода-вывода.

Параметры, конструкторы и возвращаемые значения

  • Конструкторы:
    • Socket() - создаёт не подключённый сокет (позволяет затем вызвать connect или bind).
    • Socket(String host, int port) - устанавливает соединение с указанным хостом и портом; выбрасывает UnknownHostException или IOException при ошибках.
    • Socket(InetAddress address, int port) - аналогично, по InetAddress.
    • Socket(String host, int port, InetAddress localAddr, int localPort) - устанавливает соединение и привязывает к локальному адресу и порту.
    • Socket(Proxy proxy) - создаёт сокет с указанным прокси (например, HTTP или SOCKS).
  • Ключевые методы:
    • connect(SocketAddress endpoint) и connect(SocketAddress endpoint, int timeout) - подключение; возвращает void, при превышении таймаута выбрасывает SocketTimeoutException, при других ошибках - IOException.
    • bind(SocketAddress bindpoint) - привязка к локальному адресу; возвращает void, может вызвать IOException или BindException.
    • close() - закрытие сокета и связанных потоков; возвращает void, многократно вызывать безопасно.
    • getInputStream() - возвращает InputStream для чтения; при закрытом сокете бросает IOException.
    • getOutputStream() - возвращает OutputStream для записи.
    • isConnected(), isClosed() - булевы состояния соединения.
    • Параметры сокета (устанавливаются и читаются):
      • setSoTimeout(int timeout) / getSoTimeout() - таймаут чтения в миллисекундах (0 - ожидание бесконечно).
      • setTcpNoDelay(boolean) / getTcpNoDelay() - включение/выключение Nagle.
      • setKeepAlive(boolean) - включение периодического keep-alive.
      • setSoLinger(boolean on, int linger) - поведение при закрытии при наличии данных в буфере.
      • setReceiveBufferSize(int), setSendBufferSize(int) - размер буферов сокета.
  • Возвращаемые значения и исключения:
    • Большинство сетевых операций возвращают void или потоковые объекты; при ошибках выбрасываются исключения семейства IOException (включая UnknownHostException, SocketTimeoutException, BindException, ConnectException).
    • Методы состояния возвращают примитивы: boolean или int.

Класс является Closeable, что позволяет использовать try-with-resources для автоматического закрытия.

Простые варианты использования

Ниже представлены компактные примеры основных сценариев: подключение к серверу, чтение и запись, обработка таймаута и привязка к локальному адресу.

1) Клиент: простой запрос и ответ

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

public class SimpleClient {
    public static void main(String[] args) throws Exception {
        try (Socket socket = new Socket("example.com", 80)) {
            OutputStream out = socket.getOutputStream();
            InputStream in = socket.getInputStream();

            out.write("GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n".getBytes());
            out.flush();

            BufferedReader reader = new BufferedReader(new InputStreamReader(in));
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }
        }
    }
}
HTTP/1.1 200 OK
... (заголовки и HTML-страница)

2) Таймаут при подключении

Socket socket = new Socket();
try {
    socket.connect(new InetSocketAddress("10.255.255.1", 80), 2000); // 2 секунды
} catch (SocketTimeoutException e) {
    System.out.println("connect timed out");
} finally {
    socket.close();
}
connect timed out

3) Привязка к локальному адресу

Socket s = new Socket();
s.bind(new InetSocketAddress("192.168.1.100", 0)); // выбрать локальный порт автоматически
s.connect(new InetSocketAddress("remote.host", 8080));
// использование потоков...
s.close();
(нет вывода; подключение к удаленному хосту с указанного локального адреса)

Сопоставимые классы в Java

Внутри Java имеются классы, служащие для схожих задач, с отличиями по модели и возможностям:

  • DatagramSocket - для UDP: без установления соединения, без гарантий доставки, применяется для простых, быстрых и некритичных по порядку сообщений.
  • SocketChannel (NIO) - неблокирующий ввод-вывод, лучше подходит для большого числа одновременных соединений и асинхронных серверов.
  • SSLSocket - надстройка над Socket для TLS/SSL: требуется для защищённых соединений.
  • HttpClient (java.net.http) - более высокий уровень для HTTP, скрывает детали сокетов и обеспечивает асинхронность и удобство.

Выбор зависит от требований: для простых TCP-соединений - Socket; для высокой масштабируемости - SocketChannel; для шифрования - SSLSocket или TLS-конфигурации; для HTTP - HttpClient.

Альтернативы в других языках

Схожая функциональность доступна во многих языках. Ниже примеры и основные отличия от Java:

Python

import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("example.com", 80))
s.sendall(b"GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n")
print(s.recv(1024))
s.close()
b'HTTP/1.1 200 OK\r\n...'

Отличие: более компактный синтаксис, прямой доступ к байтовым операциям, есть синхронный и асинхронный API (asyncio).

Node.js (JavaScript)

const net = require('net');
const client = net.createConnection({ host: 'example.com', port: 80 }, () => {
  client.write('GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n');
});
client.on('data', (data) => { console.log(data.toString()); });
client.on('end', () => {});
HTTP/1.1 200 OK
...

Отличие: событийная модель, неблокирующий ввод-вывод по умолчанию.

PHP

$fp = fsockopen('example.com', 80, $errno, $errstr, 5);
fwrite($fp, "GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n");
echo fgets($fp);
fclose($fp);
HTTP/1.1 200 OK
...

Отличие: часто используется в контексте веб-приложений, удобные обёртки для потоков.

C#

using System.Net.Sockets;
var client = new TcpClient("example.com", 80);
var stream = client.GetStream();
// запись/чтение
client.Close();
(похожие результаты HTTP)

Отличие: .NET предлагает TcpClient/TcpListener и асинхронные методы Task-based.

Go

package main
import (
  "fmt"
  "net"
)
func main() {
  conn, _ := net.Dial("tcp", "example.com:80")
  fmt.Fprintf(conn, "GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n")
  buf := make([]byte, 1024)
  n, _ := conn.Read(buf)
  fmt.Println(string(buf[:n]))
}
HTTP/1.1 200 OK
...

Отличие: встроенная простота сетевого API, лёгкая работа с горутинами для параллелизма.

Kotlin

В Kotlin используется тот же Java API, код компактнее из-за языка, при необходимости применяются корутины для асинхронности.

Lua (LuaSocket)

local socket = require('socket')
local tcp = assert(socket.tcp())
tcp:connect('example.com', 80)
tcp:send('GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n')
print(tcp:receive('*l'))
tcp:close()
HTTP/1.1 200 OK

Итог: синтаксис и модель выполнения различаются, но концепция сокета как абстракции TCP-потока сохраняется; Java выделяется строгой типизацией и интеграцией с JVM-моделями (потоки, NIO, SSL).

Типичные ошибки при работе с Socket

  • Не закрытие сокета и утечки ресурсов: отсутствие close() приводит к исчерпанию дескрипторов.
  • Блокировки при чтении без таймаута: вызов read() может блокировать поток навсегда при отсутствии данных.
  • Неправильная обработка потоков: использование getInputStream()/getOutputStream() после close вызывает IOException.
  • Игнорирование исключений при connect: UnknownHostException и ConnectException требуют обработки для информативности о проблеме.
  • Использование порта, уже занятого другим процессом: BindException.
  • Неправильное чтение протокола: чтение фиксированного числа байт вместо обработки окончания потока или заголовков приводит к разрывам протокола.

Пример: блокировка без таймаута

try (Socket s = new Socket("10.255.255.1", 12345)) {
    InputStream in = s.getInputStream();
    int b = in.read(); // может заблокировать надолго
    System.out.println(b);
} catch (IOException e) {
    e.printStackTrace();
}
(программа зависает или длительно ждёт ответа)

Пример: ошибка привязки к занятому порту

ServerSocket srv = new ServerSocket(8080);
// ... в другом месте попытка создать ещё один ServerSocket на 8080
ServerSocket srv2 = new ServerSocket(8080);
java.net.BindException: Address already in use: bind

Рекомендация по диагностике: логирование исключений, установка таймаутов через setSoTimeout и connect(..., timeout), использование try-with-resources и профилирование дескрипторов.

Изменения и эволюция API

API сокетов в Java сохраняет стабильность с ранних версий. Основные изменения и смежные улучшения в экосистеме:

  • Переход к модульной системе в Java 9 затронул доступность пакетов, но функциональность java.net.Socket осталась доступной в стандартной библиотеке.
  • Развитие NIO и NIO.2 (SocketChannel, AsynchronousSocketChannel) добавило неблокирующие и асинхронные модели, рекомендованные для высоконагруженных приложений.
  • Интеграция с TLS через SSLSocket/SSLEngine и улучшенные средства управления сертификатами в современных версиях JDK.
  • Мелкие улучшения и исправления стабильности в реализации платформы, серьезных изменений в сигнатурах методов не происходило.

В результате для новой разработки часто рассматриваются NIO/ASYNC вариации и более высокоуровневые клиентские библиотеки (например, java.net.http для HTTP), в то время как классический Socket остаётся рабочим выбором для простых TCP-сценариев.

Расширенные и нетривиальные примеры

Несколько продвинутых сценариев: многопоточный echo-сервер и клиент, использование SSLSocket, SocketChannel (NIO) и подключение через SOCKS-прокси.

1) Многопоточный эхо-сервер и клиент

Пример java
// EchoServer.java
import java.io.*;
import java.net.*;

public class EchoServer {
    public static void main(String[] args) throws Exception {
        try (ServerSocket server = new ServerSocket(9000)) {
            while (true) {
                Socket client = server.accept();
                new Thread(() -> {
                    try (Socket s = client;
                         BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));
                         PrintWriter out = new PrintWriter(s.getOutputStream(), true)) {
                        String line;
                        while ((line = in.readLine()) != null) {
                            out.println("Echo: " + line);
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }).start();
            }
        }
    }
}

// EchoClient.java
import java.net.*;
import java.io.*;

public class EchoClient {
    public static void main(String[] args) throws Exception {
        try (Socket s = new Socket("localhost", 9000);
             BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));
             PrintWriter out = new PrintWriter(s.getOutputStream(), true)) {
            out.println("Hello");
            System.out.println(in.readLine());
        }
    }
}
(сервер запускается, клиент выводит)
Echo: Hello

2) SSL-соединение (клиент)

Пример java
import javax.net.ssl.*;
import java.io.*;

public class SSLClient {
    public static void main(String[] args) throws Exception {
        SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault();
        try (SSLSocket socket = (SSLSocket) factory.createSocket("www.google.com", 443)) {
            socket.startHandshake();
            PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
            BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            out.println("GET / HTTP/1.1\r\nHost: www.google.com\r\nConnection: close\r\n\r\n");
            String line;
            while ((line = in.readLine()) != null) System.out.println(line);
        }
    }
}
HTTP/1.1 200 OK
... (зашифрованный ответ, расшифрованный SSLSocket)

3) Неблокирующий клиент через SocketChannel

Пример java
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;

public class NioClient {
    public static void main(String[] args) throws Exception {
        SocketChannel sc = SocketChannel.open();
        sc.configureBlocking(false);
        sc.connect(new InetSocketAddress("example.com", 80));
        while (!sc.finishConnect()) {
            // можно выполнять другие задачи
        }
        ByteBuffer buf = ByteBuffer.wrap("GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n".getBytes());
        sc.write(buf);
        ByteBuffer resp = ByteBuffer.allocate(1024);
        int read = sc.read(resp);
        if (read > 0) {
            resp.flip();
            byte[] b = new byte[resp.remaining()];
            resp.get(b);
            System.out.println(new String(b));
        }
        sc.close();
    }
}
HTTP/1.1 200 OK
...

4) Подключение через SOCKS-прокси

Пример java
SocketAddress addr = new InetSocketAddress("socks-proxy.local", 1080);
Proxy proxy = new Proxy(Proxy.Type.SOCKS, addr);
try (Socket s = new Socket(proxy)) {
    s.connect(new InetSocketAddress("example.com", 80));
    // обмен данными через прокси
}
(соединение устанавливается через указанный SOCKS-прокси)

5) Передача файла с индикатором прогресса

Пример java
import java.io.*;
import java.net.*;

// Отправитель
try (Socket s = new Socket("server", 9001);
     OutputStream out = s.getOutputStream();
     FileInputStream fis = new FileInputStream("large.bin")) {
    byte[] buf = new byte[8192];
    long total = 0;
    int r;
    while ((r = fis.read(buf)) != -1) {
        out.write(buf, 0, r);
        total += r;
        System.out.println("Sent: " + total);
    }
}
Sent: 8192
Sent: 16384
... (прогресс передачи)

Пояснения: в примерах показаны разные уровни абстракции - от обычных блокирующих потоков до асинхронных каналов и SSL. Для производительных серверов предпочтительнее NIO/AsynchronousChannel; для защищённых соединений - SSLSocket/SSLEngine.

джава Socket function comments

En
Socket A socket for connecting to a server