【问题标题】:Issues with implementing a network server with SocketServer使用 SocketServer 实现网络服务器的问题
【发布时间】:2010-10-02 13:16:58
【问题描述】:

我是套接字编程的初学者,需要您的帮助。我已经使用 Python 文档中的 ThreadedTCPServer 示例实现了简单的回显服务器。它工作正常,但我有以下问题:

  1. 当客户端尝试发送零长度数据时,服务器挂起(在 socket.recv 中)。
  2. 当客户端发送的数据是 BUF_SIZE 的倍数时,服务器挂起(在 socket.recv 中)。
  3. 我不知道从外部停止服务器的正确方法。我想要一个脚本,例如 stopServer.py ,它可以在需要停止服务器时从服务器的主机启动。我已经实现了发送到服务器端口的“STOP”命令。这种方法对我来说看起来不错,但存在安全风险。请注意,我需要一个跨平台的解决方案。因此,信号可能不合适。

如果您对如何解决上述问题有任何想法,我们将不胜感激。 示例代码如下。

祝你有美好的一天! 扎哈尔

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 ()

server.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

【问题讨论】:

    标签: python sockets socketserver


    【解决方案1】:

    我不懂python,但我会根据我的套接字编程知识尝试回答。

    零字节消息不会导致“sock.recv”出现。请参阅here 以获得解释。如果 sock.recv 返回长度为 0,说明对方已经断开 TCP 连接。

    if len (buf) < BUF_SIZE
    

    这一行如何确定所有数据已被读取。 TCP recv 调用不是 TCP send 调用之间的 1-1 映射,即客户端在 1 个 send 调用中发送的内容可以在多个 recv 调用中接收。

    当您发送多个 BUF_SIZE(即 2 * BUF_SIZE)时,您可能会一次性收到所有这些。那时,您的服务器将挂起为len(buf) &gt; BUF_SIZE,并且您将卡在 sock.recv 中。尝试并弄清楚如何使用非阻塞套接字。

    【讨论】:

    • 感谢您的回复。关于“if len (buf) BUF_SIZE”的情况是不可能的。有关详细信息,请参阅 socket.recv python 文档。
    • recv 调用通常返回受 BUF_SIZE 限制的套接字上的任何可用数据(在您的情况下)。正如我之前所说,在 TCP 中,如果您在一次接收中发送 BUF_SIZE 数据,则不能保证您会通过一次接收调用来接收它们。
    【解决方案2】:

    经过一些实验,我发现了以下内容:

    1. 需要将 common.recv 重命名为 common.recvall
    2. 需要在common.recv(bug)中注释以下2行:

      if len (buf) < BUF_SIZE : # all data have been read
      break
      
    3. 只有当发送到服务器的数据可以被服务器端的 1 个 socket.recv 调用读取时,此示例才有效。所以,发送的数据必须小于BUF_SIZE。否则,客户端和服务器在他们的 sock.recv 调用中都处于死锁状态。这是因为对共享资源(套接字)的并发访问。为了解决这个问题,客户端必须在不同的端口上监听回复。

    【讨论】:

      猜你喜欢
      • 2017-08-30
      • 1970-01-01
      • 2016-11-08
      • 2021-10-04
      • 1970-01-01
      • 2013-03-12
      • 2012-12-20
      • 1970-01-01
      相关资源
      最近更新 更多