如何在Python中拆分从套接字接收到的数据?

0 python sockets client-server

我正在尝试用 Python 编写我的第一个客户端-服务器应用程序。我的客户端向服务器端发送不同的命令。例如,其中之一是用于在命令 shell 中执行命令。看起来像:

client.send("execute".encode("utf-8"))
client.send(command.encode("utf-8"))
Run Code Online (Sandbox Code Playgroud)

在服务器端,我尝试以这种方式接收它:

data = client.recv(BUFF_SIZE).decode("utf-8").strip()
if data == "execute":
    command = client.recv(BUFF_SIZE).decode("utf-8").strip()
    ...
Run Code Online (Sandbox Code Playgroud)

但我在“data”变量中得到“execute{command}”字符串,并且 ==“execute”条件未满足。我是客户端-服务器应用程序的新手,不知道如何正确执行它。我做错了什么?

小智 5

要认识到 TCP 是字节流。从 TCP 获得的保证是您发送的字节将以相同的顺序到达。如果字节流表示命令序列,则无法保证字节将以与您发送的内容对齐的块形式到达。您可以发送:“execute”、“command”并接收“e”“xecuteco”“mmand”。

(是的,这是极不可能的,而由于 nagle 算法,接收“executecommand”的可能性非常大,但我离题了。重点是,为了编写健壮的代码,你不应该假设 TCP 如何将数据分解成碎片)

因此,您需要决定的第一件事是如何将请求字节流划分为请求,以及如何将响应字节流划分为响应。在请求中,您需要决定其内部结构。

假设您确定请求如下所示:“verb param1 param2...paramN\n” 即:

  1. 请求是一系列非换行字节,后跟换行符
  2. 该请求由一个初始动词(非空格字符)组成,后跟零个或多个参数

由于协议本身现在在 TCP 上有一个附加层,因此最好使用抽象对其进行编码。类似于:

class Request(object):
    def __init__(self, verb, *args):
        self.verb = verb
        self.args = [str(x) for x in args]

class Client(object):
    def __init__(self, sock):
        self.sock = sock
        self.rxbuf = ''

    def send_request(self, req):
        req_str = req.verb
        if req.args:
            req_str += ' ' + ' '.join(req.args)
        req_str += '\n'
        self.sock.sendall(req_str.encode("utf-8"))

class Server(object):
    def __init__(self, sock):
        self.sock = sock
        self.rxbuf = ''
    def read_request(self):
        while True:
            s = self.rxbuf.split('\n', 1)
            if len(s) == 2:
                req_str = s[0]
                self.rxbuf = s[1]
                req_lst = req_str.split(' ')
                return Request(req_lst[0], *req_lst[1:])
            data = self.sock.recv(BUF_SIZE).decode("utf-8")
            self.rxbuf += data
Run Code Online (Sandbox Code Playgroud)

当然,这必须通过决定响应的外观以及如何将传入的字节流描述为响应序列来补充。我试图用这段代码表达的要点是

  1. 您读取字节
  2. 你积累它们
  3. 尝试看看您目前收到的内容是否是完整的请求
  4. 如果是 - 分析一下,剩下的留到下次

假设请求相当小,并且您不必流式传输它们,这是一个更高级的主题。