#7512
import select
import os,sys
import socket,queue
import time,hashlib
import json
import random
BASE_DIR=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BASE_DIR)
from conf import setting
class SelectFTPServer(object):
'''
FTP服务端
'''
SOCK_FAMALY = socket.AF_INET #IP v4
SOCK_TYPE = socket.SOCK_STREAM
SOCK_ALLOW_CONN = 1024
MSG_DICT = {}
INPUTS = []
OUTPUTS = []
READ ,WRITE ,EXCEPTION = None,None,None
TASK_STEP =0
def __init__(self, ip = 'localhost' ,port = 9999):
self.server = socket.socket(self.SOCK_FAMALY,self.SOCK_TYPE)
self.server.bind((ip,port))
self.server.listen(self.SOCK_ALLOW_CONN)
self.server.setblocking(False)
self.INPUTS.append(self.server)
print('服务器创建时间 : ', time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()))
self.interaction()
def com_get(self,get_con,recv_data):
os.chdir(setting.SERVER_DOWNLOAD) #切换工作目录
# self.MSG_DICT[get_con]['comunicate_data']['error'] = 222
print('com_get',self.MSG_DICT[get_con]['comunicate_data'])
if os.path.exists(recv_data['filename']):
if os.path.isfile(recv_data['filename']):
filename = setting.SERVER_DOWNLOAD +'\\%s'%recv_data['filename']
self.MSG_DICT[get_con]['comunicate_data']['error'] = 777
self.MSG_DICT[get_con]['comunicate_data']['size'] = os.stat(filename).st_size
self.MSG_DICT[get_con]['comunicate_data']['filename'] = filename
self.MSG_DICT[get_con]['user_stat']['task_step'] = 1
if get_con not in self.OUTPUTS:
self.OUTPUTS.append(get_con)
else:
pass
def com_put(self,put_con,recv_data):
os.chdir(setting.SERVER_UPLOAD)
self.MSG_DICT[put_con]['user_stat']['task_step'] = 1
if put_con not in self.OUTPUTS:
self.OUTPUTS.append(put_con)
else:
pass
def com_handle(self,recv_data):
recv_data = self.datahead(recv_data,'recv')
def data_format(self,rconn,recv_data):
# self.MSG_DICT[rconn]['comunicate_data'] = self.datahead(recv_data,'recv')
try:
if type(self.datahead(recv_data, 'recv')) is dict:
recv_data = self.datahead(recv_data,'recv')
action = recv_data['action']
if action is not None and hasattr(self,'com_%s'%action):
self.MSG_DICT[rconn]['comunicate_data'] = recv_data
func = getattr(self,'com_%s'%action)
func(rconn ,recv_data)
else:
if action == 'rspond_ok' and self.MSG_DICT[rconn]['user_stat']['task_step'] == 2:
self.MSG_DICT[rconn]['user_stat']['task_step'] = 3 #如果收到回复,任务状态进入第二步
self.file_obj = open(self.MSG_DICT[rconn]['comunicate_data']['filename'], 'rb')
except (json.decoder.JSONDecodeError,UnicodeDecodeError) :
self.MSG_DICT[rconn]['queue'].put(recv_data)
if rconn not in self.OUTPUTS:
self.OUTPUTS.append(rconn)
def connetion(self,rconn):
if rconn is self.server :
new_conn ,new_addr = self.server.accept()
new_conn.setblocking(False)
self.INPUTS.append(new_conn)
self.MSG_DICT[new_conn] = {}
self.MSG_DICT[new_conn]['queue'] = queue.Queue()
self.MSG_DICT[new_conn]['user_stat'] = {} #存储文件断点标记
self.MSG_DICT[new_conn]['user_stat']['task_step'] = 0 #任务状态
self.MSG_DICT[new_conn]['md5'] = hashlib.md5() #校验码
self.MSG_DICT[new_conn]['comunicate_data'] = {} #声明通信协议
print('新连接:[%s],' %repr(new_addr), '创建时间:', time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()))
else:
try:
recv_data = rconn.recv(1024)
if recv_data:
self.data_format(rconn,recv_data)
else:
print("空指令")
self.INPUTS.remove(rconn)
except ConnectionResetError as e:
self.INPUTS.remove(rconn)
if rconn in self.OUTPUTS:
self.OUTPUTS.remove(rconn)
del self.MSG_DICT[rconn]
print('2 \033[31;1m [%s] 连接中断,中断原因 [%s] ,中断时间:[%s] \033[0m'
%(repr(rconn.getpeername()),e,time.strftime('[%Y-%m-%d %H:%M:%S]',time.localtime())))
def sendable(self,wconn):
try:
if self.MSG_DICT[wconn]['comunicate_data']['action'] == 'get':
if self.MSG_DICT[wconn]['user_stat']['task_step'] == 1 : #第一步将文件信息发送
wconn.send(self.datahead(self.MSG_DICT[wconn]['comunicate_data'],'send'))
if self.MSG_DICT[wconn]['comunicate_data']['error'] == 777:
self.MSG_DICT[wconn]['user_stat']['file_flag'] = 0
self.MSG_DICT[wconn]['user_stat']['task_step'] = 2
else:
self.MSG_DICT[wconn]['user_stat']['task_step'] = 0
elif self.MSG_DICT[wconn]['user_stat']['task_step'] == 2:
pass
elif self.MSG_DICT[wconn]['user_stat']['task_step'] == 3: #第二步开始发送文件
self.file_obj.seek(self.MSG_DICT[wconn]['user_stat']['file_flag'])
size = self.MSG_DICT[wconn]['comunicate_data']['size']
data = self.file_obj.readline()
wconn.send(data)
self.MSG_DICT[wconn]['user_stat']['file_flag'] += len(data)
self.MSG_DICT[wconn]['md5'].update(data)
#传输完成
if self.MSG_DICT[wconn]['user_stat']['file_flag'] >= size:
self.MSG_DICT[wconn]['user_stat']['task_step'] = 0
self.MSG_DICT[wconn]['user_stat']['file_flag'] = 0
self.OUTPUTS.remove(wconn)
wconn.send(self.MSG_DICT[wconn]['md5'].hexdigest().encode())
self.file_obj.close()
else:
print('\033[31;1m 文件不存在! \033[0m')
self.OUTPUTS.remove(wconn)
elif self.MSG_DICT[wconn]['comunicate_data']['action'] == 'put':
if self.MSG_DICT[wconn]['user_stat']['task_step'] == 1:
wconn.send(b'rspond_ok')
self.OUTPUTS.remove(wconn)
self.MSG_DICT[wconn]['user_stat']['task_step'] = 2
self.MSG_DICT[wconn]['user_stat']['file_flag'] = 0
self.file_put = open(self.MSG_DICT[wconn]['comunicate_data']['filename']+'%d.dat'%random.randint(1,10000), 'wb')
elif self.MSG_DICT[wconn]['user_stat']['task_step'] == 2:
try:
data = self.MSG_DICT[wconn]['queue'].get_nowait()
except queue.Empty as e:
pass
# self.OUTPUTS.remove(wconn)
else:
self.file_put.seek(self.MSG_DICT[wconn]['user_stat']['file_flag'])
self.file_put.write(data)
self.MSG_DICT[wconn]['user_stat']['file_flag'] += len(data)
percentage = float( self.MSG_DICT[wconn]['user_stat']['file_flag'] / self.MSG_DICT[wconn]['comunicate_data']['size']) * 100
print('%.2f'%percentage)
if self.MSG_DICT[wconn]['user_stat']['file_flag'] >= self.MSG_DICT[wconn]['comunicate_data']['size']:
self.OUTPUTS.remove(wconn)
self.MSG_DICT[wconn]['user_stat']['task_step'] = 0
print("上传成功")
except KeyError as e:
print('1 \033[31;1m [%s] 连接中断,中断原因 [%s] ,中断时间:[%s] \033[0m'
% (repr(wconn.getpeername()), e, time.strftime('[%Y-%m-%d %H:%M:%S]', time.localtime())))
self.INPUTS.remove(wconn)
if wconn in self.OUTPUTS:
self.OUTPUTS.remove(wconn)
del self.MSG_DICT[wconn]
def excepthandle(self,econn):
pass
def interaction(self):
while True:
self.READ , self.WRITE ,self.EXCEPTION = select.select(self.INPUTS,self.OUTPUTS,self.INPUTS)
for rconn in self.READ:
self.connetion(rconn)
for wconn in self.WRITE:
self.sendable(wconn)
for econn in self.EXCEPTION:
self.excepthandle(econn)
def initiate(self):
pass
def datahead(self,data,flag):
if flag == 'send':
return json.dumps(data).encode()
elif flag == 'recv':
return json.loads(data.decode())
else:
print("调用出错")
if __name__ == '__main__':
server = SelectFTPServer()