python socket传输大文件的方法

×××××××××××××方法一:

	发送端
1、计算发送文件大小,然后结合文件的其他信息,组成文件头先发送一次。
2、发送文件数据时用sendall(),一次发送所有数据(好像是重复调用了send())
	接收端
1、接收端根据接受文件的大小和recv_size计算要接收数据的次数,
2、然后把每次接收的数据连在一起
3、因为可能不是整除,最后要判断下最后一次具体接收多少字节数据(感觉也可以不用这样,直接接收)

1、计算文件大小

1、os.path.getsize(filepath)
2、os.stat(filepath).st_size

2、socket接收数据的操作的注意事项

因为下面的原因,我们要在接收端接收头文件时做一些处理,用来与数据文件区分

#服务器
import socket
server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.bind(("127.0.0.1",8000))
server.listen(3)
while True:
    sock,adddr = server.accept()
    data = sock.recv(1024)
    print(data.decode("utf8")+"\n")
    if(data.decode("utf8") == "111"):
        print("dsfaf")
        server.close()
sock.close()
#客户端
import socket 
import json
client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
client.connect(("127.0.0.1",8000))
header_data = {
    'file_size': 1000299,
}
client.send(json.dumps(header_data).encode("utf-8"))
client.send("ripo".encode("utf8"))
client.send("fasdf".encode("utf8"))
client.send("oiposfdpakf".encode("utf8"))
client.send("111".encode("utf8"))
client.close()

服务器输出结果

{"file_size": 1000299}ripofasdfoiposfdpakf111

可见虽然客户端是用多个send函数发送的,服务器采用1024大小的缓冲(sock.recv(1024))区,一次把所有数据的都接收了。这样就不能区分出我们先发送的文件头和文件内容了。

解决方案

1、使用send和recv隔离
在发送端第一次发送数据,也就是发送文件头信息的时候,接收端在接收后在给发送端发送一个数据,
然后发送端接收后(不必处理),相当于在发送文件头信息和文件信息之间加了一步,这样接收端就能区
分开了。
2、(推荐):使用struct计算头文件大小
使用struct库
1、首先在发送端和接收端约定一个fmt,也就是头文件的格式。
2、客户端 
	struct.pack(fmt,v1,v2.....)
3、服务器端
(1)struct.calcsize(fmt),可以计算大小,这样就可以在接收端接收指定大小的数据。这个大小对应头
文件数据的大小。
(2)struct.unpack(fmt,v1,v2.....)

第一种方法最终代码:

#服务器端
import socket
import struct
server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.bind(("127.0.0.1",3008))
server.listen(3)
while True:
    print("start.......")
    sock,adddr = server.accept()
    d = sock.recv(struct.calcsize("l"))
    total_size = struct.unpack("l",d)
    num  = total_size[0]//1024
    data = b''
    for i in range(num):
        data += sock.recv(1024)
    data += sock.recv(total_size[0]%1024)

    with open("11.png","wb") as f:
        f.write(data)
    sock.close()
sock.close()
#客户端
import socket 
import struct
import os
import json
client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
client.connect(("127.0.0.1",3008))
print("connect success....")
filepath = "1.png"
size =  os.stat(filepath).st_size
f= struct.pack("l",os.stat(filepath).st_size)
client.send(f)
img = open(filepath,"rb")
client.sendall(img.read())
img.close()
client.close()

××××××××××××方法二(推荐):

方法二:
	发送端(事先知道接收的缓冲区大小buf)
1、读取要发送的文件,获取总大小
2、每次发送buf大小的数据,是的接收端正好接这么多
3、一直发,直到把文件的数据发送完
	接收端
1、一直接收直到数据的不大于0

第二种方法最终代码:

#服务器
import socket
import struct
server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.bind(("127.0.0.1",3008))
server.listen(3)
while True:
    print("start......")
    sock,adddr = server.accept()
    total_data = b''
    num = 0
    data = sock.recv(1024)
    total_data += data
    num =len(data)
    # 如果没有数据了,读出来的data长度为0,len(data)==0
    while len(data)>0:
        data = sock.recv(1024)
        num +=len(data)
        total_data += data       
    with open("11.png","wb") as f:
        f.write(total_data)
    sock.close()
sock.close()
#客户端
import socket 
import struct
import os
import json

client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
client.connect(("127.0.0.1",3008))
print("connect success.....")
filepath = "1.png"
img = open(filepath,"rb")
# print(len(img.read()))

client.sendall(img.read())
img.close()
client.close()

使用python socket分发大文件

python socket编程(传输字符、文件、图片)

struct.pack()、struct.unpack()和struct.calcsize()

socket中AF_*** 和socket.SOCK_STREAM的资料来自《python核心编程》
套接字是计算机网络数据结构,它体现了上节中所描述的“通信端点”的概念。在任何
类型的通信开始之前,网络应用程序必须创建套接字。可以将它们比作电话插孔,没有它将
无法进行通信。
套接字的起源可以追溯到 20 世纪 70 年代,它是加利福尼亚大学的伯克利版本 UNIX(称
为 BSD UNIX)的一部分。因此,有时你可能会听过将套接字称为伯克利套接字或 BSD 套接
字。套接字最初是为同一主机上的应用程序所创建,使得主机上运行的一个程序(又名一个
进程)与另一个运行的程序进行通信。这就是所谓的进程间通信(Inter Process Communication,
IPC)。有两种类型的套接字:基于文件的和面向网络的。
UNIX 套接字是我们所讲的套接字的第一个家族,并且拥有一个“家族名字” AF_UNIX
(又名 AF_LOCAL,在 POSIX1.g 标准中指定),它代表地址家族(address family): UNIX。
包括 Python 在内的大多数受欢迎的平台都使用术语地址家族及其缩写 AF;其他比较旧的系
统可能会将地址家族表示成域(domain)或协议家族(protocol family),并使用其缩写 PF 而
非 AF。类似地, AF_LOCAL(在 2000~2001 年标准化)将代替 AF_UNIX。然而,考虑到
后向兼容性,很多系统都同时使用二者,只是对同一个常数使用不同的别名。 Python 本身仍
然在使用 AF_UNIX。
因为两个进程运行在同一台计算机上,所以这些套接字都是基于文件的,这意味着文件
系统支持它们的底层基础结构。这是能够说得通的,因为文件系统是一个运行在同一主机上
的多个进程之间的共享常量。
第二种类型的套接字是基于网络的,它也有自己的家族名字 AF_INET,或者地址家族:
因特网。另一个地址家族 AF_INET6 用于第 6 版因特网协议(IPv6)寻址。此外,还有其他
的地址家族,这些要么是专业的、过时的、很少使用的,要么是仍未实现的。在所有的地址
家族之中,目前 AF_INET 是使用得最广泛的。
Python 2.5 中引入了对特殊类型的 Linux 套接字的支持。套接字的 AF_NETLINK 家族(无
连接[见 2.3.3 节])允许使用标准的 BSD 套接字接口进行用户级别和内核级别代码之间的 IPC。
之前那种解决方案比较麻烦,而这个解决方案可以看作一种比前一种更加优雅且风险更低的
解决方案,例如,添加新系统调用、 /proc 支持,或者对一个操作系统的“IOCTL”。
针对 Linux 的另一种特性(Python 2.6 中新增)就是支持透明的进程间通信(TIPC)协
议。 TIPC 允许计算机集群之中的机器相互通信,而无须使用基于 IP 的寻址方式。 Python 对
TIPC 的支持以 AF_TIPC 家族的方式呈现。
总的来说, Python 只支持 AF_UNIX、 AF_NETLINK、 AF_TIPC 和 AF_INET 家族。因为
本章重点讨论网络编程,所以在本章剩余的大部分内容中,我们将使用 AF_INET。
  • 22
    点赞
  • 112
    收藏
    觉得还不错? 一键收藏
  • 18
    评论
评论 18
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值