文件上传 + socketserver(并发)
文件上传
示例 1 : 将文件或图片等数据 从客户端传送到服务端
涉及到解决黏包, 将 命令 / 文件路径(文件) / 文件大小 + 文件内容 都发过去 前面的东西和后面的数据在传输的过程中,可能会出现黏包现象, 为了解决这个问题,将 文件信息 部分的长度进行压缩struct.pack()进行压缩,一起发送到服务端,则发送的内容为 文件信息长度 / 文件信息 / 文件内容 三部分,通过解压文件长度,获取接收文件信息的字节长度,最终通过循环接收文件内容.
涉及知识点 : 黏包 / 序列化 / hashlib对比文件数据是否一致
# 服务端
import struct
import socket
import json
import hashlib
sock=socket.socket()
sock.bind(('127.0.0.1',8800))
sock.listen(5)
while 1:
print("server is working....")
conn,addr= sock.accept()
while 1:
# 接收json的打包长度
file_info_length_pack=conn.recv(4)
file_info_length=struct.unpack("i",file_info_length_pack)[0]
# 接收json字符串
file_info_json=conn.recv(file_info_length).decode("utf8")
file_info=json.loads(file_info_json)
action=file_info.get("action")
filename=file_info.get("filename")
filesize=file_info.get("filesize")
# 循环接收文件
md5=hashlib.md5()
with open("put/"+filename,"wb") as f:
recv_data_length=0
while recv_data_length<filesize:
data=conn.recv(1024)
recv_data_length+=len(data)
f.write(data)
# MD5摘要
md5.update(data)
print("文件总大小:%s,已成功接收%s"%(filesize,recv_data_length))
print("接收成功!")
conn.send(b"OK")
print(md5.hexdigest())
md5_val=md5.hexdigest()
client_md5=conn.recv(1024).decode("utf8")
if md5_val==client_md5:
conn.send(b"203")
else:
conn.send(b"204")
# 客户端
import socket
import os
import json
import struct
import hashlib
sock=socket.socket()
sock.connect(("127.0.0.1",8800))
while 1 :
cmd=input("请输入命令:") # put 111.jpg
action,filename=cmd.strip().split(" ")
filesize=os.path.getsize(filename)
file_info={
"action": action,
"filename": filename,
"filesize": filesize,
}
file_info_json=json.dumps(file_info).encode("utf8")
ret=struct.pack("i",len(file_info_json))
sock.send(ret) # 发送 file_info_json的打包长度
sock.send(file_info_json) # 发送 file_info_json字节串
# 发送 文件数据
md5=hashlib.md5()
with open(filename,"rb") as f:
for line in f:
sock.send(line)
md5.update(line)
data=sock.recv(1024)
print(md5.hexdigest())
md5_val=md5.hexdigest()
sock.send(md5_val.encode("utf8")) # 发送密文
is_valid=sock.recv(1024).decode('utf8')
if is_valid=="203":
print("文件完整!")
else:
print("文件上传失败!")
socketserver (并发)
♥♥♥♥♥
使用socketserver 的 ThreadingTCPServer 可以是服务端同时接收多个客户端的请求. 逻辑部分封装在 handle 方法中
import socketserver
class My_Server(socketserver.BaseRequestHandler): # 定义自己的服务类,必须继承 socketserver.BaseRequestHandle 类
def handle(self): # 方法名是固定的
'''
handle中有 套接对象request 等价于之前的conn
用法为 self.request.recv(1024) self.request.send()
:return:
'''
pass # handle方法中是服务端的请求后的命令 逻辑部分
server = socketserver.ThreadingTCPServer(('127.0.0.1',8899),My_Server) # 参数为 ip端口, 自定的类 # 用于 bingd绑定 listen等候数
server.serve_forever() # 用于 accept() 等待连接
socketserver 源码解析

浙公网安备 33010602011771号