Обнаружение отключения клиента TCP
допустим я простой сервер accept()ed соединение от клиента.
Как лучше всего сказать, когда клиент отключился? Обычно клиент должен отправить команду закрытия, но что делать, если он отключается вручную или полностью теряет сетевое соединение? Как сервер может обнаружить или обработать это?
13 ответов:
select (с установленной маской чтения) возвратится с сигнализированным дескриптором, но когда вы используете ioctl* для проверки количества байтов, ожидающих чтения, оно будет равно нулю. Это признак того, что разъем был отключен.
Это большое обсуждение различных методов проверки того, что клиент отключился:Стивен Клири, обнаружение полуоткрытых (отброшенных) соединений.
* для Windows используйте ioctlsocket.
в TCP есть только один способ обнаружить упорядоченное разъединение, и это путем получения нуля в качестве возвращаемого значения от
read()/recv()/recvXXX()при чтении.существует только один надежный способ выявления разрыва соединения: путем записи в него. После достаточного количества записей в разорванное соединение TCP сделает достаточно повторных попыток и таймаутов, чтобы знать, что он сломан и в конечном итоге вызовет
write()/send()/sendXXX()чтобы вернуть -1 Сerrno/WSAGetLastError()стоимостьюECONNRESET,или в некоторых случаях "время ожидания соединения". Заметить что последний отличается от "тайм-аута подключения", который может произойти в фазе подключения.вы также должны установить разумный тайм-аут чтения и отбросить соединения, которые его не выполняют.
ответ здесь о
ioctl()иFIONREADконкурировать бред. Все, что делает, это говорит вам, сколько байтов в настоящее время находится в буфере приема сокета, доступном для чтения без блокировки. Если клиент не отправляет вам ничего в течение пяти минут, это не означает разъединения, но это так причинаFIONREADк нулю. Не одно и то же: даже не близко.
более подробно остановиться на этом немного подробнее:
Если вы используете сервер, вам либо нужно использовать TCP_KEEPALIVE для мониторинга клиентских подключений, либо сделать что-то подобное самостоятельно, либо иметь знания о данных/протоколе, который вы используете через соединение.
в принципе, если соединение будет убито (т. е. неправильно закрыто), то сервер не заметит, пока он не попытается написать что-то клиенту, что и делает keepalive для вас. Альтернативно, если вы знаете протокол лучше, вы можете просто отключить тайм-аут бездействия в любом случае.
Если вы используете перекрывающийся (т. е. асинхронный) ввод-вывод с подпрограммами завершения или портами завершения, вы будете немедленно уведомлены (при условии, что у вас есть выдающееся чтение), когда клиентская сторона закрывает соединение.
""" tcp_disconnect.py Echo network data test program in python. This easily translates to C & Java. A server program might want to confirm that a tcp client is still connected before it sends a data. That is, detect if its connected without reading from socket. This will demonstrate how to detect a TCP client disconnect without reading data. The method to do this: 1) select on socket as poll (no wait) 2) if no recv data waiting, then client still connected 3) if recv data waiting, the read one char using PEEK flag 4) if PEEK data len=0, then client has disconnected, otherwise its connected. Note, the peek flag will read data without removing it from tcp queue. To see it in action: 0) run this program on one computer 1) from another computer, connect via telnet port 12345, 2) type a line of data 3) wait to see it echo, 4) type another line, 5) disconnect quickly, 6) watch the program will detect the disconnect and exit. John Masinter, 17-Dec-2008 """ import socket import time import select HOST = '' # all local interfaces PORT = 12345 # port to listen # listen for new TCP connections s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.bind((HOST, PORT)) s.listen(1) # accept new conneciton conn, addr = s.accept() print 'Connected by', addr # loop reading/echoing, until client disconnects try: conn.send("Send me data, and I will echo it back after a short delay.\n") while 1: data = conn.recv(1024) # recv all data queued if not data: break # client disconnected time.sleep(3) # simulate time consuming work # below will detect if client disconnects during sleep r, w, e = select.select([conn], [], [], 0) # more data waiting? print "select: r=%s w=%s e=%s" % (r,w,e) # debug output to command line if r: # yes, data avail to read. t = conn.recv(1024, socket.MSG_PEEK) # read without remove from queue print "peek: len=%d, data=%s" % (len(t),t) # debug output if len(t)==0: # length of data peeked 0? print "Client disconnected." # client disconnected break # quit program conn.send("-->"+data) # echo only if still connected finally: conn.close()
попробуйте найти EPOLLHUP или EPOLLERR. Как проверить, что клиентское соединение все еще живо
чтение и поиск 0 будет работать в некоторых случаях, но не все.
TCP имеет" открытые "и" закрытые " процедуры в протоколе. После " открытия "соединение удерживается до"закрытия". Но есть много вещей, которые могут остановить поток данных ненормально. При этом методы определения возможности использования связи сильно зависят от уровней программного обеспечения между протоколом и прикладной программой. Те, которые упомянуты выше, сосредоточены на программисте, пытающемся использовать сокет неинвазивным способом (чтение или запись 0 байт), возможно самые распространенные. Некоторые слои в библиотеках будут поставлять "опрос" для программиста. Например, вызовы Win32 asych (delayed) могут начать чтение, которое вернется без ошибок и 0 байтов, чтобы сигнализировать сокет, который больше не может быть прочитан (предположительно процедура TCP FIN). В других средах могут использоваться "события", определенные в их слоях обертывания. На этот вопрос нет однозначного ответа. Механизм определения того, когда сокет не может быть использован и должен быть закрыт, зависит от обертки поставляется в библиотеках. Также стоит отметить, что сами сокеты могут быть повторно использованы слоями ниже библиотеки приложений, поэтому разумно выяснить, как ваша среда работает с интерфейсом сокетов Berkley.
возвращаемое значение будет -1, если связь теряется, то это будет размер буфера.
void ReceiveStream(void *threadid) { while(true) { while(ch==0) { char buffer[1024]; int newData; newData = recv(thisSocket, buffer, sizeof(buffer), 0); if(newData>=0) { std::cout << buffer << std::endl; } else { std::cout << "Client disconnected" << std::endl; if (thisSocket) { #ifdef WIN32 closesocket(thisSocket); WSACleanup(); #endif #ifdef LINUX close(thisSocket); #endif } break; } } ch = 1; StartSocket(); } }
apr библиотека из проекта apache является хорошим справочником для этой проблемы. Он использует опрос со значением тайм-аута, чтобы проверить, нарушено ли соединение с другой стороны.
Я играл с несколькими решениями, но этот, кажется, лучше всего подходит для обнаружения отключения хоста и/или клиента в Windows. Он предназначен для неблокирующих сокетов и получен из пример IBM.
char buf; int length=recv(socket, &buf, 0, 0); int nError=WSAGetLastError(); if(nError!=WSAEWOULDBLOCK&&nError!=0){ return 0; } if (nError==0){ if (length==0) return 0; }
это действительно легко сделать: надежный и не грязный:
Try Clients.Client.Send(BufferByte) Catch verror As Exception BufferString = verror.ToString End Try If BufferString <> "" Then EventLog.Text &= "User disconnected: " + vbNewLine Clients.Close() End If
мы запускаем в аналогичной проблеме при обнаружении удаления кабеля на ПК была проблема. После поиска в Google мы попали в библиотеку SuperCom для TCP, которая предлагала эту функцию и очень надежную библиотеку передачи данных, которая также могла обрабатывать отчетные события, когда соединение было закрыто.
этот вопрос основан на различных заблуждениях о сетевом стеке.
во-первых, нет понятия "соединение" Tcp или иначе, и чем раньше вы сделаете это различие, тем раньше вы поймете, что это не отличается от udp или любого другого протокола.
когда время приема истекает, у вас либо есть данные для отправки, либо нет.
Если вы не хотите передавать, чем, дополнительно увеличить тайм-аут и получить снова.
Если клиент отключил компьютер или иным образом отключен, то как или почему это имеет значение?
Это собирается разорвать вашу "связь"? Может быть, но, возможно, нет, кто знает, как ваша машина настроена или LSP такого.
обычный способ сделать это так же, как и с любыми другими протоколами, и я напоминаю вам, что сокет уже "подключен" в случаях, когда происходит recive.
когда вы пытаетесь отправить этот клиент что-то вы будете знайте, что клиент "подключен" или более правильно подтвердил ваши данные по Tcp. (Под udp также в зависимости от сети через icmp)
Почему вы проголосовали за этот ответ из-за вашего собственного непонимания.
короче говоря, если у вас есть данные для отправки, отправьте их и определите, какое действие требуется после успешной или неудачной отправки.
Если время ожидания операции приема совпадает, и при необходимости вы можете использовать отправку размера 0 для подтверждения другой стороны все еще получает ваши данные.
Comments