一、客户端从服务端下载文件(面向过程--函数版本)
server. py
import socket
import json
import struct
import os
# 定义路径全局变量,这里为服务端提供文件的路径
share_dir = r\'/Users/xiexinran/Desktop/乱七八糟的.py/socket/server/Share\'
# 建立
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 绑定
phone.bind((\'127.0.0.1\', 8081))
# 监听
phone.listen(5)
# 通信循环
while True:
# 接收客户端连接请求
conn, client_addr = phone.accept()
while True:
# 接收客户端数据/命令
res = conn.recv(1024)
if not res:
continue
# 解析命令 \'get 1.mp4\'
cmds = res.decode(\'utf-8\').split() # [\'get\',\'1.mp4\']
filename = cmds[1] # \'1.mp4\'
# 以读的方式打开文件,提取文件内容发送给客户端
# 1.制作固定长度的报头
header_dic = {
\'filename\': filename,
\'file_size\': os.path.getsize(\'{}/{}\'.format(share_dir, filename))
}
# 序列化报头
header_json = json.dumps(header_dic) # 序列化为byte字节流类型
header_bytes = header_json.encode(\'utf-8\') # 编码为utf-8(Mac系统)
# 2.先发送报头的长度
# 2.1 将byte类型的长度打包成4位int
conn.send(struct.pack(\'i\', len(header_bytes)))
# 2.2 再发报头
conn.send(header_bytes)
# 2.3 再发真实数据
with open(\'{}/{}\'.format(share_dir, filename), \'rb\') as f:
for line in f:
conn.send(line)
# 结束连接
conn.close()
# 关闭套接字
phone.close()
client. py
import socket
import struct
import json
# 定义路径全局变量,这里为客户端下载文件到本地的保存路径
Download_dir = r\'/Users/xiexinran/Desktop/乱七八糟的.py/socket/client/Download\'
# 建立
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 连接
phone.connect((\'127.0.0.1\', 8081))
while True:
cmd = input(\'>>> \').strip()
if not cmd:
continue
if cmd == \'quit\':
break
# 给服务端发送命令
phone.send(cmd.encode(\'utf-8\'))
# 接收服务端数据
# 1.先收报头长度
obj = phone.recv(4)
header_size = struct.unpack(\'i\', obj)[0]
# 2.收报头
\'\'\'
header_dic = {
\'filename\': filename,
\'file_size\': os.path.getsize(filename)
}
\'\'\'
header_bytes = phone.recv(header_size)
# 3.从报头中解析出数据的真实信息(报头字典)
header_json = header_bytes.decode(\'utf-8\')
header_dic = json.loads(header_json)
# 4.解析命令
total_size = header_dic[\'file_size\']
filename = header_dic[\'filename\']
# 4.接受真实数据
with open(\'%s/%s\' % (Download_dir, filename), \'wb\') as f:
recv_size = 0
while recv_size < total_size:
line = phone.recv(1024)
f.write(line)
recv_size += len(line)
# print(\'总大小:%s 已下载:%s\' % (total_size, recv_size))
# 关闭套接字
phone.close()
客户端运行代码:
下载成功后的效果:
二、客户端向服务端下载文件(面向对象版本)
server. py
import socket
import struct
import json
import os
class MYTCPServer:
# AF_INET IPv4因特网协议
address_family = socket.AF_INET
# SOCK_STREAM 提供顺序的,可靠的双向的基于连接的字节流。可能支持带外数据传输机制。
socket_type = socket.SOCK_STREAM
# 一次性允许传输的最大字节数
max_packet_size = 8192
# 编码方式
coding = \'utf-8\'
# 最大连接数
request_queue_size = 5
# 服务端文件url,这里填写自己本地服务器提供的上传文件夹
server_dir = \'/Users/xiexinran/Desktop/乱七八糟的.py/socket/server2/file_upload\'
def __init__(self, server_address, bind_and_activate=True):
self.server_address = server_address
self.socket = socket.socket(self.address_family,
self.socket_type)
if bind_and_activate:
try:
self.server_bind()
self.server_activate()
except:
self.server_close()
raise # 中断程序
def server_bind(self):
"""
由构造函数调用以绑定套接字
"""
self.socket.bind(self.server_address)
self.server_address = self.socket.getsockname()
def server_activate(self):
"""
由构造函数调用监听
"""
self.socket.listen(self.request_queue_size)
def server_close(self):
"""
由构造函数调用关闭服务器套接字
"""
self.socket.close()
def get_request(self):
"""
接收客户端请求
"""
return self.socket.accept()
def close_request(self, request):
"""
关闭单个客户端请求
"""
request.close()
def run(self):
while True:
self.conn, self.client_addr = self.get_request()
print(\'from client \', self.client_addr)
while True:
try:
head_struct = self.conn.recv(4) # 收客户端的报头长度
if not head_struct:
break
head_len = struct.unpack(\'i\', head_struct)[0]
head_json = self.conn.recv(head_len).decode(self.coding) # 收客户端的序列化报头
head_dic = json.loads(head_json) # 反序列化报头
print(head_dic)
# head_dic = {\'cmd\':\'put\',\'filename\':\'a.txt\',\'filesize\':123123}
cmd = head_dic[\'cmd\']
if hasattr(self, cmd):
func = getattr(self, cmd)
func(head_dic)
except Exception:
break
def put(self, args):
# 规范path字符串形式,把目录和文件名合成一个路径
file_path = os.path.normpath(os.path.join(
self.server_dir,
args[\'filename\']
))
filesize = args[\'filesize\']
recv_size = 0
print(\'----->\', file_path)
with open(file_path, \'wb\') as f:
while recv_size < filesize:
recv_data = self.conn.recv(self.max_packet_size)
f.write(recv_data)
recv_size += len(recv_data)
# print(\'recvsize:%s filesize:%s\' % (recv_size, filesize))
tcpserver1 = MYTCPServer((\'127.0.0.1\', 8080))
tcpserver1.run()
client. py
import socket
import struct
import json
import os
class MYTCPClient:
address_family = socket.AF_INET
socket_type = socket.SOCK_STREAM
max_packet_size = 8192
coding = \'utf-8\'
request_queue_size = 5
def __init__(self, server_address, connect=True):
self.server_address = server_address
self.socket = socket.socket(self.address_family,
self.socket_type)
if connect:
try:
self.client_connect()
except:
self.client_close()
raise
def client_connect(self):
self.socket.connect(self.server_address)
def client_close(self):
self.socket.close()
def run(self):
while True:
inp = input(">>: ").strip()
if not inp:
continue
l = inp.split()
cmd = l[0]
if hasattr(self, cmd):
func = getattr(self, cmd)
func(l)
def put(self, args):
cmd = args[0]
filename = args[1]
if not os.path.isfile(filename): # 判断路径是否为文件
print(\'file:%s is not exists\' % filename)
return
else:
filesize = os.path.getsize(filename)
head_dic = {\'cmd\': cmd, \'filename\': os.path.basename(filename), \'filesize\': filesize} # 返回文件名
# print(head_dic)
head_json = json.dumps(head_dic)
head_json_bytes = bytes(head_json, encoding=self.coding)
head_struct = struct.pack(\'i\', len(head_json_bytes))
self.socket.send(head_struct)
self.socket.send(head_json_bytes)
send_size = 0
with open(filename, \'rb\') as f:
for line in f:
self.socket.send(line)
send_size += len(line)
# print(send_size)
else:
print(\'upload successful\')
client = MYTCPClient((\'127.0.0.1\', 8080))
client.run()
server. py 运行结果
client. py 运行结果
上传成功后的效果图: