Обнаружение отключения клиента TCP



допустим я простой сервер accept()ed соединение от клиента.



Как лучше всего сказать, когда клиент отключился? Обычно клиент должен отправить команду закрытия, но что делать, если он отключается вручную или полностью теряет сетевое соединение? Как сервер может обнаружить или обработать это?

1715   13  

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

    Ничего не найдено.