7
open_sockets = []

listening_socket = socket.socket( socket.AF_INET, socket.SOCK_STREAM )

listening_socket.bind( ("", 1234) )

listening_socket.listen(5)

while True:
    rlist, wlist, xlist = select.select( [listening_socket] + open_sockets, [], [] )
    for i in rlist:
        if i is listening_socket:
            new_socket, addr = listening_socket.accept()
            open_sockets.append(new_socket)
        else:
            data = i.recv(1024)
            if data == "":
                i.close()
                open_sockets.remove(i)
                print "Connection closed"
            else:
                i.send(data)
                print repr(data)

これで、これが少数のクライアントを処理できる単純なサーバー コードであることがわかりました。理解できないのは、次の 2 行だけです。

        data = i.recv(1024)
        if data == "":

クライアントが既に受け入れている場合、他のオプション、つまりバッファーに何かがあるかどうかを確認するオプションに進むことを理解しています。バッファに何もない場合、続行して行をチェックしない理由がわかりませんでした:

if data == "":

""ただし、クライアントがEnterキーを押すだけで、切断と同等になります

何も押されていないときと同じではないのはなぜ""ですか?

4

4 に答える 4

7

呼びかけから始まりselectます。この関数は、ソケットの設定を監視し、注目に値する何かが起こるのを待ちます。最初のリストのソケットの場合、「注目に値する」とは、ソケットに読み取り可能なデータがあることを意味します。

rlist, wlist, xlist = select.select( [listening_socket] + open_sockets, [], [] )

このコードは、読み取りの準備が整ったデータを使用してソケットのリストを反復処理し、処理されているソケットのタイプに基づいて動作します。

    for i in rlist:
        if i is listening_socket:

接続指向 (「リッスン」) ソケットは、新しい接続を受け入れるために使用されます。にあるので、rlist「読む」ものがあることがわかります。リッスン ソケットのコンテキストでは、これは新しい接続が受信されたことを意味します。したがって、接続を受け入れ、新しいソケットを のリストに保存しますopen_sockets

            new_socket, addr = listening_socket.accept()
            open_sockets.append(new_socket)

ソケットが でない場合はlistening_socket、リモート クライアントに接続されている (または接続されていた) ソケットです。また、 にあるのでrlist、「読み取る」ものがあることがわかります。接続されたソケットのコンテキストでは、これは、データが実際に読み取り可能であること、またはソケットが閉じられたことを意味します。

recv利用可能なデータを取得するために呼び出します。

        else:
            data = i.recv(1024)

実際に何かを読んだかどうかを確認します。利用可能なデータがない場合は、接続が閉じられているはずなので、ソケット オブジェクトを閉じて から削除しopen_socketsます。

            if data == "":
                i.close()
                open_sockets.remove(i)
                print "Connection closed"

実際にデータを受信した場合は、それをクライアントに書き戻して画面に出力するだけです。

            else:
                i.send(data)
                print repr(data)

への最初の呼び出しselectは、接続が受信されるまで待機します。コードを次のように更新することで、自分の目で確認できます。

print "About to call select"
rlist, wlist, xlist = select.select( [listening_socket] + open_sockets, [], [] )
print "Returned from select"

最初の呼び出しの後、rlistが含まれますlistening_socketopen_socketsは空であり、何かが読み取られて「読み取り」になるまで、呼び出されたselectままに戻りません。そのため、新しい接続を受け入れて に追加しopen_socketsます。

select再度呼び出されると、3 つのイベントが発生する可能性があります。まず、listening_socket別の接続を受信した可能性があります。この場合、以前と同じように処理されます。接続を受け入れて に追加しopen_socketsます。

次に、新しい接続がデータを受信した可能性があります。selectに含まれているためrlist、ソケットから「読み取る」準備ができているデータがあることがわかります (データを読み取る準備ができているか、ソケットが閉じられていることを意味します)。i.recv新しいデータを返します。

3 番目に、新しい接続が閉じられた可能性があります。selectに含まれているためrlist、ソケットから読み取る準備ができているデータがあることがわかります (上記と同じ意味で)。ただしi.recv、ソケットには新しいデータがないため、"" が返されます。したがって、ソケットが閉じられていることがわかり、それに応じてクリーンアップします。

クライアントからデータが送信されない (そして接続がまだ開いている) 場合、 はそのデータを にselect含めませんrlist。したがって、ループはそれを処理せi.recvず、その特定のソケットで呼び出されません。

于 2012-12-07T22:45:51.523 に答える
3

ソケットが応答で送信する場合""、それは通常、ソケットが閉じられている (またはシャットダウンされている?) ことを意味します。ここで間違っている場合は、誰かが私を修正してください。このステートメントがないと、リモート サーバーが突然応答を停止した場合に、無限ループに陥る可能性があります。

于 2012-12-07T22:43:32.580 に答える
0

i(ところで、ソケットの不幸な名前) は、rlist何かを読み取るものがない限り、つまり、何かi.recv(1024)を返すか、接続が完了した場合 (つまり、を返します) に含まれません。i.recv(1024)b""

.recv()返されるb""と、このソケットからは何も受け取りません。

「クライアント(人間)がEnterを押すだけ」の解釈は、クライアント(ソフトウェア)に依存します。サーバーとは何の関係もありません。たとえば、クライアントは、改行が発生するかタイムアウトが発生するまで入力文字列をバッファリングしたり、ユーザーから受信するとすぐに各バイトを送信したりできます。

于 2012-12-08T01:06:17.137 に答える