利用socket模块实现 电影的上传与下载(基础版)

 

1 server_duan.py

 1 import socket
 2 from socket import SOL_SOCKET, SO_REUSEADDR
 3 import commen
 4 import os
 5 
 6 
 7 sk = socket.socket()
 8 sk.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
 9 sk.bind(('127.0.0.1', 8081))  # '127.0.0.1':本地回环地址;
10 sk.listen(3)  # 设定"可以等待链接的客户端最大数量"
11 
12 # 服务端下载功能
13 def s_down(c_or_s):
14     # 1 接收电影报头
15     mv_header = commen.recv_header(c_or_s)
16 
17     # 2 接收电影
18     # 2.1 获取电影名和电影大小
19     mv_name = mv_header.get('mv_name')
20     mv_size = mv_header.get('mv_size')
21     # 2.2 接收电影
22     commen.down_file(c_or_s, mv_name, mv_size)
23 
24 # 服务端上传功能
25 def s_up(c_or_s):
26     # 输入/获取电影资源地址(请手动复制地址替换url)
27     # source_mv_addr = r'url'
28     source_mv_addr = r'E:\BaiduNetdiskDownload\IT\笔记与视频\python\网络并发\网络并发day04\视频'
29     mv_list = os.listdir(source_mv_addr)
30     # 发送"电影列表"给客户端
31     commen.send_header(c_or_s, mv_list)
32     # 接收客户端选择的电影编号
33     choice_mv_num = commen.recv_header(c_or_s)
34     # 校验客户端软件是否发生异常
35     # 正常情况下,客户端发送过来的电影编号都是存在的(客户端已校验过),若不存在说明客户端代码异常
36     if not commen.exist_choice_num(choice_mv_num, mv_list):
37         war_meg = '文件被破坏,请重新下载客户端'
38         print('客户端代码被修改,发送告知: %s' % war_meg)
39         commen.send_header(c_or_s, war_meg)
40         return print('等待客户端下载完成后重新链接')
41     # 运行到此步,说明电影编号存在
42     # 上传电影到客户端
43 
44     # 1) 制作电影报头
45     # 获取制作报头需要的信息: 电影名和电影地址
46     mv_name = mv_list[int(choice_mv_num)-1]
47     mv_path = os.path.join(source_mv_addr, mv_name)
48     # 制作
49     mv_header = {'mv_name': mv_name, 'mv_size': os.path.getsize(mv_path)}
50     # print(mv_header)
51     # 2) 发送电影报头
52     commen.send_header(c_or_s, mv_header)
53     # 3) 发送电影
54     commen.up_file(c_or_s, mv_name, mv_path)
55 
56 
57 # 主程序核心代码
58 def s_src(s_server):
59     func_dict = {'1': s_down, '2': s_up}
60     # 接收客户端的消息,获取客户端的需求: 是上传还是下载,然后做对应处理
61     func_choice = commen.recv_header(s_server)
62     if not func_choice.isdigit():
63         return print('客户端软件损坏')
64     if func_choice not in func_dict:
65         return print('客户端软件损坏')
66     func = func_dict.get(func_choice)
67     # print(func)
68     func(s_server)
69 
70 # 主程序
71 def s_run():
72     while True:
73         sock, addr = sk.accept()
74         while True:
75             try:
76                 s_src(sock)
77             except Exception:
78                 print('客户端异常断开,正在等待其他客户端接入...')
79                 break
80 
81 
82 s_run()
View Code

 

2 client_duan.py

 1 import socket
 2 import os
 3 import commen
 4 
 5 client = socket.socket()
 6 client.connect(('127.0.0.1', 8081))
 7 
 8 # 客户端上传功能
 9 def c_up():
10     # 获取本地视频文件地址(手动复制视频地址后替换url)
11     # mv_dir_path = r'url'
12     mv_dir_path = r'E:\BaiduNetdiskDownload\IT\笔记与视频\python\网络并发\网络并发day03\视频'
13     # 根据地址获取电影列表
14     mv_list = os.listdir(mv_dir_path)
15     # 展示电影条目
16     print('电影清单>>>:')
17     for i, j in enumerate(mv_list):
18         print(i + 1, j)
19     while True:
20         # 1 选择要上传的电影编号
21         choice_num = input('请输入想要上传的电影编号>>>:').strip()
22         # 2 判断电影编号是否存在,不存在,重新输入
23         if not commen.exist_choice_num(choice_num, mv_list):
24             continue
25         # 运行到此步说明电影编号存在
26         # 3 发送电影
27 
28         # 3.1 制作报头
29         # 3.11 获取报头必要信息
30         # 获取电影名字
31         mv_name = mv_list[int(choice_num) - 1]
32         # 获取电影地址
33         mv_path = os.path.join(mv_dir_path, mv_name)
34         # 启发: 两行代码一般没必要定义成函数
35         # 3.12 制作
36         mv_header = {'mv_name': mv_name, 'mv_size': os.path.getsize(mv_path)}
37         # 3.2 发送报头
38         commen.send_header(client, mv_header)
39         # 3.3 发送电影
40         commen.up_file(client, mv_name, mv_path)
41         break
42 
43 # 客户端下载功能
44 def c_down():
45     # 接收客户端发来的电影列表信息
46     mv_list = commen.recv_header(client)
47     # 将电影以"枚举"方式展示给客户端
48     for i, j in enumerate(mv_list):
49         print(i + 1, j)
50     # 选择要下载的电影编号,以及后续下载相关
51     while True:
52         # 1 输入想要下载的电影编号
53         choice_num = input('请输入想要下载的电影编号>>>:').strip()
54         # 2 判断电影编号是否存在,若不存在,则重新输入电影编号
55         if not commen.exist_choice_num(choice_num, mv_list):
56             continue
57         # 3 输入的电影编号存在, 将电影编号发送给服务端
58         commen.send_header(client, choice_num)
59 
60         # 4 接收服务端的电影数据
61         # 1)接收电影报头
62         mv_header = commen.recv_header(client)
63         # 2)接收电影
64         mv_name = mv_header.get('mv_name')
65         mv_size = mv_header.get('mv_size')
66         commen.down_file(client, mv_name, mv_size)
67         break
68 
69 # 主程序
70 def c_run():
71     func_dict = {'1': c_up, '2': c_down}  # 注意函数名后无小括号,否则检查语法阶段就直接执行函数了
72     while True:
73         func_choice = input("请选择想要的功能:\r\n"
74                             "1: 上传电影\r\n"
75                             "2: 下载电影\r\n>>>:"
76                             ).strip()
77 
78         if not func_choice.isdigit():
79             print('输入不合法')
80             continue
81         if func_choice not in func_dict:
82             print('无该功能')
83             continue
84         func = func_dict.get(func_choice)
85         print(func)
86         # print('======')
87         # 将客户端选择的功能编号发送给服务端
88         commen.send_header(client, func_choice)
89         func()
90 
91 
92 c_run()
View Code

 

3 commen.py

 1 import json
 2 import struct
 3 
 4 # 发送报头,其实就是发送了某一种数据类型的数据(如一个字典)
 5 # 所以,当你想发送一个字典或列表等任意非文件的数据,都可以用如下函数发送.一步到位.
 6 def send_header(c_or_s, header):
 7     json_str_bt = json.dumps(header)
 8     # 打包报头
 9     pack_bt = struct.pack('i', len(json_str_bt))
10     # 发送打包的报头
11     c_or_s.send(pack_bt)
12     # 发送报头
13     c_or_s.send(json_str_bt.encode('utf-8'))
14 
15 def up_file(c_or_s, file_name, file_path):
16     with open(file_path, 'rb') as f:
17         for line in f:
18             c_or_s.send(line)
19     print('>>>:"%s"上传完了' % file_name)
20 
21 def recv_header(c_or_s):
22     # 接收打包的报头
23     pack_bt = c_or_s.recv(4)
24     # 解压报头
25     bt_size = struct.unpack('i', pack_bt)[0]
26     # 接收报头
27     bytes_bt = c_or_s.recv(bt_size)
28     # print(type(bytes_bt), bytes_bt.decode('utf-8'))
29     # 将报头转换成原数据类型
30     orig_header = json.loads(bytes_bt)
31     return orig_header
32 
33 def down_file(c_or_s, file_name, file_size):
34     with open(file_name, 'wb') as f:
35         recv_size = 0
36         while recv_size < file_size:
37             tmp_data = c_or_s.recv(1024)
38             recv_size += len(tmp_data)
39             f.write(tmp_data)
40     print('>>>:"%s"下载完了' % file_name)
41 
42 def exist_choice_num(choice_num, mv_list):
43     if not choice_num.isdigit():
44         print('输入无效!')
45         return False
46     choice_num = int(choice_num)
47     if choice_num not in range(1, len(mv_list) + 1):
48         print('您输入的编号不存在!')
49         return False
50     return True
View Code

 

~注: 本次代码服务端未实现并发(仅同时可链接一台客户端)

posted @ 2022-01-24 19:47  tslam  阅读(153)  评论(0编辑  收藏  举报