- PVSM.RU - https://www.pvsm.ru -

Говорят, что нельзя полностью понять систему, пока не поймёшь её сбои. Ещё будучи студентом я ради забавы написал реализацию TCP, а потом несколько лет проработал в IT, но до сих пор продолжаю глубже и глубже изучать работу TCP — и его ошибки. Самое удивительное, что некоторые из этих ошибок проявляются в базовых вещах. И они неочевидны. В этой статье я преподнесу их как головоломки, в стиле Car Talk [1] или старых головоломок Java [2]. Как и любые другие хорошие головоломки, их очень просто воспроизвести, но решения обычно удивляют. И вместо того, чтобы фокусировать наше внимание на загадочных подробностях, эти головоломки помогают изучить некоторые глубинные принципы работы TCP.
Эти головоломки подразумевают наличие базовых знаний о работе TCP на Unix-подобных системах. Но вам не нужно быть мастером, чтобы вникнуть в них. Например:
read, write, connect, bind, listen и accept. Помимо этого, есть также send и recv, но в наших примерах они будут вести себя как read и write.poll. Хотя многие системы используют что-то более эффективное, например, kqueue или epoll, в рамках нашей задачи все эти инструменты будут эквивалентны. Что касается приложений, использующих операции блокирования, а не какой-либо из этих механизмов: один раз поняв, как ошибки TCP отражаются на poll, вам будет проще догадаться, какой эффект они окажут на любые операции блокирования.
Вы можете повторить все эти примеры самостоятельно. Я использовал две виртуальные машины, запущенные с помощью VMware Fusion. Результаты получились такие же, как на production-сервере. Для тестирования я использовал nc(1) на SmartOS, и не поверю, что любая из воспроизводимых неполадок будет специфична для конкретной ОС. Для отслеживания системных вызовов и сбора грубой информации о таймингах я использовал утилиту truss(1) [4] из проекта illumos. Вы можете получить подобную информацию с помощью dtruss(1m) [5] под OS X или strace(1) [6] под GNU/Linux.
nc(1) очень простая программа. Мы будем использовать её в двух режимах:
accept и заблокирует, пока не будет установлено соединение.nc создаст сокет и установит соединение с удалённым сервером.
В обоих режимах после установки соединения каждая из сторон использует poll для ожидания стандартного ввода или подключения сокета, имеющего готовые для чтения данные. Входящие данные выводятся в терминал. Данные, которые вы вводите в терминал, отправляются через сокет. При нажатии CTRL-C сокет закрывается и процесс останавливается.
В примерах мой клиент будет называться kang, а сервер — kodos.
Начнём с базовой ситуации. Представим, что мы настроили сервер на kodos:
Сервер
[root@kodos ~]# truss -d -t bind,listen,accept,poll,read,write nc -l -p 8080
Base time stamp: 1464310423.7650 [ Fri May 27 00:53:43 UTC 2016 ]
0.0027 bind(3, 0x08065790, 32, SOV_SOCKBSD) = 0
0.0028 listen(3, 1, SOV_DEFAULT) = 0
accept(3, 0x08047B3C, 0x08047C3C, SOV_DEFAULT, 0) (sleeping...)
(Напоминаю, что в этих примерах я использую truss для вывода системных вызовов, которые делает nc. Информация о времени выводится с помощью флага -d, а -t позволяет выбрать, какие из вызовов мы хотим увидеть.)
Теперь я устанавливаю соединение на kang:
Клиент
[root@kang ~]# truss -d -t connect,pollsys,read,write,close nc 10.88.88.140 8080
Base time stamp: 1464310447.6295 [ Fri May 27 00:54:07 UTC 2016 ]
...
0.0062 connect(3, 0x08066DD8, 16, SOV_DEFAULT) = 0
pollsys(0x08045670, 2, 0x00000000, 0x00000000) (sleeping...)
На kodos мы видим:
Сервер
23.8934 accept(3, 0x08047B3C, 0x08047C3C, SOV_DEFAULT, 0) = 4
pollsys(0x08045680, 2, 0x00000000, 0x00000000) (sleeping...)
Подключение TCP находится в состоянии ESTABLISHED, а оба процесса в poll. Мы можем увидеть это на каждой системе с помощью netstat:
Сервер
[root@kodos ~]# netstat -f inet -P tcp -n
TCP: IPv4
Local Address Remote Address Swind Send-Q Rwind Recv-Q State
–––––––––––––––––––– –––––––––––––––––––– ––––– –––––- ––––– –––––- ––––––––––-
10.88.88.140.8080 10.88.88.139.33226 1049792 0 1049800 0 ESTABLISHED
...
Клиент
[root@kang ~]# netstat -f inet -P tcp -n
TCP: IPv4
Local Address Remote Address Swind Send-Q Rwind Recv-Q State
–––––––––––––––––––– –––––––––––––––––––– ––––– –––––- ––––– –––––- ––––––––––-
10.88.88.139.33226 10.88.88.140.8080 32806 0 1049800 0 ESTABLISHED
...
Вопрос: когда мы завершим один из процессов, что случится с другим? Поймёт ли он, что произошло? Как он это поймёт? Попробуем предугадать поведение конкретных системных вызовов и объяснить, почему каждый из них делает то, что делает.
Нажмём CTRL-C на kodos:
Сервер
pollsys(0x08045680, 2, 0x00000000, 0x00000000) (sleeping...)
^C127.6307 Received signal #2, SIGINT, in pollsys() [default]
А вот что мы видим на kang:
Клиент
pollsys(0x08045670, 2, 0x00000000, 0x00000000) (sleeping...)
126.1771 pollsys(0x08045670, 2, 0x00000000, 0x00000000) = 1
126.1774 read(3, 0x08043670, 1024) = 0
126.1776 close(3) = 0
[root@kang ~]#
Что случилось? Давайте разберемся:
ESTABLISHED, стек TCP на kodos отправляет через соединение FIN и переходит в состояние FIN_WAIT_1.kang получает пакет FIN, переводит собственное соединение в состояние CLOSE_WAIT и отправляет в ответ ACK. Пока клиент nc блокирует сокет — он готов к чтению, ядро будит этот тред с помощью POLLIN.nc видит POLLIN для сокета и вызывает read, который тут же возвращает 0. Это означает конец соединения. nc решает, что мы закончили работу с сокетом, и закрывает его.kodos получает ACK и переходит в состояние FIN_WAIT_2.nc на kang закрывает свой сокет, стек TCP на kang отправляет FIN на kodos. Соединение на kang переходит в состояние LAST_ACK.kodos получает FIN, соединение переходит в состояние TIME_WAIT, и стек на kodos подтверждает FIN.kang получает ACK для FIN и полностью удаляет соединение.kodos закрывается, и стек полностью удаляет соединение.
Очерёдность этапов может незначительно меняться. Также kang может вместо FIN_WAIT_2 проходить через состояние CLOSING.
Вот так, согласно netstat, выглядит финальное состояние:
Сервер
[root@kodos ~]# netstat -f inet -P tcp -n
TCP: IPv4
Local Address Remote Address Swind Send-Q Rwind Recv-Q State
–––––––––––––––––––– –––––––––––––––––––– ––––– –––––- ––––– –––––- ––––––––––-
10.88.88.140.8080 10.88.88.139.33226 1049792 0 1049800 0 TIME_WAIT
На kang для этого соединения нет никаких исходящих данных.
Промежуточные состояния проходят очень быстро, но вы можете отследить их с помощью DTrace TCP provider [7]. Поток пакетов можно посмотреть с помощью snoop(1m) [8] или tcpdump(1) [9].
Выводы: Мы увидели нормальный путь прохождения системных вызовов во время установки и закрытия соединения. Обратите внимание, что kang незамедлительно обнаружил факт закрытия соединения на kodos — он был разбужен из poll, а возвращение нуля read обозначило завершение потока передачи. В этот момент kang решил закрыть сокет, что привело к закрытию соединения с kodos. Мы вернёмся к этому позже и посмотрим, что будет, если kang не станет закрывать сокет в этой ситуации.
Что случится с установленным неактивным TCP подключением при перезапуске питания одной из систем?
Поскольку в процессе запланированной перезагрузки многие процессы завершаются корректным образом (с использованием команды “reboot”), то результат будет такой же, если ввести в консоль kodos команду “reboot” вместо завершения работы сервера с помощью CTRL-C. Но что случится, если в предыдущем примере мы просто отключим электропитание для kodos? В конечном итоге kang об этом узнает, верно?
Давайте проверим. Устанавливаем подключение:
Сервер
[root@kodos ~]# truss -d -t bind,listen,accept,poll,read,write nc -l -p 8080
Base time stamp: 1464312528.4308 [ Fri May 27 01:28:48 UTC 2016 ]
0.0036 bind(3, 0x08065790, 32, SOV_SOCKBSD) = 0
0.0036 listen(3, 1, SOV_DEFAULT) = 0
0.2518 accept(3, 0x08047B3C, 0x08047C3C, SOV_DEFAULT, 0) = 4
pollsys(0x08045680, 2, 0x00000000, 0x00000000) (sleeping...)
Клиент
[root@kang ~]# truss -d -t open,connect,pollsys,read,write,close nc 10.88.88.140 8080
Base time stamp: 1464312535.7634 [ Fri May 27 01:28:55 UTC 2016 ]
...
0.0055 connect(3, 0x08066DD8, 16, SOV_DEFAULT) = 0
pollsys(0x08045670, 2, 0x00000000, 0x00000000) (sleeping...)
Для эмуляции перезапуска электропитания я воспользуюсь функцией «reboot» из VMware. Обратите внимание, что это будет настоящий перезапуск — всё, что приводит к постепенному выключению, больше похоже на первый пример.
Спустя 20 минут kang всё в том же состоянии:
Клиент
pollsys(0x08045670, 2, 0x00000000, 0x00000000) (sleeping...)
Мы склонны верить, что работа TCP заключается в постоянном поддержании абстракции (а именно, TCP-соединения) между несколькими системами, так что подобные случаи сломанной абстракции выглядят удивительно. И если вы считаете, что это какая-то проблема nc(1), то вы ошибаетесь. «netstat» на kodos не показывает никакого соединения с kang, но при этом kang покажет полностью рабочее подключение к kodos:
Клиент
[root@kang ~]# netstat -f inet -P tcp -n
TCP: IPv4
Local Address Remote Address Swind Send-Q Rwind Recv-Q State
–––––––––––––––––––– –––––––––––––––––––– ––––– –––––- ––––– –––––- ––––––––––-
10.88.88.139.50277 10.88.88.140.8080 32806 0 1049800 0 ESTABLISHED
...
Если оставить всё как есть, то kang никогда не узнает, что kodos был перезагружен.
Теперь предположим, что kang пытается отправить данные kodos. Что произойдёт?
Клиент
pollsys(0x08045670, 2, 0x00000000, 0x00000000) (sleeping...)
kodos, are you there?
3872.6918 pollsys(0x08045670, 2, 0x00000000, 0x00000000) = 1
3872.6920 read(0, " k o d o s , a r e y".., 1024) = 22
3872.6924 write(3, " k o d o s , a r e y".., 22) = 22
3872.6932 pollsys(0x08045670, 2, 0x00000000, 0x00000000) = 1
3872.6932 read(3, 0x08043670, 1024) Err#131 ECONNRESET
3872.6933 close(3) = 0
[root@kang ~]#
Когда я ввожу сообщение и жму Enter, kodos просыпается, читает сообщение из stdin и отправляет его через сокет. Вызов write успешно завершён! nc возвращается в poll в ожидании следующего события, и в конце концов приходит к выводу, что сокет не может быть прочитан без блокировки, после чего вызывает read. В этот раз read падает со статусом ECONNRESET. Что это значит? Документация к read(2) [10] говорит нам:
[ECONNRESET]
Была попытка чтения из сокета, то соединение было принудительно закрыто пиром.
Другой источник [11] содержит чуть больше подробностей:
ECONNRESET
Аргумент filedes ссылается на сокет с установленным соединением. Оно было принудительно закрыто пиром и больше недействительно. Операции ввода/вывода больше не могут выполняться с filedes.
Эта ошибка не означает какую-то конкретную проблему с вызовом read. Она лишь говорит о том, что сокет был отключён. По этой причине большинство операций с сокетом приведут к ошибке.
Так что же случилось? В тот момент, когда nc на kang попытался отправить данные, стек TCP всё ещё не знал, что подключение уже мертво. kang отправил пакет данных на kodos, который ответил RST, потому что ничего не знал о подключении. kang увидел RST и прервал подключение. Файловый дескриптор сокета закрыть невозможно, — файловые дескрипторы работают не так, — но последующие операции будут неудачными со статусом ECONNRESET, пока nc не закроет файловый дескриптор.
Выводы:
kang всё-таки узнал об исчезновении удалённой стороны, заключается в том, что он отправил данные и получил ответ, сигнализирующий об отсутствии подключения.
Возникает вопрос: а что если kodos по какой-то причине не отвечает на отправку данных?
Что случится, если конечная точка TCP соединения отключится от сети на какое-то время? Узнают ли об этом остальные узлы? Если да, то как? И когда?
Вновь установим соединение с помощью nc:
Сервер
[root@kodos ~]# truss -d -t bind,listen,accept,poll,read,write nc -l -p 8080
Base time stamp: 1464385399.1661 [ Fri May 27 21:43:19 UTC 2016 ]
0.0030 bind(3, 0x08065790, 32, SOV_SOCKBSD) = 0
0.0031 listen(3, 1, SOV_DEFAULT) = 0
accept(3, 0x08047B3C, 0x08047C3C, SOV_DEFAULT, 0) (sleeping...)
6.5491 accept(3, 0x08047B3C, 0x08047C3C, SOV_DEFAULT, 0) = 4
pollsys(0x08045680, 2, 0x00000000, 0x00000000) (sleeping...)
Клиент
[root@kang ~]# truss -d -t open,connect,pollsys,read,write,close nc 10.88.88.140 8080
Base time stamp: 1464330881.0984 [ Fri May 27 06:34:41 UTC 2016 ]
...
0.0057 connect(3, 0x08066DD8, 16, SOV_DEFAULT) = 0
pollsys(0x08045670, 2, 0x00000000, 0x00000000) (sleeping...)
Теперь внезапно выключим питание kodos и попытаемся отправить данные с kang:
Клиент
pollsys(0x08045670, 2, 0x00000000, 0x00000000) (sleeping...)
114.4971 pollsys(0x08045670, 2, 0x00000000, 0x00000000) = 1
114.4974 read(0, "n", 1024) = 1
114.4975 write(3, "n", 1) = 1
pollsys(0x08045670, 2, 0x00000000, 0x00000000) (sleeping...)
Вызов write завершается нормально, и я долго ничего не вижу. Только через пять минут появляется:
Клиент
pollsys(0x08045670, 2, 0x00000000, 0x00000000) (sleeping...)
425.5664 pollsys(0x08045670, 2, 0x00000000, 0x00000000) = 1
425.5665 read(3, 0x08043670, 1024) Err#145 ETIMEDOUT
425.5666 close(3) = 0
Эта ситуация очень похожа на ту, когда мы перезапускали электропитание вместо полного отключения. Есть два отличия:
Снова обратите внимание — это истёкший тайм-аут read. Мы бы увидели ту же самую ошибку и при других операциях с сокетом. Это происходит потому, что сокет входит в состояние, когда истёк тайм-аут подключения. Причина этого в том, что удалённая сторона слишком долго не подтверждала пакет данных — 5 минут, в соответствии с настройками этой системы.
Выводы:
На этот раз вместо того, чтобы описывать вам специфическую ситуацию и спрашивать, что происходит, я поступлю наоборот: опишу некое наблюдение и посмотрю, сможете ли вы понять, как такое произошло. Мы обсуждали несколько ситуаций, в которых kang может верить, что он подключён к kodos, но kodos об этом не знает. Возможно ли для kang быть подключённым к kodos так, чтобы kodos не знал об этом в течение неопределённого срока (т.е. проблема не решится сама собой), и при этом не было бы отключения или перезапуска электропитания, никакой другой ошибки операционной системы kodos или сетевого оборудования?
Подсказка: рассмотрим вышеописанный случай, когда соединение застряло в статусе ESTABLISHED. Справедливо считать, что ответственность за решение этой проблемы несёт приложение, так как оно держит сокет открытым и может обнаружить посредством отправки данных, когда соединение было прервано. Но что если приложение уже не держит сокет открытым?
В разминке мы рассматривали ситуацию, когда nc на kodos закрыло сокет. Мы сказали, что nc на kang прочитало 0 (указатель окончания передачи) и закрыло сокет. Допустим, сокет остался открытым. Очевидно, что из него невозможно было бы читать. Но касательно TCP ничего не говорится о том, что вы не можете отправлять дополнительные данные той стороне, которая послала вам FIN. FIN означает лишь закрытие потока данных в том направлении, по которому был послан FIN.
Чтобы продемонстрировать это, мы не можем использовать nc на kang, потому что оно автоматически закрывает сокет после получения 0. Поэтому, я написал демо-версию nc, под названием dnc [12], которая пропускает этот момент. Также dnc [12] явным образом выводит системные вызовы, которые она совершает. Это даст нам шанс отследить состояния TCP.
Сперва настроим подключение:
Сервер
[root@kodos ~]# truss -d -t bind,listen,accept,poll,read,write nc -l -p 8080
Base time stamp: 1464392924.7841 [ Fri May 27 23:48:44 UTC 2016 ]
0.0028 bind(3, 0x08065790, 32, SOV_SOCKBSD) = 0
0.0028 listen(3, 1, SOV_DEFAULT) = 0
accept(3, 0x08047B2C, 0x08047C2C, SOV_DEFAULT, 0) (sleeping...)
1.9356 accept(3, 0x08047B2C, 0x08047C2C, SOV_DEFAULT, 0) = 4
pollsys(0x08045670, 2, 0x00000000, 0x00000000) (sleeping...)
Клиент
[root@kang ~]# dnc 10.88.88.140 8080
2016-05-27T08:40:02Z: establishing connection
2016-05-27T08:40:02Z: connected
2016-05-27T08:40:02Z: entering poll()
Теперь убедимся, что на обеих сторонах подключение находится в статусе ESTABLISHED:
Сервер
[root@kodos ~]# netstat -f inet -P tcp -n
TCP: IPv4
Local Address Remote Address Swind Send-Q Rwind Recv-Q State
–––––––––––––––––––– –––––––––––––––––––– ––––– –––––- ––––– –––––- ––––––––––-
10.88.88.140.8080 10.88.88.139.37259 1049792 0 1049800 0 ESTABLISHED
Клиент
[root@kang ~]# netstat -f inet -P tcp -n
TCP: IPv4
Local Address Remote Address Swind Send-Q Rwind Recv-Q State
–––––––––––––––––––– –––––––––––––––––––– ––––– –––––- ––––– –––––- ––––––––––-
10.88.88.139.37259 10.88.88.140.8080 32806 0 1049800 0 ESTABLISHED
На kodos применим CTRL-C для процесса nc:
Сервер
pollsys(0x08045670, 2, 0x00000000, 0x00000000) (sleeping...)
^C[root@kodos ~]#
На kang сразу увидим следующее:
Клиент
2016-05-27T08:40:12Z: poll returned events 0x0/0x1
2016-05-27T08:40:12Z: reading from socket
2016-05-27T08:40:12Z: read end-of-stream from socket
2016-05-27T08:40:12Z: read 0 bytes from socket
2016-05-27T08:40:12Z: entering poll()
Теперь посмотрим на статус подключений TCP:
Сервер
[root@kodos ~]# netstat -f inet -P tcp -n
TCP: IPv4
Local Address Remote Address Swind Send-Q Rwind Recv-Q State
–––––––––––––––––––– –––––––––––––––––––– ––––– –––––- ––––– –––––- ––––––––––-
10.88.88.140.8080 10.88.88.139.37259 1049792 0 1049800 0 FIN_WAIT_2
Клиент
[root@kang ~]# netstat -f inet -P tcp -n
TCP: IPv4
Local Address Remote Address Swind Send-Q Rwind Recv-Q State
–––––––––––––––––––– –––––––––––––––––––– ––––– –––––- ––––– –––––- ––––––––––-
10.88.88.139.37259 10.88.88.140.8080 1049792 0 1049800 0 CLOSE_WAIT
Это имеет смысл: kudos отправил FIN на kang. FIN_WAIT_2 показывает, что kodos получил ACK от kang в ответ на посланный им FIN, а CLOSE_WAIT показывает, что kang получил FIN, но не отправил FIN в ответ. Это вполне нормальное состояние TCP-подключения, которое может длится бесконечно. Представьте, что kodos отправил запрос kang и не планировал отправлять ничего больше; kang может часами счастливо отправлять данные в ответ. Только в нашем случае kodos фактически закрыл сокет.
Давайте подождём минуту и вновь проверим статус TCP-подключений. Выяснилось, что на kodos подключение полностью пропадает, но всё ещё существует на kang:
Клиент
[root@kang ~]# netstat -f inet -P tcp -n
TCP: IPv4
Local Address Remote Address Swind Send-Q Rwind Recv-Q State
–––––––––––––––––––– –––––––––––––––––––– ––––– –––––- ––––– –––––- ––––––––––-
10.88.88.139.37259 10.88.88.140.8080 1049792 0 1049800 0 CLOSE_WAIT
Мы столкнулись с менее известной ситуацией, связанной со TCP-стеком: когда приложение закрыло сокет, стек отправил FIN, удалённый стек его распознал FIN, а локальный стек ожидает фиксированный период времени и закрывает соединение. Причина? Удалённая сторона была перезагружена. Этот случай аналогичен тому, когда подключение на одной стороне находится в статусе ESTABLISHED, а другая сторона об этом не знает. Разница заключается лишь в том, что приложение закрыло сокет, и нет никакого другого компонента, который мог бы разобраться с проблемой. В результате TCP-стек ждёт установленный период времени и закрывает соединение (ничего не посылая на другую сторону).
Вопрос вдогонку: что случится, если в этой ситуации kang отправит данные к kodos? Не забывайте, kang всё ещё считает, что соединение открыто, хотя на стороне kodos оно уже завершено.
Клиент
2016-05-27T08:40:12Z: entering poll()
kodos, are you there?
2016-05-27T08:41:34Z: poll returned events 0x1/0x0
2016-05-27T08:41:34Z: reading from stdin
2016-05-27T08:41:34Z: writing 22 bytes read from stdin to socket
2016-05-27T08:41:34Z: entering poll()
2016-05-27T08:41:34Z: poll returned events 0x0/0x10
2016-05-27T08:41:34Z: reading from socket
dnc: read: Connection reset by peer
Это то же самое, что мы видели в Головоломке 1: write() успешно выполняется, так как TCP-стек ещё не знает, что соединение закрыто. Но затем идёт RST, который пробуждает находящийся в poll() тред, и последующий запрос read() возвращает ECONNRESET.
Выводы:
kang не имеет возможности узнать, ожидает ли kodos получения данных от kang, или же kodos закрыл сокет и не прослушивает его (по крайней мере, не без отправки пакета). Поэтому не стоит проектировать систему, которая при нормальных условиях эксплуатации в течение длительного времени будет использовать сокеты в подобных полуоткрытых состояниях. TCP обычно представляется нам как протокол, который поддерживает абстракцию — «TCP-соединение» — между двумя системами. Мы знаем, что из-за некоторых программных или сетевых проблем соединение упадёт. Но не всегда очевидно, что бывают случаи возникновения сбоев самой абстракции, из-за чего системы расходятся во мнении по поводу состояния подключения. Например:
Такое поведение не говорит о недостатках TCP. Наоборот, в подобных случаях TCP ведёт себя наиболее разумно, с учетом ситуации. Если бы мы пытались реализовать свой собственный механизм передачи данных вместо TCP, то подобные случаи напомнили бы нам о том, насколько сложные проблемы могут возникнуть. Это внутренние проблемы, связанные с распределёнными системами, а TCP-подключение по сути является распределённой системой.
Тем не менее, наиболее важный урок, который можно вынести из всего этого, заключается в том, что понятие «TCP-соединения, охватывающего несколько систем» — это удобная фикция. Когда что-то идёт не так, очень важно, чтобы две разные машины одновременно пытались согласованное представление о соединении. Приложение начинает решать возникающие проблемы в тех случаях, когда машины действую по-разному (для этого часто используется механизм keep-alive).
Кроме того, дескриптор файла «оторван» от соответствующего TCP-соединения. Соединения существуют (в разных, связанных с закрытием состояниях) даже тогда, когда приложение закрыло дескриптор файла. А иногда дескриптор файла может быть открыт, хотя TCP-соединение было закрыто в результате ошибки.
Остальные уроки, о которых стоит помнить:
read(), write(), или выполняя другие операции с дескриптором файла сокетом. Если ваша программа по какой-то причине этого не делает, то вы никогда не узнаете об ошибке соединения. Некоторые малоизвестные моменты:
read(), write() и других операций. Она означает, что удалённый компьютер послал RST.read(), write() и остальных операций. Она означает, что истёк некий таймаут, имеющий отношение к соединению. В большинстве случаев это происходит, когда удалённая сторона слишком долго не признаёт пакет. Обычно это пакеты данных, пакет FIN или сигнал KeepAlive.Важно отметить, что эти ошибки не означают, будто что-то пошло не так с вашими операциями чтения или записи. Это лишь означает, что закрыт сам сокет.
Автор: Mail.Ru Group
Источник [13]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/tcp/213664
Ссылки в тексте:
[1] Car Talk: http://www.cartalk.com/content/puzzlers
[2] старых головоломок Java: https://www.youtube.com/watch?v=wbp-3BJWsU8
[3] Википедии: https://ru.wikipedia.org/wiki/TCP#.D0.9C.D0.B5.D1.85.D0.B0.D0.BD.D0.B8.D0.B7.D0.BC_.D0.B4.D0.B5.D0.B9.D1.81.D1.82.D0.B2.D0.B8.D1.8F_.D0.BF.D1.80.D0.BE.D1.82.D0.BE.D0.BA.D0.BE.D0.BB.D0.B0
[4] truss(1): http://illumos.org/man/truss
[5] dtruss(1m): https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man1/dtruss.1m.html
[6] strace(1): http://linux.die.net/man/1/strace
[7] DTrace TCP provider: http://dtracebook.com/index.php/Network_Lower_Level_Protocols:tcpstate.d
[8] snoop(1m): http://illumos.org/man/snoop
[9] tcpdump(1): http://www.tcpdump.org/tcpdump_man.html
[10] Документация к read(2): http://pubs.opengroup.org/onlinepubs/009695399/functions/read.html
[11] Другой источник: http://illumos.org/man/read.2
[12] dnc: https://github.com/davepacheco/experiment-dnc
[13] Источник: https://habrahabr.ru/post/316128/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best
Нажмите здесь для печати.