使用SocketServer实现网络服务器的问题

时间:2010-10-02 13:16:58

标签: python sockets socketserver

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

  1. 当客户端尝试发送零长度数据时,服务器挂起(在socket.recv中)。
  2. 当客户端发送的数据是BUF_SIZE的倍数时,服务器挂起(在socket.recv中)。
  3. 我不知道从外面停止服务器的正确方法。我想要一个脚本,例如,stopServer.py,当需要停止服务器时,它可以从服务器的主机启动。我已经实现了“STOP”命令,该命令被发送到服务器的端口。这种方法对我来说很好,但存在安全风险。请注意,我需要一个跨平台的解决方案。所以,可能信号不合适。
  4. 如果您对如何解决上面列出的问题有任何想法,我将不胜感激。 示例的代码如下所示。

    祝你有愉快的一天! Zakhar

    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
    

2 个答案:

答案 0 :(得分:2)

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

零字节消息不会导致“sock.recv”出现。有关说明,请参阅here。如果sock.recv返回长度为0,则表示另一方已断开TCP连接。

if len (buf) < BUF_SIZE

此行如何确定已读取所有数据。 TCP recv呼叫不是TCP发送呼叫之间的1-1映射,即在多个recv呼叫中可以接收客户端发送的1个发送呼叫。

当您发送多个BUF_SIZE,即假设2 * BUF_SIZE时,您可能会一次性收到所有这些内容。此时,您的服务器将挂起len(buf) > BUF_SIZE,您将陷入sock.recv。尝试&amp;弄清楚如何使用非阻塞套接字。

答案 1 :(得分:0)

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

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

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