【问题标题】:Python Socket is receiving inconsistent messages from ServerPython Socket 正在从服务器接收不一致的消息
【发布时间】:2016-04-28 09:54:39
【问题描述】:

所以我对网络非常陌生,我使用 Python Socket 库连接到正在传输位置数据流的服务器。

这是使用的代码。

import socket

BUFFER_SIZE = 1024
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((gump.gatech.edu, 756))

try:
    while (1):
        data = s.recv(BUFFER_SIZE).decode('utf-8')
        print(data)
except KeyboardInterrupt:
    s.close()

问题在于数据以不一致的形式到达。

大多数时候它以这样的正确形式到达:

2016-01-21 22:40:07,441,-84.404153,33.778685,5,3

但有时它会像这样分成两行到达:

2016-01-21

22:40:07,404,-84.396004,33.778085,0,0

有趣的是,当我使用 Putty 建立与服务器的原始连接时,我只能得到正确的形式,而不是拆分。所以我想一定是发生了什么事情正在分裂信息。或者腻子正在做的事情总是正确地组装它。

我需要的是变量data 始终包含正确的行。知道如何做到这一点吗?

【问题讨论】:

  • 它发生在 tcp 和套接字上。理论上它可以一次到达一个字节。您的代码需要再次将其缝合在一起。至于实现这一点的方法,您可以将标头合并到数据包中。如果您将前 2 或 4 个字节作为数据的长度,那么重新组合起来应该很简单。
  • 在此处查看答案,详细了解为什么会发生这种情况:stackoverflow.com/a/1716173/2372812

标签: python sockets networking tcp client


【解决方案1】:

最好将套接字视为一个连续的数据流,它可能会以点点滴滴的形式到达,也可能会泛滥成灾。

特别是,接收者的工作是将数据分解成它应该包含的“记录”,套接字并不神奇地知道如何为你做这件事。这里的记录是行,所以你必须自己读取数据并拆分成行。

您不能保证单个recv 将是一个完整的行。可能是:

  • 只是一行的一部分;
  • 或几行;
  • 或者,很可能是几行和另一部分行。

尝试类似:(未经测试)

# we'll use this to collate partial data
data = ""

while 1:
    # receive the next batch of data
    data += s.recv(BUFFER_SIZE).decode('utf-8')

    # split the data into lines
    lines = data.splitlines(keepends=True)

    # the last of these may be a part line
    full_lines, last_line = lines[:-1], lines[-1]

    # print (or do something else!) with the full lines
    for l in full_lines:
        print(l, end="")

    # was the last line received a full line, or just half a line?
    if last_line.endswith("\n"):
        # print it (or do something else!)
        print(last_line, end="")

        # and reset our partial data to nothing
        data = ""
    else:
        # reset our partial data to this part line
        data = last_line

【讨论】:

  • 这太复杂了。只需在原始程序中将 end 参数设为空字符串即可。还要看一下endswith的论点;你错过了一个角色......
  • 这完全取决于您可能想要真正使用这些数据做什么......如果您不只是打印它,而是想要存储它以进行分析,或者通过正则表达式运行它以提取信息,那么你需要整理整行。
  • 对......我想问题的最后一行让事情变得混乱......但从他之前的陈述和他的代码来看,我认为他想要做的就是打印响应。即使他想保存这些行,使用列表也会更有效率,因为 append() 的成本是摊销的,收集块并最终在列表上调用 join()。字符串连接,尤其是当您可以获得大量响应时,效率会非常低。
  • 这是我需要的最佳解决方案!这是因为我需要将它插入数据库,这是最好的解决方案。
【解决方案2】:

修复代码的最简单方法是打印接收到的数据而不添加新行,print 语句 (Python 2) 和 print() 函数 (Python 3) 会这样做默认。像这样:

Python 2:

print data,

Python 3:

print(data, end='')

现在print 不会在每个打印值的末尾添加自己的换行符,只会打印接收到的数据中存在的换行符。结果是每一行都被打印,而不是根据每个 `socket.recv() 接收到的数据量进行拆分。例如:

from __future__ import print_function
import socket

s = socket.socket()
s.connect(('gump.gatech.edu', 756))

while True:
    data = s.recv(3).decode('utf8')
    if not data:
        break    # socket closed, all data read
    print(data, end='')

这里我使用了一个非常小的缓冲区大小 3,这有助于突出问题。

请注意,这仅解决了打印数据的 POV 问题。如果您想逐行处理数据,则需要对传入数据进行自己的缓冲,并在收到新行或套接字关闭时处理该行。

【讨论】:

  • 这非常适合打印,但我确实需要结合它。我现在想我会记录它并从这里完成它。然后使用日志文件。
【解决方案3】:

编辑socket.recv() 正在阻塞,就像其他人说的那样,每次调用该方法时都不会得到确切的行。因此,套接字正在等待数据,获取它可以获取的内容然后返回。当你打印这个时,由于 pythons 默认的 end 参数,你可能会得到比你预期更多的换行符。因此,要从您的服务器获取原始内容,请使用以下命令:

import socket 
BUFFER_SIZE = 1024 
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('gump.gatech.edu', 756)) 
try: 
    while (1):   
        data=s.recv(BUFFER_SIZE).decode('utf-8')
        if not data: break
        print(data, end="") 
except KeyboardInterrupt: 
    s.close()

【讨论】:

  • 与此答案相反,socket.recv 是阻塞的,除非套接字已明确设置为非阻塞,或已关闭,或存在信号中断。此外,此答案未解决正在接收的部分行。
  • 错了。 a) socket.recv is 默认阻塞,b) socket.recv 在连接关闭时返回空字符串。 c) 一个空字符串 is 为空。
  • @donkopotamus 对。它的阻塞,我将删除它。但是,如果它是非阻塞的,我的回答确实解释了为什么会有部分行。
  • @mhawke 我说的是,如果你用 print 函数打印一个空字符串,你得到的是一个换行符,而不是你期望的“空”
  • @TisteAndii:你已经解决了我的反对意见,但是你添加了一个无限循环作为一个新问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-27
  • 2021-03-30
  • 2021-03-21
  • 1970-01-01
  • 2021-03-05
  • 2021-08-06
相关资源
最近更新 更多