web-dev-qa-db-ja.com

検出TCPクライアント切断

単純なサーバーを実行していて、クライアントからのaccept() edの接続があるとします。

クライアントが切断されたことを知る最良の方法は何ですか?通常、クライアントはcloseコマンドを送信することになっていますが、手動で切断するか、ネットワーク接続を完全に失うとどうなりますか?サーバーはこれをどのように検出または処理できますか?

67
Zxaos

select(読み取りマスクが設定されている)はハンドルが通知されて戻りますが、ioctl *を使用して読み取り待ちバイト数をチェックすると、ゼロになります。これは、ソケットが切断されたことを示しています。

これは、クライアントが切断されたことを確認するさまざまな方法に関する素晴らしい議論です。 Stephen Cleary、ハーフオープン(ドロップ)接続の検出

* Windowsの場合はioctlsocketを使用します。

1
sep

TCPでは、正常な切断を検出する方法は1つしかありません。つまり、読み取り時にread()/recv()/recvXXX()からの戻り値としてゼロを取得する方法です。

破損した接続を検出するための唯一の信頼できる方法もあります。それを書き込むことです。切断された接続への十分な書き込みの後、TCPは、切断されたことを知るのに十分な再試行とタイムアウトを行い、最終的にwrite()/send()/sendXXX()errno/WSAGetLastError()ECONNRESET,の値、または場合によっては「接続がタイムアウトしました」後者は、接続フェーズで発生する可能性がある「接続タイムアウト」とは異なることに注意してください。

また、適切な読み取りタイムアウトを設定し、失敗した接続をドロップする必要があります。

ioctl()FIONREADについてのここでの答えは、まったく無意味です。ソケット受信バッファに現在何バイトがあり、ブロックせずに読み取ることができるかがわかります。クライアントが切断を構成しない5分間何も送信しないが、FIONREADがゼロになる場合。同じことではありません:近くさえありません。

100
user207421

これをもう少し拡張するには:

サーバーを実行している場合は、TCP_KEEPALIVEを使用してクライアント接続を監視するか、同様の操作を行うか、接続で実行しているデータ/プロトコルに関する知識が必要です。

基本的に、接続が切断された(つまり、適切に閉じられなかった)場合、サーバーはクライアントに何かを書き込もうとするまで気付きません。これがキープアライブによって実現されます。または、プロトコルをよく知っている場合は、とにかく非アクティブタイムアウトで切断することもできます。

12
Peter Jeffery

完了ルーチンまたは完了ポートでオーバーラップ(非同期)I/Oを使用している場合、クライアント側が接続を閉じるとすぐに通知されます(未処理の読み取りがあると仮定します)。

2
Graeme Perrow

TCPには、プロトコルに「開く」手順と「閉じる」手順があります。 「開く」と、接続は「閉じる」まで保持されます。しかし、データフローを異常に停止させる可能性のあるものはたくさんあります。つまり、リンクを使用できるかどうかを判断する手法は、プロトコルとアプリケーションプログラムの間のソフトウェアの層に大きく依存しています。上記の例は、非侵襲的な方法(0バイトの読み取りまたは書き込み)でソケットを使用しようとするプログラマーに焦点を当てていますが、これはおそらく最も一般的な方法です。ライブラリの一部のレイヤーは、プログラマーに「ポーリング」を提供します。たとえば、Win32 asych(遅延)呼び出しは、エラーなしで返される読み取りを開始し、0バイトがこれ以上読み取れないソケットを通知します(おそらくTCP FINプロシージャ)。ラッピング層で定義された「イベント」を使用するかもしれません。この質問に対する単一の答えはありません。ソケットを使用できず、閉じる必要があることを検出するメカニズムは、ライブラリで提供されるラッパーに依存します。ソケット自体はアプリケーションライブラリの下のレイヤーで再利用できるため、環境がBerkley Socketsインターフェイスをどのように処理するかを理解するのが賢明です。

1
jlpayton
"""
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()
0
John Masinter

EPOLLHUPまたはEPOLLERRを探してみてください。 クライアント接続がまだ生きていることを確認する方法

0の読み取りと検索は一部のケースでは機能しますが、すべてではありません。

0

接続が失われた場合、receiveの戻り値は-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();
    }
}
0
Anand Paul