У меня возникли проблемы с API сокетов Java. Я пытаюсь отобразить количество игроков, подключенных к моей игре в данный момент. Легко определить, когда игрок подключился. Однако кажется излишне сложным определить, когда игрок отключился, используя API сокетов.
Вызов isConnected()
сокета, который был отключен удаленно, всегда возвращается true
. Точно так же вызов isClosed()
сокета, который был удаленно закрыт, всегда кажется возвращаемым false
. Я читал, что для фактического определения того, был ли сокет закрыт, данные должны быть записаны в выходной поток и должно быть обнаружено исключение. Это кажется действительно нечистым способом справиться с этой ситуацией. Нам просто пришлось бы постоянно рассылать по сети мусорное сообщение, чтобы узнать, когда сокет закрылся.
Есть ли другое решение?
источник
0
вместо-1
.Обычной практикой в различных протоколах обмена сообщениями является поддержание пульса друг друга (продолжение отправки пакетов ping), пакет не должен быть очень большим. Механизм зондирования позволит вам обнаружить отключенного клиента даже до того, как TCP выяснит это в целом (таймаут TCP намного больше). Отправьте зонд и подождите, скажем, 5 секунд для ответа, если вы не видите ответа, скажем, 2-3 при последующих проверках ваш плеер отключается.
Также связанный вопрос
источник
Я вижу другой только что опубликованный ответ, но я думаю, что вы взаимодействуете с клиентами, играющими в вашу игру, поэтому я могу предложить другой подход (в то время как BufferedReader определенно действителен в некоторых случаях).
Если бы вы хотели ... вы могли бы делегировать ответственность за "регистрацию" клиенту. Т.е. у вас будет набор подключенных пользователей с отметкой времени на последнем сообщении, полученном от каждого ... если время ожидания клиента истечет, вы принудительно перерегистрируете клиента, но это приводит к приведенной ниже цитате и идее.
Если ваш Java-код не закрыл / не отключил Socket, то как еще вы узнали бы, что удаленный хост закрыл ваше соединение? В конечном счете, ваш try / catch делает примерно то же самое, что и опросчик, ожидающий событий в сокете ACTUAL. Обратите внимание на следующее:
Я думаю, что одна из особенностей абстрактных языков заключается в том, что вы абстрагированы от мелочей. Подумайте о ключевом слове using в C # (try / finally) для SqlConnection s или чего-то еще ... это просто затраты на ведение бизнеса ... Я думаю, что try / catch / finally является принятым и необходимым шаблоном для использования Socket.
источник
Я думаю, что это природа tcp-соединений, по этим стандартам требуется около 6 минут молчания при передаче, прежде чем мы сделаем вывод, что исходящее соединение пропало! Так что я не думаю, что вы сможете найти точное решение этой проблемы. Возможно, лучший способ - написать удобный код, чтобы угадать, когда сервер должен предположить, что соединение пользователя закрыто.
источник
Поскольку @ user207421 говорит, что нет способа узнать текущее состояние соединения из-за модели архитектуры протокола TCP / IP. Таким образом, сервер должен вас заметить, прежде чем закрывать соединение, или вы проверяете это самостоятельно.
Это простой пример, который показывает, как узнать, что сокет закрыт сервером:
sockAdr = new InetSocketAddress(SERVER_HOSTNAME, SERVER_PORT); socket = new Socket(); timeout = 5000; socket.connect(sockAdr, timeout); reader = new BufferedReader(new InputStreamReader(socket.getInputStream()); while ((data = reader.readLine())!=null) log.e(TAG, "received -> " + data); log.e(TAG, "Socket closed !");
источник
Я столкнулся с подобной проблемой. В моем случае клиент должен периодически отправлять данные. Я надеюсь, что у вас такое же требование. Затем я устанавливаю SO_TIMEOUT,
socket.setSoTimeout(1000 * 60 * 5);
который генерируется по истеченииjava.net.SocketTimeoutException
указанного времени. Тогда я легко могу обнаружить мертвого клиента.источник
Вот как я с этим справляюсь
while(true) { if((receiveMessage = receiveRead.readLine()) != null ) { System.out.println("first message same :"+receiveMessage); System.out.println(receiveMessage); } else if(receiveRead.readLine()==null) { System.out.println("Client has disconected: "+sock.isClosed()); System.exit(1); } }
если result.code == null
источник
readLine()
дважды. Вы уже знаете, что вelse
блоке было null .В Linux при записи () в сокет, который другой, неизвестный вам, закрыт, вызовет сигнал / исключение SIGPIPE, как бы вы ни хотели его вызвать. Однако, если вы не хотите быть пойманным SIGPIPE, вы можете использовать send () с флагом MSG_NOSIGNAL. Вызов send () вернется с -1, и в этом случае вы можете проверить errno, который сообщит вам, что вы пытались записать сломанный канал (в данном случае сокет) со значением EPIPE, которое согласно errno.h эквивалентно 32. В качестве реакции на EPIPE вы можете вернуться назад и попытаться снова открыть сокет и попытаться отправить свою информацию еще раз.
источник
send()
Вызов возвратит -1 , только если исходящие данные получили буферные для достаточно долго для посыла таймеров истекают. Это почти наверняка не произойдет при первой отправке после отключения из-за буферизации на обоих концах и асинхронного характераsend()
под капотом.