多程序併發模型--TFTP檔案伺服器
阿新 • • 發佈:2021-01-11
1、專案功能
【客戶端】
客戶端有簡單的頁面命令提示 ,功能包含
【1】檢視伺服器檔案庫中的檔案列表(普通檔案)
【2】可以下載其中的某個檔案到本地
【3】可以上傳客戶端檔案到伺服器檔案庫
【服務端】
伺服器需求
【1】允許多個客戶端同時操作
【2】每個客戶端可能回連續傳送命令
2、技術分析
【1】tcp套接字更適合檔案傳輸
【2】併發方案 ---> fork 多程序併發
【3】對檔案的讀寫操作
【4】獲取檔案列表 ----> os.listdir()
【5】粘包的處理
3、整體結構設計
【1】伺服器功能封裝在類中(上傳,下載,檢視列表)
【2】建立套接字,流程函式呼叫 main()
【3】① 客戶端負責發起請求,接受回覆,展示
②服務端負責接受請求,邏輯處理
4、程式設計實現
【1】搭建整體結構,建立網路連線
【2】建立多程序和類的結構
【3】每個功能模組的實現
【TFTP-server.py】
from socket import *
import os
import signal
import sys
import time
#檔案庫
FILE_PATH = "/home/tarena/"
#實現功能模組
class TftpServer(object):
def __init__(self,connfd):
self.connfd = connfd
def do_list(self):
#獲取列表
file_list = os.listdir(FILE_PATH)
if not file_list:
self.connfd.send("檔案庫為空".encode())
return
else:
self.connfd.send(b 'OK')
time.sleep(0.1)
files = ""
for file in file_list:
if os.path.isfile(FILE_PATH+file) and \
file[0] != '.':
files = files + file + '#'
self.connfd.send(files.encode())
def do_get(self,filename):
try:
fd = open(FILE_PATH + filename,'rb')
except:
self.connfd.send("檔案不存在".encode())
return
self.connfd.send(b'OK')
time.sleep(0.1)
#傳送檔案
try:
while True:
data = fd.read(1024)
if not data:
break
self.connfd.send(data)
except Exception as e:
print(e)
time.sleep(0.1)
self.connfd.send(b'##') #表示檔案傳送完成
print("檔案傳送完畢")
def do_put(self,filename):
try:
fd = open(FILE_PATH+filename,'wb')
except:
self.connfd.send("無法上傳".encode())
return
self.connfd.send(b'OK')
while True:
data = self.connfd.recv(1024)
if data == b'##':
break
fd.write(data)
fd.close()
print("檔案上傳完畢")
#流程控制,建立套接字,建立併發,方法呼叫
def main():
HOST = '0.0.0.0'
PORT = 8888
ADDR = (HOST,PORT)
sockfd = socket()
sockfd.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
sockfd.bind(ADDR)
sockfd.listen(5)
signal.signal(signal.SIGCHLD,signal.SIG_IGN)
while True:
try:
connfd,addr = sockfd.accept()
except KeyboardInterrupt:
sockfd.close()
sys.exit("伺服器退出")
except Exception as e:
print(e)
continue
print("客戶端登入:",addr)
#建立父子程序
pid = os.fork()
if pid == 0:
sockfd.close()
tftp = TftpServer(connfd) # __init__傳參
while True:
data = connfd.recv(1024).decode()
if (not data) or data[0] == 'Q':
print("客戶端退出")
sys.exit(0)
elif data[0] == "L":
tftp.do_list()
elif data[0] == 'G':
filename = data.split(' ')[-1]
tftp.do_get(filename)
elif data[0] == 'P':
filename = data.split(' ')[-1]
tftp.do_put(filename)
else:
print("客戶端傳送錯誤指令")
else:
connfd.close()
continue
if __name__ == "__main__":
main()
【TFTP-client.py】
from socket import *
import sys
import time
#實現各種功能請求
class TftpClient(object):
def __init__(self,sockfd):
self.sockfd = sockfd
def do_list(self):
self.sockfd.send(b'L') #傳送請求型別
#接收伺服器迴應
data = self.sockfd.recv(1024).decode()
if data == "OK":
data = self.sockfd.recv(4096).decode()
files = data.split('#')
for file in files:
print(file)
print("檔案展示完畢")
else:
#請求失敗原因
print(data)
def do_get(self,filename):
self.sockfd.send(('G ' + filename).encode())
data = self.sockfd.recv(1024).decode()
if data == 'OK':
fd = open(filename,'wb')
while True:
data = self.sockfd.recv(1024)
if data == b'##':
break
fd.write(data)
fd.close()
print("%s 下載完成\n"%filename)
else:
print(data)
def do_put(self,filename):
try:
fd = open(filename,'rb')
except:
print("上傳檔案不存在")
return
self.sockfd.send(("P "+filename).encode())
data = self.sockfd.recv(1024).decode()
if data == 'OK':
while True:
data = fd.read(1024)
if not data:
break
self.sockfd.send(data)
fd.close()
time.sleep(0.1)
self.sockfd.send(b'##')
print("%s 上傳完畢"%filename)
else:
print(data)
#建立套接字建立連線
def main():
if len(sys.argv) < 3:
print("argv is error")
return
HOST = sys.argv[1]
PORT = int(sys.argv[2])
ADDR = (HOST,PORT)
sockfd = socket()
sockfd.connect(ADDR)
tftp = TftpClient(sockfd) #__init__是否需要傳參
while True:
print("")
print("==========命令選項===========")
print("********** list *********")
print("********** get file ******")
print("********** put file ******")
print("********** quit *********")
print("=============================")
cmd = input("輸入命令>>")
if cmd.strip() == "list":
tftp.do_list()
elif cmd[:3] == "get":
filename = cmd.split(' ')[-1]
tftp.do_get(filename)
elif cmd[:3] == "put":
filename = cmd.split(' ')[-1]
tftp.do_put(filename)
elif cmd.strip() == "quit":
sockfd.send(b'Q')
sockfd.close()
sys.exit("歡迎使用")
else:
print("請輸入正確命令!")
if __name__ == "__main__":
main()