fileno()文件描述符 handle_request()處理單個請求 server_forever(poll_interval=0.5)處理多個請求,poll_interval每0.5秒檢測是否關閉, 作業:開發一個支持多用戶線上的FTP程式 要求: 1.用戶加密認證; 2.允許同時多用戶登錄; ...
fileno()文件描述符
handle_request()處理單個請求
server_forever(poll_interval=0.5)處理多個請求,poll_interval每0.5秒檢測是否關閉,
作業:開發一個支持多用戶線上的FTP程式
要求:
1.用戶加密認證;
2.允許同時多用戶登錄;
3.每個用戶有自己的家目錄,且只能訪問自己的家目錄;
4.對用戶進行磁碟配額,每個用戶的可用空間不同;
5.允許用戶在ftp.server上隨意切換目錄;
6.允許用戶查看當前目錄下文件;
7.允許上傳和下載文件,保證文件一致性;
8.文件傳輸過程中顯示進度條;
9.附加功能,支持文件的斷點續傳。
發送文件的大致思路:
接收文件的大致思路:
遍歷空的文件,如果一個文件是空的,Python是不會執行後面縮進的代碼的,空的文件。
http://www.cnblogs.com/liujiacai/p/7417953.html
http://www.cnblogs.com/lianzhilei/p/5869205.html
http://www.bubuko.com/infodetail-1758633.html
由於不是做運維的,對一些系統的操作不是很熟悉,因此首先完成了文件的上傳與下載:下麵來看看這兩個功能:
客戶端:
import socket,os,json,sys class Myclient(object): '''定義客戶端''' def __init__(self): '''定義socket()實例''' self.client = socket.socket() #生成soket()實例 def connect(self,ip,port): """定義連接""" self.client.connect((ip,port)) def interactive(self): '''定義和用戶交互''' while True: cmd = input("請輸入指令>>:").strip() #用戶輸入指令 if len(cmd) == 0: print("指令不能為空!!!") continue #指令不能為空 cmd_str = cmd.split()[0] #指令由兩種形式 if hasattr(self,cmd_str): #判斷指令是否存在 func = getattr(self,cmd_str) #指令存在,則獲取對應的方法 func(cmd) elif cmd_str == 'q': break else: self.help() continue def help(self): msg = """ ls pwd cd put filename get filename """ print("指令名稱",msg) def put(self,cmd): '''定義上傳數據介面''' cmd_list = cmd.split() if len(cmd_list) > 1: '''指令是由指令名和文件名構成''' filename = cmd_list[1] #獲取文件名 if os.path.isfile(filename): #檢測上傳的文件名是否存在 '''文件名存在,則要告訴伺服器端上傳的文件名,文件大小,並檢測伺服器是否有相應的方法''' filesize = os.stat(filename).st_size msg_dic = { "action":"put", "filename":filename, "filesize":filesize } '''轉換為位元組碼的形式進行傳輸''' self.client.send(json.dumps(msg_dic).encode("utf-8")) #轉換為json格式進行上傳,便於伺服器接收後處理 server_response = self.client.recv(1024) #等待伺服器傳回信息,防止粘包 self.client.send("收到了".encode("utf-8")) if int(server_response.decode("utf-8")): '''根據客戶端反饋,伺服器端是否存在相同的文件名''' file_exist = int(self.client.recv(1024).decode("utf-8")) if file_exist: '''伺服器端存在相應的文件''' while True: cover_flag = input("文件已存在,是否覆蓋原文件(y/n):") if cover_flag == "y": self.client.send("y".encode("utf-8")) break elif cover_flag == "n": new_filename = input("請輸入新的文件名>>:") self.client.send(("n "+new_filename).encode("utf-8")) break else: print("您輸入的指令有誤,請重新輸入!!!") continue else: self.client.send("新創建".encode("utf-8")) '''上面準備工作完畢,開始傳輸文件''' with open(filename,'rb') as f: for line in f: self.client.send(line) #發送數據 '''客戶端等待伺服器端傳來消息,告知上傳成功''' response = self.client.recv(1024) print(response.decode("utf-8")) #數據是否上傳成功,告知用戶 else: print("伺服器端不存在相應的指令!!!") else: print("要上傳的文件名不存在!") else: print("沒有輸入要上傳的文件名!!!") def get(self,cmd): '''從伺服器端下載數據''' cmd_list = cmd.split() #獲取用戶輸入指令 if len(cmd_list) > 1: #是由指令和文件名構成 filename = cmd_list[1] msg_dic = { "filename":filename, "action":"get" } #把指令封裝成字典發送給伺服器 self.client.send(json.dumps(msg_dic).encode("utf-8")) #把指令以json形式傳給伺服器 server_response = int(self.client.recv(1024).decode("utf-8")) #接收服務端發揮的指令,判斷是否支持此方法 self.client.send("收到指令!".encode("utf-8")) if server_response: file_exists = int(self.client.recv(1024).decode("utf-8")) #下載文件存在與否標識符 self.client.send("收到標識符".encode("utf-8")) #告知伺服器收到標識符,防止粘包 if file_exists: '''文件存在,則準備接收數據,會接收伺服器發送過來的文件大小''' file_mess = json.loads(self.client.recv(3072).decode("utf-8")) self.client.send("文件信息接收成功,開始進行文件接收!".encode("utf-8")) filesize = file_mess["filesize"] while True: '''確定文件名''' if os.path.isfile(filename): '''說明客戶端存在文件,就要讓用戶選擇是否覆蓋''' choice_cover = input("文件名重覆,是否覆蓋現有文件(y/n)>>:") if choice_cover == "y": filename = filename break #覆蓋文件之後退出迴圈 elif choice_cover == "n": filename = input("請輸入新的文件名>>:") '''由於用戶輸入了新的用戶名,我們不知道此文件是否重名,因此重新檢測一下,確保不重名''' continue else: filename = filename break #得到文件名之後退出迴圈 recvive_size = 0 with open(filename,'wb') as file: while recvive_size < filesize: data = self.client.recv(1024) file.write(data) recvive_size += len(data) print("數據下載完成!!!") self.client.send("接收完畢".encode("utf-8")) else: print("要下載的文件不存在!!!") else: print("伺服器不支持此功能!!!") else: print("對不起,輸入有誤!") self.help() if __name__ == "__main__": try: client = Myclient() client.connect("localhost",9998) client.interactive() except KeyboardInterrupt as e: print("客戶端斷開!!!",e)
上面客戶端只實現了上傳和下載功能,沒有實現其他系統操作有關的功能,後續將逐步完善;
伺服器端:
'''定義伺服器端''' import socketserver,json,os class MyTcpServer(socketserver.BaseRequestHandler): '''定義伺服器端,socketserver,繼承的是BaseRequestHandler類''' def handle(self): '''伺服器端所有的功能都在handle()裡面進行處理,因此接收數據也是在handle()中,只是處理的時候,調用下麵模塊而已''' while True: data = self.request.recv(3072) if len(data) == 0: print("客戶端斷開") break msg_dic = json.loads(data.decode("utf-8")) #伺服器端接收指令 cmd = msg_dic["action"] if hasattr(self,cmd): #判斷伺服器是否有相應的指令 self.request.send("1".encode("utf-8")) self.request.recv(1024) func = getattr(self,cmd) func(msg_dic) else: self.request.send("0".encode("utf-8")) self.request.recv(1024) def put(self,msg_dic): '''定義上傳的伺服器代碼''' filename = msg_dic['filename'] filesize = msg_dic["filesize"] '''判斷伺服器端是否存在對應的文件名''' recvive_size = 0 if os.path.isfile(filename): '''如果伺服器存在相應的文件名,則詢問客戶端是否覆蓋''' self.request.send("1".encode("utf-8")) #文件存在,迴圈客戶端是否覆蓋 response = self.request.recv(1024).decode("utf-8") response = response.split() '''如果用戶讓覆蓋則覆蓋,否則重新創建''' if response[0] == "y": '''覆蓋''' with open(filename,"wb") as f1: while recvive_size < filesize: data = self.request.recv(1024) f1.write(data) recvive_size += len(data) self.request.send("文件接收成功!".encode("utf-8")) else: '''不覆蓋,新創建''' new_filename = response[1] with open(new_filename,'wb') as f: while recvive_size < filesize: data1 = self.request.recv(1024) f.write(data1) recvive_size += len(data1) self.request.send("文件接收成功!".encode("utf-8")) else: '''如果伺服器不存在相應文件名,則創建''' self.request.send("0".encode("utf-8")) self.request.recv(1024) with open(filename,'wb') as f2: while recvive_size < filesize: data1 = self.request.recv(1024) f2.write(data1) recvive_size += len(data1) self.request.send("文件接收成功!".encode("utf-8")) def get(self,msg_dict): '''基礎交流結束,伺服器支持文件下載功能,開始文件下載操作''' filename = msg_dict["filename"] if os.path.isfile(filename): self.request.send("1".encode("utf-8")) #文件存在告知客戶端 self.request.recv(1024) filesize = os.stat(filename).st_size '''首先告知客戶端文件大小,讓這邊做好接收準備''' file_mess = { "filesize":filesize, } self.request.send(json.dumps(file_mess).encode("utf-8")) #以json格式發送給客戶端 self.request.recv(1024) #防止粘包,客戶端回應伺服器接收完畢 with open(filename,"rb") as f_obj: for line in f_obj: self.request.send(line) self.request.recv(1024) else: self.request.send("0".encode("utf-8")) #文件不存在,告知客戶端 self.request.recv(1024) if __name__ == "__main__": try: IP,PORT = "localhost",9998 server = socketserver.ThreadingTCPServer((IP,PORT),MyTcpServer) #定義socketserver實例 server.serve_forever() except KeyboardInterrupt as e: print("關閉伺服器")
上面代碼是伺服器端,客戶端與伺服器端其實是相呼應的,兩者實現數據的交換;其實就是收發數據,中間摻雜我們想要實現的功能。
下麵來看幾個容易犯錯的地方:
1、BrokenPipeError: [Errno 32] Broken pipe
客戶端代碼:
import socket,os,json,sys class Myclient(object): '''定義客戶端''' def __init__(self): '''定義socket()實例''' self.client = socket.socket() #生成soket()實例 def connect(self,ip,port): """定義連接""" self.client.connect((ip,port)) def interactive(self): '''定義和用戶交互''' while True: cmd = input("請輸入指令>>:").strip() #用戶輸入指令 if len(cmd) == 0: print("指令不能為空!!!") continue #指令不能為空 cmd_str = cmd.split()[0] #指令由兩種形式 if hasattr(self,cmd_str): #判斷指令是否存在 func = getattr(self,cmd_str) #指令存在,則獲取對應的方法 func(cmd) elif cmd_str == 'q': break else: self.help() continue def help(self): msg = """ ls pwd cd put filename get filename """ print("指令名稱",msg) def put(self,cmd): '''定義上傳數據介面''' cmd_list = cmd.split() if len(cmd_list) > 1: '''指令是由指令名和文件名構成''' filename = cmd_list[1] #獲取文件名 if os.path.isfile(filename): #檢測上傳的文件名是否存在 '''文件名存在,則要告訴伺服器端上傳的文件名,文件大小,並檢測伺服器是否有相應的方法''' filesize = os.stat(filename).st_size msg_dic = { "action":"put", "filename":filename, "filesize":filesize } '''轉換為位元組碼的形式進行傳輸''' self.client.send(json.dumps(msg_dic).encode("utf-8")) #轉換為json格式進行上傳,便於伺服器接收後處理 server_response = self.client.recv(1024) #等待伺服器傳回信息,防止粘包 self.client.send("收到了".encode("utf-8")) if int(server_response.decode("utf-8")): '''根據客戶端反饋,伺服器端是否存在相同的文件名''' file_exist = int(self.client.recv(1024).decode("utf-8")) if file_exist: '''伺服器端存在相應的文件''' while True: cover_flag = input("文件已存在,是否覆蓋原文件(y/n):") if cover_flag == "y": self.client.send("y".encode("utf-8")) break elif cover_flag == "n": new_filename = input("請輸入新的文件名>>:") self.client.send(("n "+new_filename).encode("utf-8")) break else: print("您輸入的指令有誤,請重新輸入!!!") continue else: self.client.send("新創建".encode("utf-8")) '''上面準備工作完畢,開始傳輸文件''' with open(filename,'rb') as f: for line in f: self.client.send(line) #發送數據 '''客戶端等待伺服器端傳來消息,告知上傳成功''' response = self.client.recv(1024) print(response.decode("utf-8")) #數據是否上傳成功,告知用戶 else: print("伺服器端不存在相應的指令!!!") else: print("要上傳的文件名不存在!") else: print("沒有輸入要上傳的文件名!!!") def get(self,cmd): '''從伺服器端下載數據''' cmd_list = cmd.split() #獲取用戶輸入指令 if len(cmd_list) > 1: #是由指令和文件名構成 filename = cmd_list[1] msg_dic = { "filename":filename, "action":"get" } #把指令封裝成字典發送給伺服器 self.client.send(json.dumps(msg_dic).encode("utf-8")) #把指令以json形式傳給伺服器 server_response = int(self.client.recv(1024).decode("utf-8")) #接收服務端發揮的指令,判斷是否支持此方法 self.client.send("收到指令!".encode("utf-8")) if server_response: file_exists = int(self.client.recv(1024).decode("utf-8")) #下載文件存在與否標識符 self.client.send("收到標識符".encode("utf-8")) #告知伺服器收到標識符,防止粘包 if file_exists: '''文件存在,則準備接收數據,會接收伺服器發送過來的文件大小''' file_mess = json.loads(self.client.recv(3072).decode("utf-8")) self.client.send("文件信息接收成功,開始進行文件接收!".encode("utf-8")) filesize = file_mess["filesize"] while True: '''確定文件名''' if os.path.isfile(filename): '''說明客戶端存在文件,就要讓用戶選擇是否覆蓋''' choice_cover = input("文件名重覆,是否覆蓋現有文件(y/n)>>:") if choice_cover == "y": filename = filename break #覆蓋文件之後退出迴圈 elif choice_cover == "n": filename = input("請輸入新的文件名>>:") '''由於用戶輸入了新的用戶名,我們不知道此文件是否重名,因此重新檢測一下,確保不重名''' continue else: filename = filename break #得到文件名之後退出迴圈 recvive_size = 0 with open(filename,'wb') as file: while recvive_size < filesize: data = self.client.recv(1024) file.write(data) recvive_size += len(data) print("數據下載完成!!!") self.client.send("接收完畢".encode("utf-8")) else: print("要下載的文件不存在!!!") else: print("伺服器不支持此功能!!!") else: print("對不起,輸入有誤!") self.help() if __name__ == "__main__": try: client = Myclient() client.connect("localhost",9998) client.interactive() except KeyboardInterrupt as e: print("客戶端斷開!!!",e)
伺服器端:
'''定義伺服器端''' import socketserver,json,os class MyTcpServer(socketserver.BaseRequestHandler): '''定義伺服器端,socketserver,繼承的是BaseRequestHandler類''' def handle(self): '''伺服器端所有的功能都在handle()裡面進行處理,因此接收數據也是在handle()中,只是處理的時候,調用下麵模塊而已''' # while True: data = self.request.recv(3072) if len(data) == 0: print("客戶端斷開") # break msg_dic = json.loads(data.decode("utf-8")) #伺服器端接收指令 cmd = msg_dic["action"] if hasattr(self,cmd): #判斷伺服器是否有相應的指令 self.request.send("1