我制作了一个快速程序,使用python中的套接字发送文件。
服务器:
import socket, threading
#Create a socket object.
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#Bind the socket.
sock.bind( ("", 5050) )
#Start listening.
sock.listen()
#Accept client.
client, addr = sock.accept()
#Open a new file jpg file.
file = open("out.jpg", "wb")
#Receive all the bytes and write them into the file.
while True:
received = client.recv(5)
#Stop receiving.
if received == b'':
file.close()
break
#Write bytes into the file.
file.write( received )
客户端:
import socket, threading
#Create a socket object.
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#Connect to the server.
sock.connect(("192.168.1.3", 5050))
#Open a file for read.
file = open("cpp.jpg", "rb")
#Read first 5 bytes.
read = file.read(5)
#Keep sending bytes until reaching EOF.
while read != b'':
#Send bytes.
sock.send(read)
#Read next five bytes from the file.
read = file.read(1024)
sock.close()
file.close()
根据经验,学习发送可以发送您的网络的字节数 速度是发送它们的能力。如果您举例:sock.send(20 gb),您将丢失字节,因为大多数网络连接无法发送20 GB 一旦。你必须逐个发送它们。
所以我的问题是:我怎么知道socket.send()的最大字节数 可以通过互联网发送?如何根据我的网速提高我的程序以尽快发送文件?
答案 0 :(得分:3)
send
无法保证所有数据都已发送(它与网络速度没有直接联系;有多种原因可以发送少于请求的数据),只是它让你知道多少寄了,送了。根据{{1}},您可以明确地将循环写入send
,直到它们全部真正发送。
或者您可以使用Dunno's answer并避免麻烦。 sendall
基本上是sendall
中描述的包装器,但Python为您完成了所有繁重的工作。
如果您不关心将整个文件啜饮到内存中,您可以使用它来替换整个循环结构:
sock.sendall(file.read())
如果您在类似UNIX的操作系统上使用现代Python(3.5或更高版本),您可以优化一点,以避免使用the other answer将文件数据读入Python(这应该只会导致部分send
出错):
sock.sendfile(file)
如果Python在您的操作系统上不支持os.sendfile
,这只是一个有效的循环,read
和send
重复,但在支持的系统上它直接从内核中的文件复制到套接字,甚至无需处理Python中的文件数据(通过减少系统调用和完全消除一些内存副本,可以显着提高吞吐速度)。
答案 1 :(得分:1)
只需在循环中发送这些字节,直到所有字节都被发送,此处为example from the docs
def mysend(self, msg):
totalsent = 0
while totalsent < MSGLEN:
sent = self.sock.send(msg[totalsent:])
if sent == 0:
raise RuntimeError("socket connection broken")
totalsent = totalsent + sent
在您的情况下,MSGLEN
将为1024,并且由于您不使用课程,因此您不需要自我论证
答案 2 :(得分:1)
源和目标之间的所有步骤都有输入/输出缓冲区。一旦缓冲区填满,在空间可用之前,其他任何东西都不会被接受。
当您的应用程序尝试发送数据时,它将填满操作系统中的缓冲区,该缓冲区被清除,因为操作系统能够将该数据卸载到网络设备驱动程序(也有缓冲区)。
网络设备驱动程序与实际网络连接,并了解如何知道何时可以发送数据以及如何确认收据将由另一方确认(如果有的话)。在发送数据时,该缓冲区被清空,允许操作系统从其缓冲区中推送更多数据。反过来,这为您的应用程序腾出了空间,可以将更多数据推送到操作系统。
还有许多其他因素会影响到这个过程(超时,最大跳数是我可以随意考虑的两个),但一般过程是你必须在每一步缓冲数据,直到它可以被发送到下一步。
答案 3 :(得分:1)
根据经验,学习发送可以发送一定数量的字节 你的网络速度是发送它们的能力。
由于您使用的是TCP套接字(即SOCK_STREAM),因此会自动为您处理传输速度问题。也就是说,一旦通过send()调用从缓冲区(并进入套接字的内部发送缓冲区)复制了一些字节,TCP层将确保它们无论需要多长时间都可以进入接收程序(好吧,无论如何,如果在多分钟的过程中无法取得任何进展,TCP层最终将放弃重发数据包。)
如果您举例:sock.send(20 gb),您将丢失字节 因为大多数网络连接不能一次发送20 GB。你必须 逐个发送它们。
这是不正确的;你不会“丢失字节”,因为TCP层会在必要时自动重新发送任何丢失的数据包。但是,可能会发生的事情是send()可能决定不接受您提供的所有字节。这就是为什么绝对有必要检查send()的返回值,看看send()实际接受了多少字节的责任 - 你不能简单地假设send()总是接受所有的字节你提出来了。
所以我的问题是:我怎么知道最大字节数 socket.send()可以通过互联网发送吗?
你做不到。相反,您必须查看send()返回的值,以了解send()从缓冲区中复制了多少字节。这样,在您下次调用send()时,您将知道要传入的数据(即从上一次调用中发送的最后一个字节后的下一个字节开始)
如何改进我的程序以尽快发送文件 取决于我的网速?
一次提供send()尽可能多的字节数;这将使其具有最大的灵活性来优化幕后的工作。除此之外,只需在循环中调用send(),使用每个send()调用的返回值来确定下次传递给send()的字节数(例如,如果第一个调用返回5,则知道发送(读取缓冲区中的前5个字节并确保它们到达目的地,因此您对send()的下一次调用应该从数据流的第6个字节开始传入缓冲区......依此类推)。 (或者如果你不想自己处理那个逻辑,你可以像@ShadowRanger建议一样调用sendall(); sendall()只是一个包含send()循环的包装器,它为你做了那个逻辑。唯一的缺点例如,如果你在20千兆字节的数据上调用sendall(),那么在sendall()调用返回之前可能需要几个小时!这是否会对你造成问题取决于你的程序可能想要完成的其他内容,如果发送数据的任何事情。)
这就是TCP的全部内容。
另一方面,如果使用UDP套接字发送数据,情况就会大不相同;在UDP的情况下,可以简单地丢弃数据包,并且由程序员明确地管理传输速度问题,数据包重发等。但是,操作系统会为您处理所有TCP。
答案 4 :(得分:0)
@Jeremy Friesner
所以我可以这样做:
file = open(filename, "rb")
read = file.read(1024**3) #Read 1 gb.
totalsend = 0
#Send Loop
while totalsend < filesize:
#Try to send all the bytes.
send = sock.send(read)
totalsend += send
#If failed, then seek into the file the position
#where the next read will also read the missing bytes.
if send < 1024**3:
file.seek(totalsend)
read = file.read(1024**3) #Read 1 gb.
这是对的吗?
另外,从这个例子中我不得不再想一想。您可以在每个循环中发送的数据,其大小不会超过您的内存。因为你从磁盘上的内存中带来了字节。因此,从理论上讲,即使您的网络速度是无穷大,如果文件大于您的内存,您也无法立即发送所有字节。