【问题标题】:Difference between socket.send and socket.sendall()socket.send 和 socket.sendall() 的区别
【发布时间】:2020-03-30 11:45:16
【问题描述】:

我编写了两个程序来测试服务器端的 p23_server.py 和客户端的 p23_client.py :

p23_server.py

#p23_server.py
import socket

HOST = '10.0.2.15'
PORT = 12345

server = socket.socket()
server.bind((HOST,PORT))
server.listen(1)
(client,addr) = server.accept()
while True:
    data = client.recv(32)
    if not data:
        break
    print(data.decode('utf-8'))
server.close()

p23_client.py

#p23_client.py
import socket
import sys

HOST = '10.0.2.15'
PORT = 12345

string = sys.argv[1]
data_to_send = string.encode('utf-8')


s = socket.socket()
s.connect((HOST,PORT))
#s.sendall(data_to_send)
s.send(data_to_send)
s.close()

我运行 p23_server.py 然后执行命令:

wahalez@wahalez:~/dev/python$ python p23_client.py $(python -c 'for i in range(1024): print("a",end="")')

运行客户端并查看服务器输出的内容。

我使用 socket.send() 和 socket.sendall() 函数执行了一次。 结果是一样的。问题是为什么? 不应该只发送一次数据,然后服务器收到 32 个字节,就是这样?

与 send() 不同,此方法继续从字节发送数据,直到 要么所有数据都已发送,要么发生错误。没有返回 成功。出错时会引发异常,并且没有办法 确定成功发送了多少数据(如果有)。

为什么这两个函数产生相同的结果?

【问题讨论】:

  • 虽然send保证发送所有数据,但它仍然可以在适当的情况下发送所有数据。拒绝发送所有数据即使有可能也是一种浪费。
  • 你能澄清一下“不应该只发送一次数据然后服务器接收到 32 个字节就可以了”的意思吗? sendrecv 的数据大小不是严格相关的,例如你可以一次send 64 字节和两次recv 32 字节。
  • 这正是我的意思。那么有什么区别呢?我正在尝试发送各种长度,结果仍然相同......
  • 那你发送什么“长度”?您示例中的消息是 1024 字节,可以轻松放入标准的 4096 缓冲区。请注意,消息大小可能取决于您的操作系统和硬件。
  • @Masklinn 我不想暗示它们不能更大(我知道它们经常如此)。由于它们的实际大小取决于系统,我只想提供一些(旧)默认值,它是 OP 可能使用的所有系统的下限。关键部分是 1024 太小了无法进行这样的测试。

标签: python sockets python-sockets


【解决方案1】:

文档很清楚:

socket.send(bytes[, flags])
向套接字发送数据。套接字必须连接到远程套接字。可选的 flags 参数与上面的 recv() 具有相同的含义。返回发送的字节数。应用程序负责检查所有数据是否已发送;如果仅传输了部分数据,则应用程序需要尝试传输剩余数据。有关此主题的更多信息,请参阅 Socket Programming HOWTO。

在 3.5 版中更改:如果系统调用被中断并且信号处理程序未引发异常,则该方法现在重试系统调用而不是引发 InterruptedError 异常(有关基本原理,请参阅 PEP 475)。

socket.sendall(bytes[, flags])
向套接字发送数据。套接字必须连接到远程套接字。可选的 flags 参数与上面的 recv() 具有相同的含义。与 send() 不同,此方法继续从字节发送数据,直到所有数据都已发送或发生错误。成功时不返回任何内容。出错时会引发异常,并且无法确定成功发送了多少数据(如果有)。

TL;DRsend 返回发送的字节数,可能小于请求的数量。 sendall 在成功传输所有数据时返回None,并在错误时引发异常。

【讨论】:

  • 我也检查了文档,但我还是有点困惑。如果无法确定成功发送了多少数据,.sendall( ) 可以做的似乎只是.send( ) 可以做的一部分。那么,使用socket.sendall( ) 有什么意义呢?
  • @Paw .sendall()` 在内部循环,直到发送完所有数据,如果失败,您无法确定发送了多少字节。一般来说,如果您想确保发送整个缓冲区并且不想检查返回值并像.send() 那样传输缓冲区的剩余部分,请使用.sendall()(请参阅NadavS's answer)。这是一个方便的功能。
【解决方案2】:

仅仅因为 99% 的时间 send() 将设法一次性发送所有数据。 从理论上讲,当您使用 send() 时,您可能看不到服务器上的所有数据。

为了强调区别,sendall 的伪实现示例:

def sendall(data):
    already_sent = 0
    while already_sent < len(data):
        already_sent += send(data[already_sent:])

【讨论】:

  • 澄清一下,这与数据包大小无关
  • 所以这只是理论上的?那么我应该只使用 send all 而不是 send 来确保我完整地发送数据?
  • 不错,我默认使用sendall,否则检查send的返回值。
【解决方案3】:

为什么这两个函数产生相同的结果?

因为你发送的数据太少,它要么总是完全工作,要么完全失败。

sendall 所做的只是循环和send 有效负载,直到所有内容都发送完毕。如果一切都适合第一个send,那么没有区别。

【讨论】:

    猜你喜欢
    • 2016-03-19
    • 2015-06-13
    • 1970-01-01
    • 1970-01-01
    • 2012-07-14
    • 1970-01-01
    • 2013-08-07
    相关资源
    最近更新 更多