本节内容
1、概述
2、文件下载实现
3、MD5值校验
一、概述
我们如何利用socket去下载一个文件,整体思路是这样的:
- 读取文件名
- 检测文件是否存在
- 打开文件
- 检测文件大小
- 发送文件大小给客户端
- 等客户确认
- 开始边读边发数据
- 发送md5值给客户端校验
友情提示:以下代码都是在Linux系统,并且是python3换将下实验的。
二、文件下载实现
2.1、服务端代码
逻辑:获取命令和文件名->判断文件是否存在->打开文件->获取文件大小->发送文件大小给客户端->等待客户端确认->边读边发
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
import
hashlib
import
socket,os
server
=
socket.socket()
server.bind((
"localhost"
,
9999
))
server.listen()
while
True
:
conn,addr
=
server.accept()
print
(
"new conn:"
,addr)
while
True
:
print
(
"等待新指令"
)
data
=
conn.recv(
1024
)
if
not
data:
print
(
"客户端已断开"
)
break
cmd,filename
=
data.decode().split()
#接收客户端发过来的命令和文件名
print
(filename)
if
os.path.isfile(filename):
#判断文件是否存在
# m = hashlib.md5()
with
open
(filename,
"rb"
) as f:
file_size
=
os.stat(filename).st_size
#获取文件大小
conn.send(
str
(file_size).encode() )
#发送文件大小
conn.recv(
1024
)
for
line
in
f:
#m.update(line)
conn.send(line)
#边读边发给客户端
#print("file md5",m.hexdigest())
print
(
"send done"
)
server.close()
|
2.2、客户端代码
逻辑:判断是否是下载命令(get) ->发送下载命令和文件名 ->获取文件大小->发送确认信息->判断时候已经全部接收
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
import
socket
client
=
socket.socket()
client.connect((
"localhost"
,
9999
))
while
True
:
cmd
=
input
(
">>>:"
).strip()
if
len
(cmd)
=
=
0
:
continue
if
cmd.startswith(
"get"
):
client.send(cmd.encode())
#发送下载命令和文件名
server_respose
=
client.recv(
1024
)
#接收文件大小
print
(
"server response:"
,server_respose)
client.send(
"ready to recv file"
.encode())
#发送确认信息
file_total_size
=
int
(server_respose.decode())
revived_size
=
0
#初始化接收大小
filename
=
cmd.split()[
1
]
#获取文件名
with
open
(filename
+
".new"
,
"wb"
) as f:
while
revived_size < file_total_size:
#判断接收大小和文件大小比较
data
=
client.recv(
1024
)
revived_size
+
=
len
(data)
#接收大小
f.write(data)
#写入文件
else
:
print
(file_total_size,revived_size)
print
(
"file recv done"
)
client.close()
|
三、MD5值校验
说明:上面只提到了怎么传输,我们在概述里面也提到了最后要用MD5值做校验,但是很显然没看到,下面我们就来说说怎么用MD5值做比较
3.1、服务端
说明:生成md5的对象->计算MD5值->生成16进制的形式->编码后发送给客户端
①代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
import
hashlib
import
socket,os
server
=
socket.socket()
server.bind((
"localhost"
,
9999
))
server.listen()
while
True
:
conn,addr
=
server.accept()
print
(
"new conn:"
,addr)
while
True
:
print
(
"等待新指令"
)
data
=
conn.recv(
1024
)
if
not
data:
print
(
"客户端已断开"
)
break
cmd,filename
=
data.decode().split()
print
(filename)
if
os.path.isfile(filename):
m
=
hashlib.md5()
#生成MD5的对象
with
open
(filename,
"rb"
) as f:
file_size
=
os.stat(filename).st_size
conn.send(
str
(file_size).encode() )
#send file size
conn.recv(
1024
)
for
line
in
f:
m.update(line)
#计算md5值
conn.send(line)
print
(
"file md5"
,m.hexdigest())
conn.recv(
1024
)
#等待客户确认发送MD5值
conn.send(m.hexdigest().encode())
#生成MD5值并且发送给客户端
print
(
"send done"
)
server.close()
|
②改动的地方
3.2、客户端
说明:生成MD5值->计算接收的数据的MD5值->生成接收数据的MD5值16进制的形式->发送接收MD5值确认信息->接收客户端的MD5值->客户端和服务端的MD5值做比较
①代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
# __auther__ == zhangqigao
import
socket,hashlib
client
=
socket.socket()
client.connect((
"localhost"
,
9999
))
while
True
:
cmd
=
input
(
">>>:"
).strip()
if
len
(cmd)
=
=
0
:
continue
if
cmd.startswith(
"get"
):
client.send(cmd.encode())
server_respose
=
client.recv(
1024
)
print
(
"server response:"
,server_respose)
client.send(
"ready to recv file"
.encode())
file_total_size
=
int
(server_respose.decode())
revived_size
=
0
filename
=
cmd.split()[
1
]
m
=
hashlib.md5()
#生成MD5对象
with
open
(filename
+
".new"
,
"wb"
) as f:
while
revived_size < file_total_size:
data
=
client.recv(
1024
)
revived_size
+
=
len
(data)
m.update(data)
#计算数据接收的MD5值
f.write(data)
else
:
print
(file_total_size,revived_size)
client_md5_vaule
=
m.hexdigest()
#生成接收数据的MD5值16进制形式
client.send(
"ready to recv file md5 value"
.encode())
server_md5_value
=
client.recv(
1024
)
#接收客户端的MD5值
if
client_md5_vaule
=
=
server_md5_value.decode():
#客户端和服务端的MD5值做比较
print
(
"file recv done"
)
else
:
print
(client_md5_vaule,server_md5_value.decode())
client.close()
|
②改动地方
总结:
- 上传和下载都是以客户端或者服务端加载文件,然后另外一边接收再写入文件。
- 解决粘包问题,在接收数据大小后需要等待确认信息。
- 看文件能都上传和下载,应该用MD5值去校验。
- 创建一个无限大的文件,请用:dd if=/dev/sda1 of=文件名,比如创建test.txt,则:dd if=/dev/sda1 of=test.txt
- 获取一个文件的大小:os.stat(文件名).st_size