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

发布于 2024-09-25 19:28:26 字数 2801 浏览 12 评论 0原文

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

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

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

祝你今天过得愉快! 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

I am beginner in socket programming and need your help. I have implemented simple echo server using ThreadedTCPServer example from the Python documentation. It works fine, but I have the following problems:

  1. Server hangs (in socket.recv) when Client tries to send zero-length data.
  2. Server hangs (in socket.recv) when the data sent by Client is multiple of BUF_SIZE.
  3. I don't know the right way to stop the server from the outside. I'd like to have a script, for example, stopServer.py which can be started from the server's host when need to stop the server. I've implemented "STOP" command which is send to the server's port. This approach looks good for me, but has security risk. Note that I need a cross-platform solution. So, probably, signals are not suitable.

I will appreciate if you have any ideas how to fix the issues listed above.
The example's code is listed below.

Have a nice day!
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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

后知后觉 2024-10-02 19:28:26

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

零字节消息不会导致“sock.recv”出现。请参阅此处 进行解释。如果sock.recv返回长度为0,则表明对方已断开TCP连接。

if len (buf) < BUF_SIZE

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

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

I don't know python but I will try and answer based on my socket programming knowledge.

A zero byte message will NOT cause "sock.recv" to come out. Refer here for an explanation. If sock.recv returns with length 0, it is an indication that the other side has disconnected the TCP connection.

if len (buf) < BUF_SIZE

How does this line determine that ALL data has been read. TCP recv call is not a 1-1 mapping between TCP send call i.e. what client sends in 1 send call can be received in multiple recv calls.

When you send multiple of BUF_SIZE i.e lets say 2 * BUF_SIZE, it is possible that you might receive all of it in one go. At that point, your server would hang as len(buf) > BUF_SIZE and you would get stuck in sock.recv. Try & figure out how to use non-blocking sockets.

尾戒 2024-10-02 19:28:26

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

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

    if len (buf) < BUF_SIZE : # 所有数据已被读取
    休息
    
  3. 仅当发送到服务器的数据可以通过服务器端的 1 个 socket.recv 调用读取时,此示例才有效。因此,发送的数据必须小于BUF_SIZE。否则,客户端和服务器在 sock.recv 调用中都会陷入死锁。这是由于对共享资源(套接字)的并发访问而发生的。为了解决这个问题,客户端必须侦听不同端口上的回复。

After some experiments I found the following:

  1. Need to rename common.recv to common.recvall
  2. Need to comment the following 2 lines in common.recv (bug):

    if len (buf) < BUF_SIZE : # all data have been read
    break
    
  3. This example will work only if the data sent to the server can be read by 1 socket.recv call on the server side. So, the sent data must be less than BUF_SIZE. Otherwise, the client and server are in deadlock, both in their sock.recv call. This occurs because of the concurrent access to the shared resource (socket). To solve this the client must listen for reply on different port.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文