一、用戶md5認證python
二、支持多用戶同時登錄(併發)shell
三、進入用戶的命令行模式,支持cd切換目錄,ls查看目錄子文件服務器
四、執行命令(ipconfig)併發
五、傳輸文件:app
a、支持斷點續傳socket
b、傳輸中顯示進度條測試
a、客戶端僅提供用戶名和密碼,選擇登陸或註冊,
b、服務器端進行註冊並將加密後的密碼寫入文件,最後返回給客戶端是否登陸或註冊成功加密
a、客戶端輸入命令,服務器端處理並返回給客戶端spa
a、客戶端發送須要執行的命令
b、服務器端執行命令,並返回客戶端須要接收該命令的次數s=r[0]+1,其中r=divmod(結果總長度,1024)
c、客戶端收到次數,告訴服務端已經收到
d、服務端發送執行結果,客戶端進行for循環接收該結果命令行
a、客戶端輸入文件路徑(測試版路徑爲:f.png),發送文件名和文件大小
b、服務器端檢測指定目錄是否含有該文件,若是沒有,返回給客戶端字符串s,即從頭開始發送start,has_recv=0
若是有,即須要斷點續傳,返回給客戶端已經上傳了多少has_recv
c、客戶端接收返回值,並seek到has_recv的位置,進行循環收發,打印當前進度,直到傳輸完畢。
注:本程序可循環接收用戶選擇傳輸文件和執行命令
#!/usr/bin/env python # -*- coding: utf-8 -*- import os BASE_DIR = os.path.dirname(os.path.dirname(__file__)) #配置文件的上層目錄 NEW_FILENAME=os.path.join(BASE_DIR,'view') #新文件目錄 NAME_PWD=os.path.join(BASE_DIR,'db','name_pwd') #用戶名和密碼目錄 USER_FILE=os.path.join(BASE_DIR,'db')
#!/usr/bin/env python # -*- coding: utf-8 -*- import sys,os import time import socket import hashlib import pickle import subprocess import socketserver sys.path.append(os.path.dirname(os.path.dirname(__file__))) from config import settings new=settings.NEW_FILENAME class Myserver(socketserver.BaseRequestHandler): def recv_file(self): ''' 文件傳輸 :return: ''' conn=self.request a=str(conn.recv(1024),encoding='utf-8') file_size,file_name=a.split(',') new_file_name=os.path.join(new,file_name) if file_name in new: #檢測文件是否已存在,涉及斷點續傳 has_recv=os.stat(new).st_size #計算臨時文件大小 conn.sendall(bytes(has_recv,encoding='utf-8')) with open(new_file_name,'ab') as f: #追加模式 while has_recv<=int(file_size): data=conn.recv(1024) f.write(data) has_recv+=len(data) else: has_recv=0 conn.sendall(bytes('s',encoding='utf-8')) # 客戶端收到字符串s,從0開始發送 with open(new_file_name,'wb') as f: while has_recv<=int(file_size): data=conn.recv(1024) f.write(data) has_recv+=len(data) def command(self): ''' 執行命令 :return: ''' conn=self.request a=conn.recv(1024) ret=str(a,encoding='utf-8') ret2 = subprocess.check_output(ret, shell=True) r=divmod(len(ret2),1024) s=r[0]+1 #客戶端須要接收的次數 conn.sendall(bytes(str(s),encoding='utf-8')) conn.recv(1024) #確認客戶端收到須要接收的次數 conn.sendall(ret2) def md5(self,pwd): ''' 對密碼進行加密 :param pwd: 密碼 :return: ''' hash=hashlib.md5(bytes('xx7',encoding='utf-8')) hash.update(bytes(pwd,encoding='utf-8')) return hash.hexdigest() def login(self,usrname,pwd): ''' 登錄 :param usrname: 用戶名 :param pwd: 密碼 :return:是否登錄成功 ''' conn=self.request s=pickle.load(open(settings.NAME_PWD,'rb')) if usrname in s: if s[usrname]==self.md5(pwd): #和加密後的密碼進行比較 return True else: return False else: return False def regist(self,usrname,pwd): ''' 註冊 :param usrname: 用戶名 :param pwd: 密碼 :return:是否註冊成功 ''' conn=self.request s=pickle.load(open(settings.NAME_PWD,'rb')) if usrname in s: return False else: s[usrname]=self.md5(pwd) mulu=os.path.join(settings.USER_FILE,usrname) os.makedirs(mulu,'a') pickle.dump(s,open(settings.NAME_PWD,'wb')) return True def before(self,usrname,pwd,ret): ''' 判斷註冊和登錄,並展現用戶的詳細目錄信息,支持cd和ls命令 :return: ''' conn=self.request if ret=='1': r=self.login(usrname,pwd) if r: conn.sendall(bytes('y',encoding='utf-8')) else: conn.sendall(bytes('n',encoding='utf-8')) elif ret=='2': # print(usrname,pwd) r=self.regist(usrname,pwd) if r: conn.sendall(bytes('y',encoding='utf-8')) else: conn.sendall(bytes('n',encoding='utf-8')) def usr_file(self,usrname): ''' 展現用戶的詳細目錄信息,支持cd和ls命令 :param usrname: 用戶名 :return: ''' conn=self.request conn.recv(1024) mulu=os.path.join(settings.USER_FILE,usrname) conn.sendall(bytes(mulu,encoding='utf-8')) while True: b=conn.recv(1024) ret=str(b,encoding='utf-8') try: a,b=ret.split(' ',1) except Exception as e: a=ret if a=='cd': if b=='..': mulu=os.path.dirname(mulu) else: mulu=os.path.join(mulu,b) conn.sendall(bytes(mulu,encoding='utf-8')) elif a=='ls': ls=os.listdir(mulu) print(ls) a=','.join(ls) conn.sendall(bytes(a,encoding='utf-8')) elif a=='q': break def handle(self): conn=self.request conn.sendall(bytes('welcome',encoding='utf-8')) b=conn.recv(1024) ret=str(b,encoding='utf-8') print(ret) conn.sendall(bytes('b ok',encoding='utf-8')) c=conn.recv(1024) r=str(c,encoding='utf-8') usrname,pwd=r.split(',') self.before(usrname,pwd,ret) #登錄或註冊驗證 self.usr_file(usrname) #展現用戶的詳細目錄信息,支持cd和ls命令 while True: a=conn.recv(1024) conn.sendall(bytes('收到a',encoding='utf-8')) ret=str(a,encoding='utf-8') if ret=='1': self.recv_file() # conn.sendall(bytes('file ok',encoding='utf-8')) elif ret=='2': self.command() elif ret=='q': break else: pass if __name__=='__main__': sever=socketserver.ThreadingTCPServer(('127.0.0.1',9999),Myserver) sever.serve_forever()
#!/usr/bin/env python # -*- coding: utf-8 -*- import sys import time import os import socket sys.path.append(os.path.dirname(os.path.dirname(__file__))) from config import settings def send_file(file_path): ''' 發送文件 :param file_name:文件名 :return: ''' size=os.stat(file_path).st_size file_name=os.path.basename(file_path) obj.sendall(bytes(str(size)+','+file_name,encoding='utf-8')) #發送文件大小和文件名 ret=obj.recv(1024) #接收已經傳了多少 r=str(ret,encoding='utf-8') if r=='s': #文件不存在,從頭開始傳 has_send=0 else: #文件存在 has_send=int(r) with open(file_path,'rb') as f: f.seek(has_send) #定位到已經傳到的位置 while has_send<size: data=f.read(1024) obj.sendall(data) has_send+=len(data) sys.stdout.write('\r') #清空文件內容 time.sleep(0.2) sys.stdout.write('已發送%s%%|%s' %(int(has_send/size*100),(round(has_send/size*40)*'★'))) sys.stdout.flush() #強制刷出內存 print("上傳成功\n") def command(command_name): ''' 執行命令 :param command_name: :return: ''' obj.sendall(bytes(command_name,encoding='utf-8')) ret=obj.recv(1024) #接收命令須要接收的次數 obj.sendall(bytes('收到次數',encoding='utf-8')) r=str(ret,encoding='utf-8') for i in range(int(r)): #共須要接收int(r)次 ret=obj.recv(1024) #等待客戶端發送 r=str(ret,encoding='GBK') print(r) def login(usrname,pwd): ''' 登錄 :param usrname:用戶名 :param pwd:密碼 :return:是否登錄成功 ''' obj.sendall(bytes(usrname+','+pwd,encoding='utf-8')) ret=obj.recv(1024) r=str(ret,encoding='utf-8') if r=='y': return 1 else: return 0 def regist(usrname,pwd): ''' 註冊 :param usrname:用戶名 :param pwd:密碼 :return:是否註冊成功 ''' obj.sendall(bytes(usrname+','+pwd,encoding='utf-8')) ret=obj.recv(1024) r=str(ret,encoding='utf-8') if r=='y': return 1 else: return 0 def before(usrname,pwd): ''' 選擇登錄或註冊,展現用戶的詳細目錄信息,支持cd和ls命令 :return: ''' a=input('請選擇1.登錄 2.註冊') obj.sendall(bytes(a,encoding='utf-8')) obj.recv(1024) if a=='1': ret=login(usrname,pwd) if ret: print('登錄成功') return 1 else: print('用戶名或密碼錯誤') return 0 elif a=='2': ret=regist(usrname,pwd) if ret: print('註冊成功') return 1 else: print('用戶名已存在') return 0 def usr_file(usrname): obj.sendall(bytes('打印用戶文件路徑',encoding='utf-8')) ret=obj.recv(1024) #等待客戶端發送 r=str(ret,encoding='utf-8') print(r) while True: a=input('輸入cd切換目錄,ls查看目錄詳細信息,q退出>:') obj.sendall(bytes(a,encoding='utf-8')) if a=='q': break else: ret=obj.recv(1024) #等待客戶端發送 r=str(ret,encoding='utf-8') if len(r)==1:#判斷是cd結果仍是ls的結果(ls只有一個子目錄也能夠直接打印) print(r) else: li=r.split(',') for i in li: print(i) #打印每個子目錄 def main(usrname,pwd): ret=obj.recv(1024) #等待客戶端發送 r=str(ret,encoding='utf-8') print(r) result=before(usrname,pwd)#登錄或註冊 if result: usr_file(usrname) while True: a=input('請選擇1.傳文件 2.執行命令 q退出:') obj.sendall(bytes(str(a),encoding='utf-8')) ret=obj.recv(1024) #確認是否收到a r=str(ret,encoding='utf-8') print(r) if a=='1': b=input('請輸入文件路徑(測試版路徑爲:f.png):') # b='f.png' if os.path.exists(b): send_file(b) obj.sendall(bytes('hhe',encoding='utf-8')) # obj.recv(1024) elif a=='2': b=input('請輸入command:') command(b) elif a=='q': break else: print('輸入錯誤') obj.close() if __name__ == '__main__': obj=socket.socket() #建立客戶端socket對象 obj.connect(('127.0.0.1',9999)) usrname=input('請輸入用戶名') pwd=input('請輸入密碼') main(usrname,pwd)