pythonでselectを使ってechoサーバーを書いています。

サーバーに接続しているクライアントのソケットが切断された場合、サーバー側で直ちにそれを感知することはできるのでしょうか?

回答の条件
  • 1人2回まで
  • 登録:
  • 終了:2010/09/11 01:54:56
※ 有料アンケート・ポイント付き質問機能は2023年2月28日に終了しました。

ベストアンサー

id:longicorn No.1

回答回数56ベストアンサー獲得回数6

ポイント100pt

Pythonの問題じゃあなくてネットワークプログラミングの基本です。


参考までに

http://www.kt.rim.or.jp/~ksk/sock-faq/unix-socket-faq-ja-2.html#...


ひどいプログラムだけどとりあえず確認は出きるはずです。

サンプルの大本はこちらです。

http://www.tutorialspoint.com/python/python_networking.htm


server.py

#!/usr/bin/env python

import socket

s = socket.socket()
host = socket.gethostname()
port = 12345
s.bind((host, port))

s.listen(5)
while True:
        c, addr = s.accept()
        r = c.recv(1024)
        print r
        print len(r)
        r = c.recv(1024)
        print r
        print len(r)

client.py

#!/usr/bin/env python

import socket

s = socket.socket()
host = socket.gethostname()
port = 12345

s.connect((host, port))
print s.send("abcdefg")
s.close

片方のシェルで先にserver.pyを動かします。

$ ./server.py

次に別のシェルでclient.pyを動かします

$ ./client
7

するとサーバー側は次のようになります。

$ ./server.py
abcdefg
7


$

シェルが復帰しているので正しくプログラムが終了している事が分かります。

最後の0でクライアント側からcloseを受け取った事が分かります。


疑うならば次のようにクライアント側を修正します。

#!/usr/bin/env python

import socket
import time

s = socket.socket()
host = socket.gethostname()
port = 12345

s.connect((host, port))
print s.send("abcdefg")
while True:
        time.sleep(1)
#s.close

こうするとサーバー側は次の様に動作するはずです。

$ ./client.py
7

シェルが帰らずに s.send("abcdefg") だけを受信していることが分かるはずです。


ネットワークの挙動を本当に正しく知りたければ、難しいかも知れませんがC言語でのプログラミングをお勧めします。

いまや古い本ですがこの本が詳しいです。

Vol:2はIPCなので今回は必要ないです。

UNIXネットワークプログラミング〈Vol.1〉ネットワークAPI:ソケットとXTI

UNIXネットワークプログラミング〈Vol.1〉ネットワークAPI:ソケットとXTI

  • 作者: W.リチャード スティーヴンス
  • 出版社/メーカー: ピアソンエデュケーション
  • メディア: 単行本

id:ojosamacut

ありがとうございます。

サンプルまで載せていただきありがとうございます。大変勉強になりました。

2010/09/11 01:54:40
  • id:t-wata
    def isConnected(conn):
    (r, w, e) = select.select([conn], [], [], 0)
    if r:
    data = conn.recv(1024, socket.MSG_PEEK)
    if len(data)==0:
    print "Client disconnected."
    return False
    return True

    こんなメソッドで一応接続されてるかどうかはチェックできますよ。
  • id:longicorn
    selectは接続確認じゃあ無くて多重I/Oを実現するものなので関係ないかと。
    結局、recvで戻り値を確認しているので自分のと本質的には代わりは無いはずです。

    recvでの問題点は相手側がcloseしたときだけ分かるということです。
    もちろん普通はこれで問題無いはずですが、プログラムが複雑になるとバグで確認出来ないこともあります。
    また、通信相手側がcloseしていないからといってバグでrecv/send等を行えない状態になることも考慮に入れる必要があります。

    こういう場合は自前でKeepAliveを行います。
    つまり定期的に通信を行うことです。
    例えば1sec以内に送受信がなければ、1バイトでもよいので通信を行いお互いが送受信をしているので相手側のプログラムは生きている、と判断するわけです。
  • id:t-wata
    selectを使ったのはblockされたくないからです。
    まぁ本質は同じですが、ノンブロッキングで、MSG_PEEKを使ってキューに残したままにしているので、いつ、何回呼んでも大丈夫にしてます。

    > recvでの問題点は相手側がcloseしたときだけ分かるということです。

    たしかにこれは問題ですね。相手がネットワークケーブルを抜いたり、OSがハングしていたりした場合は正しく検出できません。
    これを直ちに検出する方法は無いですね。

    > recvでの問題点は相手側がcloseしたときだけ分かるということです。

    これの問題点は、TCPがメッセージ単位通信ではないストリーム通信だと言う点です。
    自前KeepAliveのためには、クライアントソケットをハンドルするスレッドとは別のスレッドが定期的に行う必要が出てきますが(複雑な計算やブロッキングなどにより定期的にメッセージ送受信できるとは限らないから)、
    プロトコルをうまく定義し、且つ、2つのスレッド間で同期をとったり、メッセージの混入やキューからのKeepAlive用メッセージだけを削除する、など実装はかなり困難です。
    特にストリームだとKeepAlive用通信とアプリケーション用通信を分離するのが困難です(UDPのようにメッセージ単位でやりとりできたり、SCTPのように複数のストリームが使えれば問題無いんですが)。
    求められる要件にもよりますが、設計/実装の困難さとそれによって救われるケースを天秤にかけると、自前KeepAliveはあまり現実的では無いように思います。
  • id:longicorn
    >selectを使ったのはblockされたくないからです。
    あ、もちろん分かっていますよ。
    ただ、今回の質問に対しては本質的に関係ないよということが言いたかっただけです。

    >求められる要件にもよりますが、設計/実装の困難さとそれによって救われるケースを天秤にかけると、自前KeepAliveはあまり現実的では無いように思います。
    まあ、確かにそこですよね。

    自分が以前した仕事の場合は、リアルタイム通信でなおかつcloseが取れないといった場合が多発するので自前KeepAliveが必要でした。
    まあ、今回の質問者の場合は必要ないかもですね。

    これ以上は本質問からはかけ離れていくのでこれ以上のコメントは控えさせて頂きます。

    関係ないけどコメントではてな記法が使えないのは痛いですね。
    特にPythonのようなインデントが意味のある言語の場合は。

この質問への反応(ブックマークコメント)

トラックバック

  • http://q.hatena.ne.jp/1284128151 コメントにも書いたけどこれって難しい。 基本は回答した通りcloseしたら0バイトの受信が発生するだけ。 けれど現実のプログラムでは正しく通信を正しくできるか
「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

回答リクエストを送信したユーザーはいません