- 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
Нажмите здесь для печати.