【问题标题】:How to split received data from socket in Python?如何在 Python 中从套接字中拆分接收到的数据?
【发布时间】:2017-08-11 01:37:26
【问题描述】:

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

client.send("execute".encode("utf-8"))
client.send(command.encode("utf-8"))

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

data = client.recv(BUFF_SIZE).decode("utf-8").strip()
if data == "execute":
    command = client.recv(BUFF_SIZE).decode("utf-8").strip()
    ...

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

【问题讨论】:

  • @3D1T0R 在这种情况下,我得到一个字符串“execute\n{command}”,但仍在同一个“数据”变量中。

标签: python sockets client-server


【解决方案1】:

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

(是的,这是极不可能的,而接收“executecommand”是极有可能的,因为 nagle 算法,但我离题了。关键是,为了编写健壮的代码,你不要假设 TCP 是如何破坏你的数据分片)

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

假设您决定请求如下所示: “动词 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

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

  1. 你读取字节
  2. 你积累它们
  3. 试着看看你目前得到的东西是否是一个完整的请求
  4. 如果是 - 分析一下,剩下的留到下一次

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

【讨论】:

  • 非常感谢!我看到你是一个知识渊博的人,你能在另一个问题上帮助或指导我吗?为了客户端和服务器,双方都必须打开必要的端口。除了手动打开,没有别的办法了吗?
【解决方案2】:

您可以通过将服务器代码上的 if 语句更改为:

if data.split("{")[0] == "execute"

解释:

data = "execute{command}"
print data.split("{")[0]

打印

"execute"

由于数据是字符串,所以可以使用split方法将字符串拆分成列表,使用“{”字符。

所以:

data.split("{")

会回来

["execute", "commmand}"]

因为你想要“执行”,所以你在列表中取索引 0

【讨论】:

  • 不不,我的意思是,我想将这两条消息分别接收到不同的变量中。我的问题是 recv 函数将两条消息都放在一个“数据”中。
  • BUFF_SIZE 设置为多少?
  • 设置为1024字节
  • 您的字节大小可能太大了。使用 sys.getsizeof("execute") 获取字符串的实际字节大小。见这篇文章:stackoverflow.com/questions/4013230/…
  • 我想实现一个接口,客户端先发送包名,然后发送“包”的正文。例如,我想上传一个文件。首先,我对服务器说“upload_file”,然后它知道它应该获取文件名及其内容。它获取名称“包”和不同变量的以下数据。因此,包含“包名”的字符串可以是不同的值:“执行”、“下载”、“任何东西”...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-07-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-16
  • 2013-09-30
  • 1970-01-01
相关资源
最近更新 更多