Как уже упоминалось в комментариях к вашему вопросу, материал TCP_KEEPALIVE
не будет иметь никакого значения для вашего варианта использования.TCP_KEEPALIVE
- это механизм уведомления программы, когда одноранговый узел на другой стороне его TCP-соединения исчезает в противном случае простаивающего TCP-соединения .Поскольку вы регулярно отправляете данные по TCP-соединению (ям), функциональность TCP_KEEPALIVE
никогда не вызывается (или не требуется), потому что сама по себе отправка данных по соединению уже достаточна, чтобы стек TCP распознавал как можно скореекогда удаленный клиент ушел.
Тем не менее, я изменил / упростил ваш пример серверного кода, чтобы он работал (как можно более правильно) на моей машине (Mac, FWIW).Я сделал следующее:
Переместил socket.setsockopt(SO_REUSEADDR)
до строки bind()
, чтобы bind()
не завершился ошибкой после того, как вы убьете и перезапустите программу.
Изменен вызов select()
для отслеживания доступных для записи сокетов.
Добавлена обработка исключений для вызовов send()
.
Перемещен код удаления сокета из списков в отдельную функцию RemoveSocketFromLists()
, чтобы избежать избыточного кода
Обратите внимание, что ожидаемое поведение для TCPчто если вы аккуратно выйдете из клиента (например, с помощью control-C'ing, или убьете его через диспетчер задач, или иным образом заставите его выйти таким образом, чтобы его стек TCP хоста все еще мог связываться с сервером, чтобы сообщитьсервер, который клиент мертв), то сервер должен более или менее немедленно распознать мертвого клиента.
Если, с другой стороны, подключение клиента к сети внезапно отключается (например, из-за того, что кто-то выдернул Ethernet клиента).или кабель питания) тогда яПрограмма сервера может занять несколько минут, чтобы обнаружить, что клиент ушел, и это ожидаемое поведение, поскольку у сервера нет возможности сказать (в этой ситуации), мертв ли клиент или нет.(т.е. он не хочет разрывать жизнеспособное TCP-соединение просто потому, что маршрутизатор отбросил несколько TCP-пакетов, что вызывает временное прерывание связи с еще живым клиентом)
Если вы хотите попытаться отброситьклиенты быстро в этом сценарии, вы можете попытаться потребовать от клиентов send()
фиктивных данных на сервер каждую секунду или около того.Сервер может отслеживать метку времени, когда он последний раз получал какие-либо данные от каждого клиента, и принудительно закрывать любые клиенты, от которых он не получал никаких данных, в «слишком длинном» (для чего бы вы ни думали о слишком длинном).Это более или менее сработает, хотя при этом вы рискуете получить ложные срабатывания (т. Е. Отбросить клиентов, которые все еще живы, просто медленны или страдают от потери пакетов), если вы установите слишком низкий порог времени ожидания.