Comments 5
нет, для других платформ справедливы тоже, за исключением разве что RMI. Но опять на других платформах тоже могут быть протоколы использующие произвольные порты. Например, CORBA, на сколько я слышал тоже грешит этим. Хотя, наверное, в некоторых случаях это даже удобнее, чем явное конфигурирование всего и всея. Правда, пока у вас не появляется firewall.
Ещё нужно помнить, что как фаерволл, так и просто сетевое оборудование может подменять ip-адреса/порты в заголовках пакетов (nat). Поэтому нежелательно встраивать в прикладной протокол какой-либо обмен ip-адресами конечных точек подключения. Из-за nat бывает не работает аутентификация в различных vpn-ах, про проблемы h323 знают многие, а уж поддержка ftp и проблемы с ftp-ssl — это канонический случай.
Добавлю от себя:
> Соединение не пересылающее никаких данных закрывается через определенный промежуток времени.
Причем в этом случае ни клиент ни сервер не получают нотификации и остаются висеть на Socket.read(). Время idle-таймаута зависит от настроек роутеров.
> Соединение не пересылающее никаких данных может превратиться в черную дыру (black hole), которая проявляется таким образом, что вы посылаете сообщения, они успешно уходят
Не совсем так. Посылаемые сообщения ставятся в WriteBuffer. Если данные успешно не отправились за время socket timeout (по дефолту 20-30 секунд на разных ОС), то возникает exception. Успешная отправка означает, что для отправляемых фреймов пришло TCP-подтверждение (т.е. данные успешно получены другой стороной). Именно запись данных в сокет гарантирует проверку состояния соединения (хотя и не сразу).
— Соединение зависит не только от настроек оборудования на стороне сервера. Клиент тоже может находится за файрволлом и проксями и иметь свои настройки, которые могут сильно варьироваться.
— Если одна из сторон оборвала соединение, то вовсе не обязательно, что об этом узнает другая (одна из основных ошибок). Если вы просто отключите роутер или выдерните сетевой кабель, то апликация продолжит висеть на Socket.read(). Даже если сетевой интерфейс упал, апликация не получит уведомления в виде эксепшна (странное довольно поведение). И так будет до тех пор, пока вы не сделаете write().
> Обычно этого добиваются, вводя специальные служебные типы сообщений, вроде ping или heartbeat, которые пересылаются по сети в обе стороны с заданным промежутком времени.
Для этого есть стандартный механизм — установить SO_KEEPALIVE. Сокеты будут обмениваться служебными пакетами прозрачно для логики апликации. К сожалению, в Java нет возможности установить время SO_KEEPALIVE (берется дефолтное для операционки, которое по RFC должно быть не менее 2х часов, что естественно много). Поэтому ping с обеих сторон будет наилучшим решением. Причем ответный pong реализовывать не обязательно. Сокет закроется сам, если доставить сообщение невозможно. Манипулируя SocketTimeout можно добиться желаемой точности.
Важно понимать, что TCP-протокол не гарантирует доставки данных ни уведомления о получении или ошибке. Он лишь гарантирует, что то, что было получено, валидно. В общем случае практически невозможно синхронизоровать состояния обеих сторон (например при выполнении транзакции).
И еще одно. Если Вы пытаетесь сделать соединение comet или websocket через HTTP, и будете использовать это для «широкого клиента», то добро пожаловать в ад. 90% клиентов выходят через proxy, которые могут творить с http-траффиком все, что им угодно. Мало будет использовать comet-библиотеку с heartbeat-ом. Необходимо будет писать свой адаптивный леер, который подбирает доступные методы соединения и настройки. И главное — это обеспечить прозрачный автоматический reconnect при обрыве соединения.
> Соединение не пересылающее никаких данных закрывается через определенный промежуток времени.
Причем в этом случае ни клиент ни сервер не получают нотификации и остаются висеть на Socket.read(). Время idle-таймаута зависит от настроек роутеров.
> Соединение не пересылающее никаких данных может превратиться в черную дыру (black hole), которая проявляется таким образом, что вы посылаете сообщения, они успешно уходят
Не совсем так. Посылаемые сообщения ставятся в WriteBuffer. Если данные успешно не отправились за время socket timeout (по дефолту 20-30 секунд на разных ОС), то возникает exception. Успешная отправка означает, что для отправляемых фреймов пришло TCP-подтверждение (т.е. данные успешно получены другой стороной). Именно запись данных в сокет гарантирует проверку состояния соединения (хотя и не сразу).
— Соединение зависит не только от настроек оборудования на стороне сервера. Клиент тоже может находится за файрволлом и проксями и иметь свои настройки, которые могут сильно варьироваться.
— Если одна из сторон оборвала соединение, то вовсе не обязательно, что об этом узнает другая (одна из основных ошибок). Если вы просто отключите роутер или выдерните сетевой кабель, то апликация продолжит висеть на Socket.read(). Даже если сетевой интерфейс упал, апликация не получит уведомления в виде эксепшна (странное довольно поведение). И так будет до тех пор, пока вы не сделаете write().
> Обычно этого добиваются, вводя специальные служебные типы сообщений, вроде ping или heartbeat, которые пересылаются по сети в обе стороны с заданным промежутком времени.
Для этого есть стандартный механизм — установить SO_KEEPALIVE. Сокеты будут обмениваться служебными пакетами прозрачно для логики апликации. К сожалению, в Java нет возможности установить время SO_KEEPALIVE (берется дефолтное для операционки, которое по RFC должно быть не менее 2х часов, что естественно много). Поэтому ping с обеих сторон будет наилучшим решением. Причем ответный pong реализовывать не обязательно. Сокет закроется сам, если доставить сообщение невозможно. Манипулируя SocketTimeout можно добиться желаемой точности.
Важно понимать, что TCP-протокол не гарантирует доставки данных ни уведомления о получении или ошибке. Он лишь гарантирует, что то, что было получено, валидно. В общем случае практически невозможно синхронизоровать состояния обеих сторон (например при выполнении транзакции).
И еще одно. Если Вы пытаетесь сделать соединение comet или websocket через HTTP, и будете использовать это для «широкого клиента», то добро пожаловать в ад. 90% клиентов выходят через proxy, которые могут творить с http-траффиком все, что им угодно. Мало будет использовать comet-библиотеку с heartbeat-ом. Необходимо будет писать свой адаптивный леер, который подбирает доступные методы соединения и настройки. И главное — это обеспечить прозрачный автоматический reconnect при обрыве соединения.
Если вы просто отключите роутер или выдерните сетевой кабель, то апликация продолжит висеть на Socket.read(). Даже если сетевой интерфейс упал, апликация не получит уведомления в виде эксепшна (странное довольно поведение).
Для этого есть стандартный механизм — установить SO_KEEPALIVE
Странно, а SO_TIMEOUT разве не отвечает за таймаут на блокирующее чтение из сокета?
Sign up to leave a comment.
Разработка распределенного приложения, часть компонентов которого находится за firewall