← Назад к вопросам

Что такое Handshake?

2.3 Middle🔥 131 комментариев
#Безопасность

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Handshake (Рукопожатие)

Handshake — это процесс установления соединения между двумя сторонами перед обменом данными. В сетевом программировании это критически важный концепт, особенно при работе с HTTP, HTTPS, и WebSocket. Как Java разработчик, понимание различных типов handshake необходимо для написания надежного кода.

1. TCP Handshake (Three-Way Handshake)

Это процесс установления TCP соединения перед любым обменом данными:

Клиент                          Сервер
  |                               |
  |------- SYN (seq=100) --------->|
  |                               |
  |<------ SYN-ACK (seq=300, ack=101) ------|
  |                               |
  |------- ACK (seq=101, ack=301) --------->|
  |                               |
  |<----- Соединение установлено ----->
  |

Шаги:

  1. SYN - Клиент отправляет пакет с флагом SYN и начальным номером последовательности
  2. SYN-ACK - Сервер отправляет пакет с флагом SYN и своим номером последовательности, подтверждая получение
  3. ACK - Клиент подтверждает получение пакета сервера

После этого соединение считается установленным.

2. TLS/SSL Handshake

Для HTTPS соединения (HTTP over TLS) нужен дополнительный handshake:

// Пример в Java - SSL handshake происходит автоматически
URL url = new URL("https://api.example.com/data");
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
// На этом этапе происходит TLS handshake
connection.getInputStream(); // Только после этого данные безопасны

Процесс TLS Handshake:

Клиент                                    Сервер
  |                                         |
  |--- ClientHello (поддерживаемые версии TLS, cipher suites) --->
  |                                         |
  |<-- ServerHello (выбранная версия, выбранный cipher) ----|
  |<-- Certificate (публичный сертификат сервера) ----|
  |<-- ServerKeyExchange (если нужно) ----|
  |<-- ServerHelloDone ----|
  |                                         |
  |--- ClientKeyExchange (зашифрованный ключ) --->
  |--- ChangeCipherSpec --->
  |--- Finished (handshake verification) --->
  |                                         |
  |<-- ChangeCipherSpec ----|
  |<-- Finished ----|
  |                                         |
  |<--- Защищенное соединение установлено --->
  |

3. HTTP Handshake и Keep-Alive

Когда используется HTTP 1.1 с Keep-Alive, handshake происходит один раз на несколько запросов:

// Без Keep-Alive - новое соединение для каждого запроса
for (int i = 0; i < 100; i++) {
    // Для каждого запроса: TCP handshake -> TLS handshake -> HTTP запрос
    makeHttpRequest("https://api.example.com/data");
    // После ответа соединение закрывается
}
// 100 handshakes!

// С Keep-Alive - одно соединение для нескольких запросов
HttpClient client = HttpClient.newBuilder()
    .version(HttpClient.Version.HTTP_1_1)
    .build();

for (int i = 0; i < 100; i++) {
    // Handshake только один раз на первый запрос
    var request = HttpRequest.newBuilder()
        .uri(URI.create("https://api.example.com/data"))
        .build();
    client.send(request, HttpResponse.BodyHandlers.ofString());
}
// 1 handshake!

4. WebSocket Handshake

WebSocket использует HTTP upgrade handshake для переключения на WebSocket протокол:

// Пример с библиотекой Tyrus
import javax.websocket.ClientEndpoint;
import javax.websocket.ContainerProvider;
import javax.websocket.WebSocketContainer;

@ClientEndpoint
public class WebSocketClient {
    @OnMessage
    public void onMessage(String message) {
        System.out.println("Received: " + message);
    }
}

// Клиент инициирует WebSocket handshake
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
Session session = container.connectToServer(
    WebSocketClient.class,
    URI.create("ws://localhost:8080/ws")
);
// После успешного handshake можно отправлять сообщения
session.getBasicRemote().sendText("Hello");

Запрос WebSocket Handshake:

GET /ws HTTP/1.1
Host: localhost:8080
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Version: 13

Ответ сервера:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=

После этого соединение переходит в режим WebSocket.

5. SSH Handshake (для Git, SSH tunnels)

Для работы с удаленными серверами по SSH:

// Пример: деплой через SSH
SSHClient ssh = new SSHClient();
ssh.addHostKeyVerifier(new PromiscuousVerifier()); // В продакшене используй правильную верификацию!

// SSH handshake и аутентификация
ssh.connect("production.example.com", 22);
ssh.authPassword("user", "password"); // или используй публичный ключ

// После handshake можно выполнять команды
Command cmd = ssh.exec("java -jar app.jar");

Handshake в контексте микросервисов

При работе с множеством микросервисов, handshake может быть узким местом:

// Проблема: N микросервисов = N handshakes
@Service
public class OrderService {
    public void createOrder(Order order) {
        // 1. TCP handshake с PaymentService
        // 2. TLS handshake
        // 3. HTTP запрос
        paymentClient.charge(order.getAmount());
        
        // 1. TCP handshake с NotificationService
        // 2. TLS handshake
        // 3. HTTP запрос
        notificationClient.sendEmail(order.getCustomer());
        
        // 1. TCP handshake с LogService
        // 2. TLS handshake
        // 3. HTTP запрос
        logClient.log("Order created: " + order.getId());
    }
}

// Решение: используй connection pooling и keep-alive
@Configuration
public class HttpClientConfig {
    @Bean
    public HttpClient httpClient() {
        return HttpClient.newBuilder()
            .version(HttpClient.Version.HTTP_2) // HTTP/2 лучше для множественных запросов
            .connectTimeout(Duration.ofSeconds(5))
            .build();
    }
}

Проблемы с Handshake

1. Slow Start проблема

Время для HTTP запроса:
- TCP handshake: ~50ms
- TLS handshake: ~100ms
- Отправка запроса: ~10ms
- Получение ответа: ~100ms

Всего: ~260ms

Если нужно 1000 запросов без keep-alive: 260 сек!
С keep-alive и HTTP/2: ~5-10 сек

2. DNS resolution

// Помимо handshake, есть еще DNS lookup!
// Решение: кешируй DNS результаты
SecurityManager securityManager = new SecurityManager() {
    @Override
    public InetAddress getHostByName(String host) throws UnknownHostException {
        // Используй кеш
        return dnsCache.getOrFetch(host);
    }
};

Best Practices

  1. Используй Connection Pooling
HttpClient client = HttpClient.newBuilder()
    .version(HttpClient.Version.HTTP_2)
    .build();
// HttpClient автоматически переиспользует соединения
  1. Preferuj HTTP/2
// HTTP/2 мультиплексирует несколько запросов в одном соединении
// Handshake только один раз!
HttpClient client = HttpClient.newBuilder()
    .version(HttpClient.Version.HTTP_2)
    .build();
  1. Timeouts для Handshake
HttpClient client = HttpClient.newBuilder()
    .connectTimeout(Duration.ofSeconds(5))
    .build();
  1. Мониторь время handshake
long start = System.nanoTime();
Session session = connectToServer(...);
long handshakeDuration = System.nanoTime() - start;
if (handshakeDuration > 1_000_000_000) { // > 1 сек
    logger.warn("Slow handshake: " + handshakeDuration);
}

Заключение

Handshake — это критический процесс, от которого зависит производительность сетевых операций. Правильное понимание различных типов handshake (TCP, TLS, WebSocket) и использование современных подходов (HTTP/2, connection pooling, keep-alive) является ключом к написанию быстрого и надежного сетевого кода на Java.

Что такое Handshake? | PrepBro