python 网络编程socketserver示例(多用户ftp上传)

之前用单用户ftp上传,服务器同时只能处理一个客户端的连接,要做到多客户端连接可以直接使用python帮我们封装好的socketserver模块。以下代码为多用户ftp上传示例,里面用到md5文件一致性校验,在一个win机器上同时扮演客户端和服务器没问题,但是当有一方使用了linux就出现问题了。运行代码测试发现;同样的文件内容,在winlinux是进行md5加密内容发现执行的结果不一样。没测试在两台win上是否一样,有其他人看到可以自己测试下。

服务器端:
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import socketserver
import struct
import pickle
import os
import hashlib
ip_port = ('10.10.10.103', 9999)
md5 = hashlib.md5(b'tony')

class FtpServer(socketserver.BaseRequestHandler):
def handle(self):
print(self.client_address, '已成功连接至服务器……')
while 1:
try:
# 接收文件信息长度
file_info_len_struct = self.request.recv(4)
# linux客户端断开接受为0
if file_info_len_struct == '':
break
# 解析文件信息字节长度
file_info_len = struct.unpack('i', file_info_len_struct)[0]
# 根据获取的文件信息字节长度接收文件信息(pickle序列化字节)
file_info_pickle = self.request.recv(file_info_len)
# 解析文件信息
file_info = pickle.loads(file_info_pickle)
file_path = file_info['file_path']
file_size = file_info['file_size']
file_name = os.path.basename(file_path)
with open('up/'+file_name, 'wb') as f:
# 累计接收量
receive_lenth = 0
# 未接收完则持续接收
while receive_lenth < file_size:
# 每次接收1024
data = self.request.recv(1024)
# 加密,后续做一致性校验
md5.update(data)
# 累加到累计接收量
receive_lenth += len(data)
# 写入文件
f.write(data)
print('文件总大小:{}, 已接收:{}, 已接收百分比{:.2f}%'.format(file_size, receive_lenth, receive_lenth/file_size*100))
print(file_name+'文件上传成功')
print(md5.hexdigest())
self.request.send(md5.hexdigest().encode('utf8'))
except Exception as e:
print(e)
break
print(self.client_address, '已断开连接……')
self.request.close()

ftp = socketserver.ThreadingTCPServer(ip_port, FtpServer)
ftp.serve_forever()
客户端:
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
34
35
36
37
38
39
40
41
42
43
44
import socket
import os
import pickle
import struct
import hashlib
md5 = hashlib.md5(b'tony')
ip_port = ('10.10.10.103', 9999)
client = socket.socket()
# 连接服务器
client.connect(ip_port)
while 1:
file = input('请选择您要上传的文件:')
# 判断用户输入空、exit或文件不存在直接停止
if file == '' or file == 'exit' or not os.path.exists(file):
break
# 将文件信息封装到一个字典中
file_dic = {
'file_path' : file,
'file_size' : os.path.getsize(file)
}
# 将文件信息字典序列化为字节
file_dic_pickle = pickle.dumps(file_dic)
# 取文件信息序列化字节的长度
file_dic_pickle_len = len(file_dic_pickle)
# 将序列化为字节的文件信息长度格式化为一个4位的字节
file_dic_pickle_len_struct = struct.pack('i', file_dic_pickle_len)
# 发送文件信息长度(字节)
client.send(file_dic_pickle_len_struct)
# 发送文件信息(字节)
client.send(file_dic_pickle)
with open(file, 'rb') as f:
for line in f:
md5.update(line)
client.send(line)
# 接收文件加密结果
ret = client.recv(1024).decode('utf8')
print(ret)
print(md5.hexdigest())
if ret == md5.hexdigest():
print('文件上传校验成功')
else:
print('文件上传校验失败')
# 关闭连接
client.close()