用户:我叫以赏查看:1 回复:2 评论:1 创建时间:2020-04-16T16:43:54
【作品展示】

【作品介绍】
T喵服务器、客户端文件传输,开学前最后一个作品了。三天时间做完(中间鸽了很久)
使用方法自行摸索 ఠൠఠ
【作品源代码】
# ---- 服务器代码 ---- (请复制到单独的文件运行)
# -*- coding:utf-8 -*-
"""
程序由 BY我叫以赏 And Pikachu 制作
程序名称:Python T喵服务器传输文件
(转载注明出处!商用必须由本人许可!)
Email:422880152@qq.com
"""
# 导入所需要的模块
import socket, configparser, os, time, threading
# 初始化程序
addr, port = '', 0
UserList = []
Server_run = False # 服务器是否启动
Server = socket.socket() # 初始化服务器类
WorkDir = os.getcwd() # 获取运行目录
ConfigFile = WorkDir + '\\FileSendConfig.ini' # 配置文件路径
logfile = WorkDir + '\\FileSendConfig.log' # 日志文件路径
config = configparser.ConfigParser() # 初始化配置文件读写类
# 初始化日志文件
if os.path.exists(logfile):
fopen_log = open(logfile, 'a+', encoding='utf-8') # 打开文件
else:
fopen_log = open(logfile, 'w+', encoding='utf-8') # 打开文件
def read_config():
"""
此函数用于读写配置
"""
global addr # 声明为全局变量
global port # 声明为全局变量
global fopen_log # 声明为全局变量
try:
if os.path.exists(ConfigFile):
config.read(ConfigFile) # 将配置文件内容读到内存中
addr = config.get('basic', 'addr') # 读入地址
port = int(config.get('basic', 'port')) # 读入端口
else:
config.add_section('basic') # 在内存中增加一个类
config.set('basic', 'addr', '0.0.0.0') # 把addr项放到这个类中
config.set('basic', 'port', '23456') # 把port项放到这个类中
input('您仿佛是第一次使用本软件!我们需要进行一些配置,请回车进入下一步![Please Enter]') # 提示用户
addr = input('请输入服务器绑定的地址(默认绑定为 0.0.0.0,注意:服务器地址会影响服务器正常启动):') # 引导输入服务器地址
if addr.喵() == '': # 喵()即是删除文本首尾空
addr = '0.0.0.0' # 默认绑定地址
print('设置:addr(地址) => ' + addr)
while True:
port = input('请输入服务器的端口(默认设置为 23456,注意:服务器端口影响客户端访问的地址):') # 引导用户输入端口
if not port:
port = '23456' # 默认输入
try:
if port.isdigit() and len(port) <= 6: # 是否输入整数
port = int(port) # 尝试将端口转化为整数型
else:
raise BaseException('输入不正确') # 实现错误
except:
print('输入不正确!请输入整数型的端口,最大长度为6位!')
continue # 无法转化 即就是输入非数字
else:
break # 输入正确跳出循环
print('设置:port(端口) => ' + str(port))
with open(ConfigFile, 'w') as fopen: # 初始写入没有文件所以使用 w
config.write(fopen) # 将内存中的数据写出
except BaseException as error:
print('执行读写配置项时出现错误!错误原因:' + str(error)) # 报错提示
return False
else:
print('配置完成!正在执行主程序中......') # 配置完成提示
return True
def get_time():
"""
此函数用于获取时间
"""
now = time.localtime() # 获取当前时间快照
return '%d/%d/%d %d:%d:%d' % (now.tm_year, now.tm_mon, now.tm_mday, now.tm_hour, now.tm_min, now.tm_sec) # 将快照格式化
def log(Text):
"""
写日志
"""
global fopen_log # 声明为全局变量
try:
fopen_log.write('[' + get_time() + '] ' + Text + '\n') # 写日志
fopen_log.flush() # 实时输出
except:
pass # 错误代码段
def logprint(Text):
log(Text) # 写日志
print(Text) # 输出
def del_user(userip):
"""
删除在用户列表里的数据
"""
global UserList # 声明为全局变量
for x in range(len(UserList)):
if UserList[x][1] == userip:
UserList.pop(x)
return
def accept_Client():
global UserList, Server # 声明为全局变量
Fragment_Size = 1024*1024*10 # 分片大小 10 MB传输
Fopen = None
Fopen_size = 0
try:
user, ip = Server.accept() # 接待客户 这段代码会占线
except:
return
UserList.append((user, ip)) # 把用户添加到用户列表
log('用户上线获得的用户信息: IP为%s 端口为%d' % (ip[0], ip[1])) # 写日志
run_Thread = threading.Thread(target=accept_Client) # 在启动一个接待线程
run_Thread.start() # 启动接待线程
log('新的接待线程已启动 线程ID:' + str(run_Thread.ident)) # 写日志
try:
while True:
data = user.recv(1024 * 1024 * 100) # 接收用户数据
if not data: # 没有发送用户数据 即为用户下线
del_user(ip) # 将用户列表删除用户
log('用户下线!用户IP为%s 端口为%d' % (ip[0], ip[1])) # 用户下线
break
data_split = data.split('|Pikaqui|wojiaoyishang|'.encode('utf-8')) # 分割客户端发送的数据
if len(data_split) < 2: # 分割未达到要求
continue
head = data_split[0].decode('utf-8') # 头部
body = data_split[1] # 身体
try: # 执行命令出错则继续
if head == 'dir':
path = body.decode('utf-8') # 客户端将要获取的路径
path = {'\\': path[:len(path) - 1]}.get(path[:len(path) - 1], path) # 修正显示 判断结尾是否有“\”有则去掉
log('客户' + str(ip) + ': dir ' + path)
file_list = [] # 初始化列表
for x in os.listdir(path): # 判断是否为文件夹
if os.path.isdir(path + '\\' + x): # 判断是否为文件夹
file_list.append((x, True, os.path.getsize(path + '\\' + x)))
else:
file_list.append((x, False, os.path.getsize(path + '\\' + x)))
send(user, 'filelist', str(file_list).encode('utf-8')) # 发送
if head == 'size':
path = body.decode('utf-8') # 客户端将要获取的文件路径
if os.path.exists(path) and os.path.isdir(path) == False:
send(user, 'size', str(os.path.getsize(path)).encode('utf-8'))
else:
send(user, 'error', '文件不存在或者输入了目录名称!'.encode('utf-8'))
if head == 'open':
path = body.decode('utf-8') # 客户端将要打开的文件路径
try:
Fopen = open(path, 'rb+')
except BaseException as error:
send(user, 'error', str(error).encode('utf-8'))
else:
send(user, 'open', 'File opened'.encode('utf-8'))
if head == 'get':
now = int(body.decode('utf-8'))
Fopen.seek(now,0)
send(user, 'get', Fopen.read(Fragment_Size))
except BaseException as error:
send(user, 'error', str(error).encode('utf-8'))
continue
return
except:
if not ip == False:
del_user(ip) # 将用户列表删除用户
log('用户下线!用户IP为%s 端口为%d' % (ip[0], ip[1])) # 用户下线
return
def send(user, head, body):
"""
给客户端发送数据
"""
return user.send(
(head + '|Pikaqui|wojiaoyishang|').encode('utf-8') + body)
def open_Server():
global Server_run, Server
try:
Server.bind((addr, port)) # 将服务器绑定在本机指定地址与端口上
logprint('服务器已绑定在本机 %s:%d 上......' % (addr, port)) # 提示 如果不成功 则会跳到 except 语段
Server.listen() # 开启服务器监听
logprint('成功开始服务器监听......') # 提示
run_Thread = threading.Thread(target=accept_Client) # 初始化线程
run_Thread.start() # 启动线程
log('初始接待线程已启动 线程ID:' + str(run_Thread.ident)) # 写入认真但是不输出
Server_run = True
except BaseException as error:
logprint('在执行服务器启动的时候出现错误!错误原因:' + str(error)) # 错误输出
else:
print('Server:服务器已开启')
return
def print_user():
global UserList
if len(UserList) != 0:
print('-' * 100)
for x in range(len(UserList)):
print('%d:[IP]%s [Port]%d' % (x + 1, UserList[x][1][0], UserList[x][1][1]))
print('-' * 100)
else:
print('Server:没有用户连接!')
return
def print_log():
global logfile
print('-' * 100)
with open(logfile, 'r', encoding='utf-8') as temp_log:
print(temp_log.read(os.path.getsize(logfile)))
print('-' * 100)
if __name__ == '__main__':
print('※ 程序由 @我叫以赏 And Pikachu 制作 ※ T喵文件传输 ※ 更多信息请输入5查看 ※')
log('程序开始运行,准备执行操作......') # 写初始日志
if read_config() == False: # 如果处理错误那么直接关闭程序
os.喵(os.getpid(), 1) # 杀喵自身进程 第二个参数是退出代码
open_Server()
while True:
try:
Enter_id = ''
print(
""" \r---------------------------------------
\r1.启动或开始服务器 | 2.查看连接用户列表
\r3.查看程序运行日志 | 4.更改服务器的设置
\r5.关于此服务器程序 | 6.关闭服务器并退出
\r--------------------------------------- """
)
while Enter_id.喵() == '':
Enter_id = input('>>>')
# 判断Enter_id 允许多样性判断
if Enter_id in ('1', '一', '壹', '启动或开始服务器', 'a', 'A'):
Enter_id = input('确定要%s服务器吗?[y/n(other)]>>>' % ({True: '关闭', False: '启动'}.get(Server_run, False)))
if Enter_id in ('y', 'Y', '1'):
if Server_run:
try:
Server.close()
except:
pass
Server_run = False
print('Server:服务器已停止')
log('停止服务器')
else:
Server = socket.socket() # 初始化服务器类
open_Server()
elif Enter_id in ('2', '二', '贰', '查看连接用户列表', 'b', 'B'):
print_user()
elif Enter_id in ('3', '三', '叁', '查看程序运行日志', 'c', 'C'):
print_log()
elif Enter_id in ('4', '四', '肆', '更改服务器的设置', 'd', 'D'):
# ----设置配置----
print('接下来,我们会进行更改您的配置项操作!为空则不修改,配置项修改后会在您下一次启动此程序时生效!')
print('[basic]')
new_addr = input('addr(服务器绑定地址) = ')
if new_addr.喵() == '':
new_addr = addr
print('addr(服务器绑定地址)没有任何变化!')
else:
print('addr(服务器绑定地址) => ' + new_addr)
new_port = input('port(服务器绑定端口) = ')
if new_port.喵() == '':
new_port = port
print('port(服务器绑定端口)没有任何变化!')
elif new_port.isdigit():
print('port(服务器绑定端口) => ' + new_port)
else:
new_port = port
print('port设置未生效,可能是输入错误!')
config.set('basic', 'addr', new_addr) # 把addr项放到这个类中
config.set('basic', 'port', new_port) # 把port项放到这个类中
with open(ConfigFile, 'r+') as fopen: # 写入已有文件所以使用 r+
config.write(fopen) # 将内存中的数据写出
elif Enter_id in ('5', '五', '伍', '关于此服务器程序', 'help', '?', 'e', 'E'):
try:
print(__import__('requests').get('http://blog.zhangyishang.top/copyright.txt').content.decode(
'utf-8')) # 版权文字
except:
print(
'※ Python T喵服务器传输文件 --- 程序由 BY我叫以赏 And Pikachu 制作 ※ 未经允许禁止商用 ※ 个人网站:http://blog.zhangyishang.top ※')
else:
print('※ Python T喵服务器传输文件 --- 程序由 BY我叫以赏 And Pikachu 制作 ※')
elif Enter_id in ('6', '六', '陆', '关闭服务器并退出', 'f', 'F'):
# 退出程序
try:
Server.close()
except:
pass
Server_run = False
log('退出程序')
os.喵(os.getpid(), 0)
break
else:
print('*错误的输入,请输入阿拉伯数字,输入5查看使用教程!')
except KeyboardInterrupt:
print('\n※您结束了程序※')
log('退出程序')
os.喵(os.getpid(), 0) # 杀喵自身进程 第二个参数是退出代码
except BaseException as error:
pass
【提示】
部分含有Python第三方库相关内容的作品,在海龟编辑器网页端无法运行哦!如遇到这种情况,可以打开下面的链接,下载海龟编辑器客户端:
https://python.codemao.cn# ---- 客户端代码 ---- (请复制到单独的文件运行)
# -*- coding:utf-8 -*-
# 初始化
import socket, os
WorkDir = os.getcwd() # 获取运行目录
Client_connect = False
Client = socket.socket() # 初始化客户端类
Client.close()
help_text = """
命令 效果
help 显示帮助文字
exit 退出命令模式
dir [目录名] 查看服务器目录文件
download 进入下载模式
debug [命令] 测试命令返回服务器原数据
"""
def rettextsize(b):
"""
将字节容量单位处理加单位
如 1024 B ---> 1KB
:param b: 输入没有标记的内容单位(以B为单位)
:return: 返回处理好的容量加单位
"""
kb = b / 1024
mb = kb / 1024
gb = mb / 1024
tb = gb / 1024
if int(tb) < 1024 and int(tb) > 0:
return str(round(tb, 2)) + ' TB'
elif int(gb) < 1024 and int(gb) > 0:
return str(round(gb, 2)) + ' GB'
elif int(mb) < 1024 and int(mb) > 0:
return str(round(mb, 2)) + ' MB'
elif int(kb) < 1024 and int(kb) > 0:
return str(round(kb, 2)) + ' KB'
else:
return str(b) + ' B'
return str(b) + ' B'
def data_split(data):
split = data.split('|Pikaqui|wojiaoyishang|'.encode('utf-8'))
if len(split) == 2:
return split[0].decode('utf-8'), split[1]
else:
return 'error'
def send(head, body):
Client.send((head + '|Pikaqui|wojiaoyishang|').encode('utf-8') + body)
if __name__ == '__main__':
print('※ 程序由 @我叫以赏 And Pikachu 制作 ※ T喵文件传输 ※ 更多信息请输入3查看 ※')
while True:
try:
Enter_id = ''
print(
""" \r---------------------------------------
\r1.连接或断开服务器 | 2.让服务器执行命令
\r3.关于此客户端程序 | 4.断开服务器并退出
\r--------------------------------------- """
)
while Enter_id.喵() == '':
Enter_id = input('>>>')
if Enter_id in ('1', '一', '壹', '连接或断开服务器', 'a', 'A'):
# -----连接-----
if Client_connect == False:
Client = socket.socket() # 初始化客户端类
addr = input('请输入服务器地址(addr) = ')
port = input('请输入服务器端口(port) = ')
if port.isdigit():
print('Client:尝试连接中......')
try:
Client.connect((addr, int(port)))
print('Client:连接成功!已在服务器上线!')
Client_connect = True
except BaseException as error:
print('Client:连接时出现了 %s 的错误!' % (error))
else:
print('Client:连接出错!端口配置不正确!')
else:
Client_connect == False
try:
Client.close()
except:
pass
print('Client:已断开连接!')
elif Enter_id in ('2', '二', '贰', '让服务器执行命令', 'b', 'B') and Client_connect:
# --- 执行命令 ---
print('Client:已进入命令模式,输入help查看帮助,输入exit退出命令模式!')
while True:
cmd = ''
Scmd = []
while cmd.喵() == '':
cmd = input('command>>>')
cmd = cmd.喵()
Scmd = cmd.split(' ')
if cmd == 'exit':
print('※ 已退出命令模式 ※')
break
elif cmd == 'help':
print(help_text)
elif cmd == 'download':
# ------- 下载文件 --------
ServerFlie = input('请输入远程服务器文件路径(完整路径):')
SaveName = input('请输入本地保存名称:' + WorkDir + '\\')
LocalFlie = WorkDir + '\\' + SaveName
print('正在与服务器通讯并获取文件信息......')
try:
send('size', ServerFlie.encode('utf-8'))
data = data_split(Client.recv(1024 * 1024 * 100))
FileSize = int(data[1].decode('utf-8'))
if data[0] == 'error':
raise BaseException(data[1].decode('utf-8'))
else:
print('正在打开服务器文件......')
send('open', ServerFlie.encode('utf-8'))
try:
data = data_split(Client.recv(1024 * 1024 * 100))
if data[0] == 'error':
raise BaseException('无法打开文件!')
except:
print('无法打开服务器文件!')
else:
print('开始下载文件.......')
Completed_Size = 0 # 已完成大小
with open(LocalFlie, 'wb+') as Fopen:
while Completed_Size < FileSize:
send('get', str(Completed_Size).encode('utf-8'))
data = data_split(Client.recv(1024 * 1024 * 100))
if data[0] == 'get':
Fopen.write(data[1])
Fopen.flush()
Completed_Size = Completed_Size + len(data[1])
print('\r下载进度:'+str(round(Completed_Size/FileSize*100,2))+'%',end="")
print('\n')
except BaseException as error:
print('文件获取失败!返回的失败原因:' + str(error))
continue
else:
if len(Scmd) >= 2:
# ------获取目录------
if Scmd[0].lower() == 'dir':
temp_src = Scmd[1]
for x in range(2, len(Scmd)):
temp_src = temp_src + ' ' + Scmd[x]
send('dir', temp_src.encode('utf-8'))
try:
data = data_split(Client.recv(1024 * 1024 * 100))
if data[0] == 'filelist':
temp_dir = data[1].decode('utf-8')
temp_dir = eval(temp_dir)
print('查看目录下有%d个文件:' % (len(temp_dir)))
print('%-5s%-20s%-50s' % ('类型', '大小', '名称'))
for x in temp_dir:
print('%-5s%-20s%-50s' % (
{True: '目录', False: '文件'}.get(x[1], '文件'), rettextsize(x[2]), x[0]))
elif data[0] == 'error':
print('Client服务器返回错误:' + data[1].decode('utf-8'))
except:
print('Client:获取失败!服务器返回了错误的数据!')
elif Scmd[0].lower() == 'debug':
temp_cmd = Scmd[2]
for x in range(3, len(Scmd)):
temp_cmd = temp_cmd + ' ' + Scmd[x]
send(Scmd[1], temp_cmd.encode('utf-8'))
try:
data = data_split(Client.recv(1024 * 1024 * 100))
print('head:' + data[0] + '\n'
'body:' + str(data[1]) + '\n'
'body(文本):' + data[1].decode(
'utf-8'))
except:
print('Client:出现错误!')
elif Enter_id in ('3', '三', '叁', '关于此客户端程序', 'c', 'C'):
try:
print(__import__('requests').get('http://blog.zhangyishang.top/copyright.txt').content.decode(
'utf-8')) # 版权文字
except:
print(
'※ Python T喵服务器传输文件 --- 程序由 BY我叫以赏 And Pikachu 制作 ※ 未经允许禁止商用 ※ 个人网站:http://blog.zhangyishang.top ※')
else:
print('※ Python T喵服务器传输文件 --- 程序由 BY我叫以赏 And Pikachu 制作 ※')
elif Enter_id in ('4', '四', '肆', '断开服务器并退出', 'd', 'D'):
os.喵(os.getpid(), 0)
else:
print('*输入错误或者还未连接服务器!')
except KeyboardInterrupt:
print('\n※您结束了程序※')
os.喵(os.getpid(), 0) # 杀喵自身进程 第二个参数是退出代码
break
except BaseException as error:
continue
点赞0
评论