Tcp socket ошибка

I have a TCP socket based client server system.
Everything works fine but when network is disconnected form client end and reconnect it again i get automatically SocketError.ConnectionReset send form client and regarding this command the socket is closed in the server side. this is also fine.

but when i look in to the client side it shows the socket is still connected with server. (regarding socket is still connected with server [It does not happen every time], sometime it shows disconnected and some times shows connected)

Does it make sense that «server get a SocketError.ConnectionReset from
client end but client is still connected»?

So i want to know what is the possible reasons of SocketError.ConnectionReset and how to handle such type of problem i have mentioned?

Again i say, Everything is working fine in normal environment (e.g if i exit the client it is disconnected the socket same for the server)

Thanks in advance.

EDIT:

Here is the code in the client side. actually it’s a timer that tick every 3 second through programs lifetime and check if Socket is connected or not if its disconnected then it tries to reconnect again through a new socket instance

private void timerSocket_Tick(object sender, EventArgs e)
        {
            try
            {
                if (sck == null || !sck.Connected)
                {
                    ConnectToServer();
                }
            }
            catch (Exception ex)
            {
                RPLog.WriteDebugLog("Exception occcured at: "+ System.Reflection.MethodBase.GetCurrentMethod().ToString()+"Message: "+ex.Message);
            }
        }

In normal situation (without network disconnect/reconnect) if TCP server get a
SocketError.ConnectionReset form any client, in the client side i see
clients socket is disconnected and it tries to reconnect it again
through the code shown. but when situation happen explained earlier,
server gets a SocketError.ConnectionReset but client shows it still
connected. though the TCP server shows the reset command is send form the exact client
side.

0 = Success 1 = Operation not permitted 2 = No such file or directory 3 = No such process 4 = Interrupted system call 5 = Input/output error 6 = No such device or address 7 = Argument list too long 8 = Exec format error 9 = Bad file descriptor 10 = No child processes 11 = Resource temporarily unavailable 12 = Cannot allocate memory 13 = Permission denied 14 = Bad address 15 = Block device required 16 = Device or resource busy 17 = File exists 18 = Invalid cross-device link 19 = No such device 20 = Not a directory 21 = Is a directory 22 = Invalid argument 23 = Too many open files in system 24 = Too many open files 25 = Inappropriate ioctl for device 26 = Text file busy 27 = File too large 28 = No space left on device 29 = Illegal seek 30 = Read-only file system 31 = Too many links 32 = Broken pipe 33 = Numerical argument out of domain 34 = Numerical result out of range 35 = Resource deadlock avoided 36 = File name too long 37 = No locks available 38 = Function not implemented 39 = Directory not empty 40 = Too many levels of symbolic links 41 = Unknown error 41 42 = No message of desired type 43 = Identifier removed 44 = Channel number out of range 45 = Level 2 not synchronized 46 = Level 3 halted 47 = Level 3 reset 48 = Link number out of range 49 = Protocol driver not attached 50 = No CSI structure available 51 = Level 2 halted 52 = Invalid exchange 53 = Invalid request descriptor 54 = Exchange full 55 = No anode 56 = Invalid request code 57 = Invalid slot 58 = Unknown error 58 59 = Bad font file format 60 = Device not a stream 61 = No data available 62 = Timer expired 63 = Out of streams resources 64 = Machine is not on the network 65 = Package not installed 66 = Object is remote 67 = Link has been severed 68 = Advertise error 69 = Srmount error 70 = Communication error on send 71 = Protocol error 72 = Multihop attempted 73 = RFS specific error 74 = Bad message 75 = Value too large for defined data type 76 = Name not unique on network 77 = File descriptor in bad state 78 = Remote address changed 79 = Can not access a needed shared library 80 = Accessing a corrupted shared library 81 = .lib section in a.out corrupted 82 = Attempting to link in too many shared libraries 83 = Cannot exec a shared library directly 84 = Invalid or incomplete multibyte or wide character 85 = Interrupted system call should be restarted 86 = Streams pipe error 87 = Too many users 88 = Socket operation on non-socket 89 = Destination address required 90 = Message too long 91 = Protocol wrong type for socket 92 = Protocol not available 93 = Protocol not supported 94 = Socket type not supported 95 = Operation not supported 96 = Protocol family not supported 97 = Address family not supported by protocol 98 = Address already in use 99 = Cannot assign requested address 100 = Network is down 101 = Network is unreachable 102 = Network dropped connection on reset 103 = Software caused connection abort 104 = Connection reset by peer 105 = No buffer space available 106 = Transport endpoint is already connected 107 = Transport endpoint is not connected 108 = Cannot send after transport endpoint shutdown 109 = Too many references: cannot splice 110 = Connection timed out 111 = Connection refused 112 = Host is down 113 = No route to host 114 = Operation already in progress 115 = Operation now in progress 116 = Stale NFS file handle 117 = Structure needs cleaning 118 = Not a XENIX named type file 119 = No XENIX semaphores available 120 = Is a named type file 121 = Remote I/O error 122 = Disk quota exceeded 123 = No medium found 124 = Wrong medium type

Ошибка TCP socket является одной из самых распространенных ошибок, с которыми сталкиваются программисты при работе с сетевыми приложениями. Она может возникнуть по многим причинам, таким как некорректное использование сокета, проблемы с сетевым оборудованием, неправильные настройки сервера и клиента, сбои в программном обеспечении и т.д.

Прежде чем рассматривать конкретные причины ошибки TCP socket, необходимо понимать, что такое сокет. Сокет – это программный интерфейс, через который приложения могут обмениваться данными через сеть. Сокеты используются в операционных системах для работы с протоколами передачи данных, такими как TCP и UDP. Сокеты можно использовать для создания различных сетевых приложений, таких как веб-серверы, почтовые клиенты, чаты и т.д.

Теперь перейдем к причинам ошибки TCP socket. Ошибка может возникнуть, если сокет был закрыт или удален до того, как процесс завершил работу с ним. В этом случае процесс попытается выполнить операции с сокетом, который уже не существует, что вызовет ошибку. Чтобы избежать данной ошибки, необходимо закрывать сокет в том порядке, в котором процессы успели завершить работу с ним.

Еще одна причина ошибки TCP socket – это некорректное использование сокета. Например, ошибки могут возникнуть, если процесс пытается использовать сокет до того, как он был инициализирован, или если он пытается выполнить операции на сокете в неверном порядке. Также может возникнуть ошибка, если сокет оказывается заблокирован другим процессом, который не освобождает его для использования другими процессами.

Еще одна причина ошибки TCP socket – это проблемы с сетевым оборудованием. Например, подключенная сеть может быть перегружена, или же сетевое оборудование может не поддерживать необходимый протокол передачи данных. В этом случае может возникнуть ошибка TCP socket.

Также может быть причиной ошибки TCP socket неправильно настроенный сервер или клиент. Например, сервер может быть настроен таким образом, что он не может обрабатывать все запросы, или же клиент может отправлять запросы на несуществующие адреса. Также может быть проблема с DNS-сервером, который не может правильно определить адрес удаленного устройства.

Большинство ошибок TCP socket можно исправить, если перезапустить сетевое оборудование, перезагрузить программное обеспечение, перенастроить сервер или клиент, обновить драйвера сетевых устройств и т.д. Однако некоторые ошибки могут быть вызваны не только программными, но и аппаратными проблемами, что может потребовать замены компонентов сетевой инфраструктуры.

В заключение можно сказать, что ошибка TCP socket – это проблема, с которой сталкиваются многие программисты, работающие с сетевыми приложениями. Общая причина ошибки заключается в некорректном использовании сокета, проблемах с сетевым оборудованием, неправильно настроенном сервере или клиенте и т.д. Ошибку можно исправить, если проанализировать ее причины и выполнить необходимые действия для ее устранения.

I have a TCP socket based client server system.
Everything works fine but when network is disconnected form client end and reconnect it again i get automatically SocketError.ConnectionReset send form client and regarding this command the socket is closed in the server side. this is also fine.

but when i look in to the client side it shows the socket is still connected with server. (regarding socket is still connected with server [It does not happen every time], sometime it shows disconnected and some times shows connected)

Does it make sense that «server get a SocketError.ConnectionReset from
client end but client is still connected»?

So i want to know what is the possible reasons of SocketError.ConnectionReset and how to handle such type of problem i have mentioned?

Again i say, Everything is working fine in normal environment (e.g if i exit the client it is disconnected the socket same for the server)

Thanks in advance.

EDIT:

Here is the code in the client side. actually it’s a timer that tick every 3 second through programs lifetime and check if Socket is connected or not if its disconnected then it tries to reconnect again through a new socket instance

private void timerSocket_Tick(object sender, EventArgs e)
        {
            try
            {
                if (sck == null || !sck.Connected)
                {
                    ConnectToServer();
                }
            }
            catch (Exception ex)
            {
                RPLog.WriteDebugLog("Exception occcured at: "+ System.Reflection.MethodBase.GetCurrentMethod().ToString()+"Message: "+ex.Message);
            }
        }

In normal situation (without network disconnect/reconnect) if TCP server get a
SocketError.ConnectionReset form any client, in the client side i see
clients socket is disconnected and it tries to reconnect it again
through the code shown. but when situation happen explained earlier,
server gets a SocketError.ConnectionReset but client shows it still
connected. though the TCP server shows the reset command is send form the exact client
side.

Время на прочтение
10 мин

Количество просмотров 25K

В поисках причин, почему установленные соединения не разрываются в некоторых случаях, я набрёл на отличную статью в блоге Cloudflare. Которая, в итоге помогла найти не только решение моей проблемы, но и помогла лучше понять как работают таймауты TCP соединений в Linux

Надеюсь перевод этой статьи будет полезен многим читателям хабра.

Начало

Работая над нашим сервером Spectrum, мы заметили нечто странное: TCP-сокеты, которые, как мы думали, должны были быть закрыты, оставались на месте. Мы поняли, что не совсем понимаем, когда TCP-сокеты должны завершать работу!

 Image by Sergiodc2 CC BY SA 3.0

Image by Sergiodc2 CC BY SA 3.0

В нашем коде мы хотели убедиться, что мы не держим соединения с мертвыми хостами. В нашем раннем коде мы наивно полагали, что включения TCP keepalives будет достаточно… но это не так. Оказалось, что довольно современная опция сокета TCP_USER_TIMEOUT не менее важна. Более того, она тонко взаимодействует с TCP keepalives. Многих это ставит в тупик.

В этой статье блога мы постараемся показать, как работают эти опции. Мы покажем, как TCP сокет может прерваться по времени на различных этапах своего существования, и как на это влияют TCP keepalives и пользовательский таймаут. Чтобы лучше проиллюстрировать внутреннее устройство TCP-соединений, мы смешаем результаты команд tcpdump и ss -o. Это наглядно показывает передаваемые пакеты и изменяющиеся параметры TCP-соединений.

SYN-SENT

Начнем с самого простого случая — что происходит при попытке установить соединение с сервером, который отбрасывает входящие SYN-пакеты?

Используемые здесь скрипты доступны на нашем Github.

syn-sent

syn-sent

Хорошо, это было просто. После системного вызова connect() операционная система посылает SYN-пакет. Поскольку она не получила никакого ответа, ОС по умолчанию повторит попытку отправки 6 раз. Это можно настроить с помощью sysctl:

$ sysctl net.ipv4.tcp_syn_retries
net.ipv4.tcp_syn_retries = 6

Можно переписать эту настройку для каждого сокета с помощью параметра TCP_SYNCNT setsockopt:

setsockopt(sd, IPPROTO_TCP, TCP_SYNCNT, 6);

Повторные попытки выполняются в шахматном порядке с интервалами 1с, 3с, 7с, 15с, 31с, 63с (время между повторными попытками начинается с 2с и затем каждый раз удваивается). По умолчанию весь процесс занимает 130 секунд, пока ядро не сдастся с ошибкой ETIMEDOUT errno. В этот момент времени жизни соединения настройки SO_KEEPALIVE игнорируются, а TCP_USER_TIMEOUT — нет. Например, установка значения 5000 мс приведет к следующему взаимодействию:

syn-sent-500

syn-sent-500

Несмотря на то, что мы установили время ожидания пользователя равным 5с, мы все еще видели шесть повторов SYN на сокете. Такое поведение, вероятно, является ошибкой (проверено на ядре 5.2): мы ожидали, что будет отправлено только две повторные попытки — на отметках 1с и 3с, а сокет истечет на отметке 5с. Вместо этого мы увидели это, но также мы увидели еще 4 повторно переданных SYN-пакета, выровненных по 5s отметке — что не имеет смысла. Так или иначе, мы узнали одну вещь — TCP_USER_TIMEOUT действительно влияет на поведение connect().

SYN-RECV

Сокеты SYN-RECV обычно скрыты от приложения. Они живут как мини-сокеты в очереди SYN. В прошлом мы писали об очередях SYN и Accept. Иногда, когда включены файлы cookie SYN, сокеты могут вообще пропустить состояние SYN-RECV.

В состоянии SYN-RECV сокет повторит попытку отправки SYN + ACK 5 раз, это контролируется:

$ sysctl net.ipv4.tcp_synack_retries
net.ipv4.tcp_synack_retries = 5

Вот как это выглядит в кабеле:

syn-recv

syn-recv

С настройками по умолчанию SYN+ACK повторно передается с отметками 1с, 3с, 7с, 15с, 31с, а сокет SYN-RECV исчезает с отметкой 64с.

Ни SO_KEEPALIVE, ни TCP_USER_TIMEOUT не влияют на время жизни сокетов SYN-RECV.

Заключительное рукопожатие ACK (Final handshake ACK)

После получения второго пакета в TCP-рукопожатии — SYN+ACK — клиентский сокет переходит в установленное состояние. Серверный сокет остается в SYN-RECV до тех пор, пока не получит окончательный пакет ACK.

Потеря этого ACK ничего не меняет — сокету сервера просто потребуется немного больше времени, чтобы перейти от SYN-RECV к ESTAB. Вот как это выглядит:

test-syn-ack

test-syn-ack

Как вы можете видеть, SYN-RECV имеет таймер «on», такой же, как в предыдущем примере. Мы могли бы возразить, что этот окончательный ACK на самом деле не имеет большого веса. Это мышление привело к разработке функции TCP_DEFER_ACCEPT — она в основном приводит к тому, что третий ACK автоматически отбрасывается. С установленным этим флагом сокет остается в состоянии SYN-RECV до тех пор, пока не получит первый пакет с фактическими данными:

test-syn-ack2

test-syn-ack2

Серверный сокет оставался в состоянии SYN-RECV даже после получения окончательного подтверждения TCP-рукопожатия. У него забавный таймер «on», счетчик застревает на 0 повторных попытках. Он преобразуется в ESTAB — и перемещается из SYN в очередь приема — после того, как клиент отправляет пакет данных или по истечении таймера TCP_DEFER_ACCEPT. По сути, при ОТЛОЖЕННОМ ПРИНЯТИИ мини-сокет SYN-RECV отбрасывает входящий ACK без данных.

Простаивающий ESTAB — это навсегда (Idle ESTAB is forever)

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

ESTABLISHED

ESTABLISHED

По умолчанию у этих сокетов нет таймера запуска — они останутся в этом состоянии навсегда, даже если связь прервана. Стек TCP заметит проблемы только тогда, когда одна сторона попытается что-то отправить. В связи с этим возникает вопрос — что делать, если вы не планируете отправлять какие-либо данные по соединению? Как вы можете убедиться, что незанятое соединение исправно, не отправляя по нему никаких данных?

Вот тут-то и пригодятся TCP keepalives. Давайте посмотрим на это в действии — в этом примере мы использовали следующие переключатели:

  • SO_KEEPALIVE = 1 — Давайте включим keepalives.

  • TCP_KEEPIDLE = 5 — Отправить первый зонд keepalive после 5 секунд бездействия.

  • TCP_KEEPINTVL = 3 — отправка последующих пробников keepalive через 3 секунды.

  • TCP_KEEPCNT = 3 — Тайм-аут после трех неудачных попыток.

test-idle

test-idle

Действительно! Мы можем ясно видеть первый зонд, отправленный на отметке 5 секунд, два оставшихся зонда на расстоянии 3 секунд друг от друга — точно так, как мы указали. В общей сложности после трех отправленных зондов и еще трех секунд задержки соединение прерывается с помощью ETIMEDOUT, и, наконец, передается RST.

Чтобы keepalives работал, буфер отправки должен быть пустым. Вы можете заметить, что таймер keepalive активен в строке «таймер: (keepalive)».

Keepalives с TCP_USER_TIMEOUT сбивают с толку

Мы уже упоминали параметр TCP_USER_TIMEOUT. Он устанавливает максимальное время, в течение которого переданные данные могут оставаться неопознанными, прежде чем ядро принудительно закроет соединение. Сам по себе он мало что делает в случае неработающих соединений. Сокеты останутся ESTABLISHED, даже если соединение будет разорвано. Однако эта опция сокета изменяет семантику TCP keepalives. Руководство по tcp(7) несколько запутано:

Более того, при использовании с опцией TCP keepalive (SO_KEEPALIVE), TCP_USER_TIMEOUT будет преобладать над keepalive для определения момента закрытия соединения из-за отказа keepalive.

Исходное сообщение коммита содержит немного больше деталей:

  • tcp: Add TCP_USER_TIMEOUT socket option

To understand the semantics, we need to look at the kernel code in linux/net/ipv4/tcp_timer.c:693:

tcp-timers.c

tcp-timers.c

Чтобы тайм-аут пользователя имел какой-либо эффект, значение icsk_probes_out не должно быть равно нулю. Проверка тайм-аута пользователя выполняется только после того, как вышел первый зонд. Давайте проверим это. Наши настройки подключения:

  • TCP_USER_TIMEOUT = 5*1000 — 5 seconds

  • SO_KEEPALIVE = 1 — enable keepalives

  • TCP_KEEPIDLE = 1 — быстрая отправка первого зонда — 1 секунда простоя

  • TCP_KEEPINTVL = 11 — последующие зонды каждые 11 секунд

  • TCP_KEEPCNT = 3 — отправить три зонда до истечения времени ожидания

keepalive

keepalive

Что же произошло? Соединение отправило первый зонд keepalive на отметке 1с. Не увидев ответа, стек TCP проснулся через 11 секунд, чтобы послать второй зонд. На этот раз, однако, он выполнил путь кода USER_TIMEOUT, который решил немедленно прервать соединение.

Что, если мы увеличим TCP_USER_TIMEOUT до больших значений, скажем, между вторым и третьим зондом? Затем соединение будет закрыто по таймеру третьего зонда. С TCP_USER_TIMEOUT, установленным на 12,5с:

keepalive_3_probe

keepalive_3_probe

Мы показали, как TCP_USER_TIMEOUT взаимодействует с keepalives для малых и средних значений. Последний случай — это когда TCP_USER_TIMEOUT необычайно велик. Допустим, мы установим его на 30 секунд:

keepalive_30s

keepalive_30s

Мы видели шесть зондов keepalive на проводе! При установке TCP_USER_TIMEOUT TCP_KEEPCNT полностью игнорируется. Если вы хотите, чтобы TCP_KEEPCNT имел смысл, единственное разумное значение USER_TIMEOUT немного меньше, чем:

TCP_KEEPIDLE + TCP_KEEPINTVL * TCP_KEEPCNT

Занятый сокет ESTAB не вечен (Busy ESTAB socket is not forever)

До сих пор мы обсуждали случай, когда соединение простаивает. Другие правила применяются, когда соединение имеет не принятые данные в буфере отправки.

Давайте подготовим другой эксперимент — после трехстороннего рукопожатия настроим брандмауэр на отбрасывание всех пакетов. Затем выполним отправку на одном конце, чтобы иметь несколько отброшенных пакетов в полете. Эксперимент показывает, что отправляющий сокет умирает через ~16 минут:

tcp_retries2

tcp_retries2

Пакет данных повторно передается 15 раз, что контролируется:

$ sysctl net.ipv4.tcp_retries2
net.ipv4.tcp_retries2 = 15

Из документации ip-sysctl.txt:

Значение по умолчанию, равное 15, дает гипотетический тайм-аут в 924,6 секунды и является нижней границей эффективного тайм-аута. TCP фактически отключит время ожидания при первом RTO, которое превышает гипотетический тайм-аут.

Соединение действительно прервалось через ~ 940 секунд. Обратите внимание, что в сокете запущен таймер «on». Если мы установим SO_KEEPALIVE — не будет иметь никакого значения — когда таймер «on» запущен, keepalives не задействованы.

TCP_USER_TIMEOUT продолжает работать до конца. Соединение будет прервано точно по истечении указанного пользователем времени ожидания с момента последнего полученного пакета. При установленном таймауте пользователя значение tcp_retries2 игнорируется.

Нулевое окно ESTAB — это… навсегда?(Zero window ESTAB is… forever?)

Есть последний случай, о котором стоит упомянуть. Если у отправителя много данных, а приемник медленный, то включается TCP flow control. В какой-то момент получатель попросит отправителя прекратить передачу новых данных. Это несколько иное состояние, чем описанное выше.

В этом случае, когда включено управление потоком, нет никаких летящих или не подтвержденных данных. Вместо этого приемник дросселирует отправителя уведомлением о «нулевом окне». Затем отправитель периодически проверяет, действительно ли это условие, с помощью «зондов окна». В этом эксперименте мы уменьшили размер буфера приема, для простоты. Вот как это выглядит на проводе:

Захват пакетов показывает несколько вещей. Во-первых, мы видим два пакета с данными, каждый длиной 576 байт. Оба они были немедленно подтверждены. Второй ACK содержит уведомление «win 0»: отправителю было сказано прекратить отправку данных.

Но отправитель жаждет отправить еще! Последние два пакета показывают первый «зонд окна»: отправитель будет периодически посылать пакеты «ack» без полезной нагрузки, чтобы проверить, не изменился ли размер окна. До тех пор, пока получатель продолжает отвечать, отправитель будет посылать такие запросы вечно. Информация о сокете показывает три важные вещи:

  • Буфер чтения считывателя заполнен — таким образом, ожидается регулирование «нулевого окна».

  • Буфер записи отправителя заполнен — у нас есть больше данных для отправки.

  • У отправителя запущен таймер «persist», отсчитывающий время до следующего «оконного зонда».

В этой статье блога нас интересуют таймауты — что произойдет, если зонды окна будут потеряны? Заметит ли это отправитель?

По умолчанию оконный зонд повторяется 15 раз — в соответствии с обычной настройкой tcp_retries2.

Таймер tcp находится в состоянии persist, поэтому TCP keepalives не будет запущен. Настройки SO_KEEPALIVE не имеют никакого значения, если задействовано оконное зондирование.

Как и ожидалось, тумблер TCP_USER_TIMEOUT продолжает работать. Небольшое отличие заключается в том, что аналогично user-timeout для keepalives, он включается только при срабатывании таймера повторной передачи. Во время такого события, если с момента последнего хорошего пакета прошло более чем user-timeout секунд, соединение будет прервано.

Примечание об использовании тайм-аутов приложений

В прошлом мы делились интересной военной историей:

  • Любопытный случай медленной загрузки

Наш HTTP-сервер прекращал соединение после истечения тайм-аута, управляемого приложением. Это была ошибка — медленное соединение могло правильно медленно опустошать буфер отправки, но сервер приложения этого не замечал.

Мы резко прекратили медленную загрузку, хотя это не входило в наши намерения. Мы просто хотели убедиться, что клиентское соединение все еще исправно. Было бы лучше использовать TCP_USER_TIMEOUT, чем полагаться на таймауты, управляемые приложением.

Но этого недостаточно. Мы также хотели защититься от ситуации, когда клиентский поток действителен, но завис и не разрывает соединение. Единственный способ добиться этого — периодически проверять количество не отправленных данных в буфере отправки и смотреть, уменьшается ли оно с нужной скоростью.

Для типичных приложений, отправляющих данные в Интернет, я бы рекомендовал:

  1. Включите TCP keepalives. Это необходимо для поддержания потока некоторых данных в случае бездействующего соединения.

  2. Установите TCP_USER_TIMEOUT равным:

    TCP_KEEPIDLE + TCP_KEEPINTVL * TCP_KEEPCNT

  3. Будьте осторожны при использовании тайм-аутов, управляемых приложением. Для обнаружения сбоев TCP используйте TCP keepalives и user-timeout. Если вы хотите сэкономить ресурсы и убедиться, что сокеты не работают слишком долго, подумайте о том, чтобы периодически проверять, истощается ли сокет с желаемой скоростью. Для этого вы можете использовать ioctl(SIOCOUTQ), но он учитывает как данные, буферизованные (не отправленные) в сокете, так и байты в полете (неподтвержденные). Лучший способ — использовать параметр TCP_INFO tcpi_notsent_bytes, который сообщает только о первом счетчике.

Пример проверки темпа слива:

pace_in_bytes_per_second

pace_in_bytes_per_second

Есть способы еще больше улучшить эту логику. Мы могли бы использовать TCP_NOTSENT_LOWFAT, хотя обычно это полезно только в ситуациях, когда буфер отправки относительно пуст. Тогда мы могли бы использовать интерфейс SO_TIMESTAMPING для уведомлений о том, когда данные будут доставлены. Наконец, если мы закончили отправку данных в сокет, можно просто вызвать close() и отложить обработку сокета для операционной системы. Такой сокет будет застрять в состоянии FIN-WAIT-1 или LAST-ACK до тех пор, пока он правильно не опустеет.

Выводы

В этом посте мы обсудили пять случаев, когда TCP-соединение может заметить, что другая сторона уходит:

  • SYN-SENT: продолжительность этого состояния можно контролировать с помощью TCP_SYNCNT или tcp_syn_retries.

  • SYN-RECV: обычно это скрыто от приложения. Он настраивается с помощью tcp_synack_retries.

  • Простаивающее ESTABLISHED соединение, никогда не заметит никаких проблем. Решением является использование TCP keepalives.

  • Зависшее ESTABLISHED соединение, придерживается настройки tcp_retries2 и игнорирует TCP keepalives.

  • Соединение ESTABLISHED с нулевым окном, придерживается настройки tcp_retries2 и игнорирует TCP keepalives.

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

Однако это опасная настройка, и если она используется в сочетании с TCP keepalives, ее следует устанавливать на значение немного меньше, чем TCP_KEEPIDLE + TCP_KEEPINTVL * TCP_KEEPCNT. В противном случае оно будет влиять на значение TCP_KEEPCNT и потенциально аннулировать его.

В этом посте мы представили сценарии, демонстрирующие влияние опций сокетов, связанных с таймаутом, в различных сетевых условиях. Чередование захвата пакетов tcpdump с выводом ss -o — отличный способ понять работу сетевого стека. Мы смогли создать воспроизводимые тестовые примеры, демонстрирующие таймеры «on», «keepalive» и «persist» в действии. Это очень полезная основа для дальнейших экспериментов.

Наконец, удивительно трудно настроить TCP-соединение так, чтобы быть уверенным, что удаленный узел действительно работает. Во время отладки мы обнаружили, что просмотр размера буфера отправки и текущего активного таймера TCP может быть очень полезен для понимания того, действительно ли сокет здоров. Ошибка в нашем приложении Spectrum оказалась в неправильной настройке TCP_USER_TIMEOUT — без нее сокеты с большими буферами отправки задерживались гораздо дольше, чем мы предполагали.

Скрипты, использованные в этой статье, можно найти на нашем Github.

Для решения этой задачи пришлось сотрудничать с тремя офисами Cloudflare. Спасибо Хирену Панчасаре из Сан-Хосе, Уоррену Нельсону из Остина и Якубу Ситницки из Варшавы.

Автор оригинальной статьи: Марек Майковски.
Ссылка на оригинальный пост: link

Понравилась статья? Поделить с друзьями:
  • Teams код ошибки 80090030
  • Tcl код ошибки стиральной машины lg
  • Telegram ошибка при запуске приложения 0xc0000005
  • Telegram ошибка internal server error
  • Telegram ошибка an error occurred