TCP-соединение зависает на SYN_SENT

Рассмотрим следующие клиентские и серверные компоненты:

import java.io.InputStream;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;

public class client {
    public static void main(String[] args) throws IOException {
        while (true) {
            URL url = new URL("http://localhost:8000");
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("GET");
            int statusCode = connection.getResponseCode();
            System.out.println("Status Code: " + statusCode);
            connection.disconnect();
        }
    }
}
import java.io.OutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class server {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(8000);
        while (true) {
            Socket clientSocket = serverSocket.accept();
            OutputStream outputStream = clientSocket.getOutputStream();
            outputStream.write("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n".getBytes());
            outputStream.flush();
            clientSocket.close();
        }
    }
}

Запустив клиент во время работы сервера, вы вскоре увидите, что клиент начинает зависать SYN_SENT на уровне TCP (всего около 30 секунд):

$ watch -n 0.1 "ss -on state syn-sent '( dport = :8000 )'"
Every 0.1s: ss -on state syn-sent '( dport = :8000 )'               myhost: Tue Jul 16 04:08:52 2024

Netid Recv-Q Send-Q      Local Address:Port        Peer Address:Port           Process
tcp   0      1           [::ffff:127.0.0.1]:60418  [::ffff:127.0.0.1]:8000     timer:(on,3.731ms,2)
$ pkill -3 java
# Stack trace of client's main thread while hanging outputted in Java terminal...
"main" #1 prio=5 os_prio=0 cpu=2429.68ms elapsed=40.96s tid=0x000079e6c40266c0 nid=0x18a1c6 runnable  [0x000079e6cb9fd000]
   java.lang.Thread.State: RUNNABLE
    at sun.nio.ch.Net.connect0([email protected]/Native Method)
    at sun.nio.ch.Net.connect([email protected]/Net.java:579)
    at sun.nio.ch.Net.connect([email protected]/Net.java:568)
    at sun.nio.ch.NioSocketImpl.connect([email protected]/NioSocketImpl.java:593)
    at java.net.Socket.connect([email protected]/Socket.java:633)
    at java.net.Socket.connect([email protected]/Socket.java:583)
    at sun.net.NetworkClient.doConnect([email protected]/NetworkClient.java:183)
    at sun.net.www.http.HttpClient.openServer([email protected]/HttpClient.java:533)
    at sun.net.www.http.HttpClient.openServer([email protected]/HttpClient.java:638)
    at sun.net.www.http.HttpClient.<init>([email protected]/HttpClient.java:281)
    at sun.net.www.http.HttpClient.New([email protected]/HttpClient.java:386)
    at sun.net.www.http.HttpClient.New([email protected]/HttpClient.java:422)
    at sun.net.www.protocol.http.HttpURLConnection.setNewClient([email protected]/HttpURLConnection.java:831)
    at sun.net.www.protocol.http.HttpURLConnection.setNewClient([email protected]/HttpURLConnection.java:819)
    at sun.net.www.protocol.http.HttpURLConnection.writeRequests([email protected]/HttpURLConnection.java:759)
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream0([email protected]/HttpURLConnection.java:1708)
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream([email protected]/HttpURLConnection.java:1611)
    at java.net.HttpURLConnection.getResponseCode([email protected]/HttpURLConnection.java:529)
    at client.main(client.java:13)
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0([email protected]/Native Method)
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke([email protected]/NativeMethodAccessorImpl.java:77)
    at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke([email protected]/DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke([email protected]/Method.java:568)
    at com.sun.tools.javac.launcher.Main.execute([email protected]/Main.java:419)
    at com.sun.tools.javac.launcher.Main.run([email protected]/Main.java:192)
    at com.sun.tools.javac.launcher.Main.main([email protected]/Main.java:132)

Я создаю Java-приложение (когда я столкнулся с этой проблемой, я пытался использовать интервал опроса 10 мс - мне показалось, что 30 мс работает), где мне нужно быстро отправлять HTTP-запросы, подобные этому, в течение определенного периода времени (я не могу использовать веб-сокеты). Итак, мой вопрос: почему происходит это зависание и как это исправить?

На данный момент моей лучшей попыткой исправить это было увеличение количества доступных дескрипторов файлов с обеих сторон (uname -n unlimitied), но безрезультатно.

Сейчас тестирую больше... Я также могу воспроизвести то же самое с Python:

import requests

while True:
    response = requests.get("http://localhost:8000")
    print(f"Status Code: {response.status_code}")

Тогда python -m http.server на сервер и вы SYN_SENT повесите. Итак, похоже, что проблема может быть глубже, чем я изначально предполагал, но мне любопытно и я готов услышать любое потенциальное решение.

Я хочу, чтобы эта SYN_SENT проблема с зависанием не возникала. Я хочу иметь возможность настроить интервал опроса HTTP-запросов так, чтобы он был очень низким (даже 1 мс между запросами в локальной сети; пока нет утечки ресурсов и все запросы выполняются последовательно, я не понимаю, почему это не так. Это достижимо), при этом мое Java или другое приложение работает совершенно надежно. Однако я также хочу понять проблему. Вид из Wireshark меня сбивает с толку, поскольку он показывает, что сервер вернул ответ HTTP 200 OK, но по какой-то причине клиент зависает во время его чтения (я пытался включить все необходимое, чтобы воспроизвести то, что я вижу, поскольку я занимался отладкой это часами). Спасибо за ваше время.

Почему -1? И почему голосование закрыто из-за «необходимости деталей отладки», которые, как мне кажется, я предоставил? Очевидно, это непростая проблема с синтаксическим сахаром, но это определенно проблема, которая меня интересует. Однако я обновляю тег Java для сети, поскольку эта проблема оказывается более общей. Спасибо.

Elliot Killick 16.07.2024 14:01

Я обновил вопрос, чтобы указать свое «желаемое поведение», как описано в разделе «Необходимы сведения об отладке».

Elliot Killick 16.07.2024 15:16

похоже, что что-то на компьютере, либо брандмауэр, либо антивирус/антивредоносное ПО или тому подобное, блокирует завершение соединения. Возникает ли у вас такая же проблема, если вы используете стандартный HTTP-порт 80 вместо 8000? А что, если клиент и сервер работают на разных компьютерах в одной локальной сети, а не на одном компьютере?

Remy Lebeau 16.07.2024 21:40
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
3
53
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я рад сообщить, что нашел причину этой проблемы! Я заметил, что всякий раз, когда у меня возникали эти SYN_SENT зависания (как показано ss), в моем dmesg также регистрировалось следующее:

nf_conntrack: nf_conntrack: table full, dropping packet

Таблица отслеживания соединений заполнялась! Каждый запрос HTTP 1.1, который я делал, выполнялся в собственном потоке TCP. Теперь все это имеет смысл.

Я использую довольно уникальный дистрибутив Linux, поэтому не уверен, что моя таблица отслеживания соединений меньше, чем в среднем. В моем приложении также было несколько проблем с утечкой сокетов/ресурсов, которые усугубляли проблему даже между отдельными программами в моей системе. Я еще не рассматривал возможность увеличения размера этой таблицы. Если вы столкнулись с этой проблемой, помните, что проблема также может заключаться в небольшой таблице подключений другого устройства в сети, например маршрутизатора, коммутатора или брандмауэра.

Я оставил свое приложение работающим на ночь с очень коротким интервалом опроса, чтобы посмотреть, что произойдет, и когда я проснулся, компилятор Java давал сбой, потому что systemd, кажется, заполнил /tmp этими nf_conntrack журналами. Вот тогда я подумал проверить dmesg, и вуаля! Затем я перезагрузился, чтобы очиститься tmpfs.

Другие вопросы по теме