0

私はソケットプログラミングの初心者で、あなたの助けが必要です. Python ドキュメントの ThreadedTCPServer の例を使用して、単純なエコー サーバーを実装しました。正常に動作しますが、次の問題があります。

  1. クライアントが長さゼロのデータを送信しようとすると、サーバーが (socket.recv で) ハングします。
  2. クライアントから送信されたデータが BUF_SIZE の倍数である場合、サーバーが (socket.recv で) ハングします。
  3. サーバーを外部から停止する正しい方法がわかりません。サーバーを停止する必要があるときに、サーバーのホストから開始できる stopServer.py などのスクリプトが必要です。サーバーのポートに送信される「STOP」コマンドを実装しました。このアプローチは私には良さそうに見えますが、セキュリティ上のリスクがあります。クロスプラットフォーム ソリューションが必要であることに注意してください。したがって、おそらく、信号は適切ではありません。

上記の問題を修正する方法についてご意見がありましたら、よろしくお願いいたします。例のコードを以下に示します。

良い1日を!ザカール

client.py

import sys
import socket
from common import recv

if len (sys.argv) == 1 :
    print "The file to be sent is not specified"
    sys.exit (1)
fname = sys.argv [1]

print "Reading '%s' file..." % fname,
f = open (sys.argv [1])
msg = f.read ()
f.close()
print "OK"

if len (msg) == 0 :
    print "Nothing to send. Exit."
    sys.exit (0)

print "Client is sending:"
print msg
print "len (msg)=%d" % len (msg)

sock = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
sock.connect ( ('localhost', 9997) )
sock.sendall (msg)
print "Sending has been finished"
print "Waiting for response..."
response = recv (sock)
print "Client received:"
print response
print "len (response)=%d\n" % len (response)
sock.close ()

サーバー.py

import threading
import SocketServer
from common import recv

class ThreadedTCPRequestHandler (SocketServer.BaseRequestHandler):

    def handle(self) :
        recvData = recv (self.request)
        print "NEW MSG RECEIVED from %s" % self.client_address [0]
        print "Data:"
        print recvData
        print "dataSize=%d" % len (recvData)
        if recvData == "!!!exit!!!" :
            print 'Server has received exit command. Exit.'
            self.server.shutdown()
        else :
            curThread = threading.currentThread ()
            response = "%s: %s" % (curThread.getName (), recvData)
            self.request.sendall (response)

class ThreadedTCPServer (SocketServer.ThreadingMixIn, SocketServer.TCPServer) :
    pass

if __name__ == "__main__":
    HOST, PORT = "localhost", 9997

    server = ThreadedTCPServer ((HOST, PORT), ThreadedTCPRequestHandler)
    server.serve_forever ()

common.py

'''
Common code for both: client and server.
'''

def recv (sock) :
    '''
    Reads data from the specified socket and returns them to the caller.
    IMPORTANT:
    This method hangs in socket.recv () in the following situations (at least
    on Windows):
    1. If client sends zero-length data.
    2. If data sent by client is multiple of BUF_SIZE.
    '''
    BUF_SIZE = 4096
    recvData = ""
    while 1 :
        buf = sock.recv (BUF_SIZE)
        if not buf :
            break
        recvData += buf
        if len (buf) < BUF_SIZE : # all data have been read
            break
    return recvData
4

2 に答える 2

2

私はPythonを知りませんが、ソケットプログラミングの知識に基づいて答えようとします。

ゼロバイトのメッセージでは、「sock.recv」は出力されません。説明はこちらをご覧ください。sock.recvが長さ0で戻った場合は、反対側がTCP接続を切断したことを示しています。

if len (buf) < BUF_SIZE

この行は、すべてのデータが読み取られたことをどのように判断しますか。TCP recv呼び出しは、TCP送信呼び出し間の1-1マッピングではありません。つまり、クライアントが1つの送信呼び出しで送信するものは、複数のrecv呼び出しで受信できます。

BUF_SIZEの複数を送信する場合、つまり2 * BUF_SIZEを送信する場合、一度にすべてを受信する可能性があります。その時点で、サーバーはのようにハングしlen(buf) > BUF_SIZE、sock.recvでスタックします。ノンブロッキングソケットの使用方法を試してみてください。

于 2010-10-02T15:58:39.103 に答える
0

いくつかの実験の後、次のことがわかりました。

  1. common.recv の名前を common.recvall に変更する必要があります
  2. common.recv の次の 2 行をコメントする必要があります (バグ):

    if len (buf) < BUF_SIZE : # all data have been read
    break
    
  3. この例は、サーバーに送信されたデータがサーバー側で 1 回の socket.recv 呼び出しで読み取れる場合にのみ機能します。したがって、送信されるデータは BUF_SIZE 未満である必要があります。そうしないと、クライアントとサーバーの両方が sock.recv 呼び出しでデッドロック状態になります。これは、共有リソース (ソケット) への同時アクセスが原因で発生します。これを解決するには、クライアントは別のポートで応答をリッスンする必要があります。
于 2010-10-17T11:58:42.753 に答える