1. 程式人生 > >Python網路程式設計--通過fork、tcp併發完成ftp檔案伺服器

Python網路程式設計--通過fork、tcp併發完成ftp檔案伺服器

ftp檔案伺服器
 一、專案功能
     1.服務端和客戶端兩部分,要求啟動一個服務端可以同時處理多個客戶端請求
     2.功能:1).可以檢視服務端檔案庫中所有的普通檔案
                   2).從客戶端可以下載檔案庫的檔案到本地
                   3).可以將本地檔案上傳到服務端檔案庫
                  4).退出
     3. 客戶端使用print在終端列印簡單的命令提示,通過命令提示發起請求

二、技術分析

    1.技術分析-(fork,tcp併發)
    2.每個功能要單獨封裝,整體功能寫在一個類中
    3.如何搭建整體架構,完成網路通訊

三、功能分析

獲取檔案列表
    客戶端:  *傳送請求
                     *得到回覆判斷能否獲取列表
                     *接收檔名稱列表列印
    服務端: * 接收請求
                     * 判斷請求型別
                    * 判斷能否滿足請求,回覆資訊確認
                    * 執行請求傳送檔案列表

-------------------------------------------------------------------------------------華麗分割線  ~_~

  • 服務端程式
'''
ftp 檔案伺服器
'''
from socket import *
import time
import os,sys
import signal

#檔案庫路徑
file_path = '/home/file/ftp/'
HOST = ''
PORT = 5000
ADDR = (HOST,PORT)

#將檔案伺服器功能寫在類中
class FtpServer(object):
    def __init__(self,c):
        self.c = c
    def do_list(self):
        #獲取檔案列表
        file_list = os.listdir(file_path)
        if not file_list:
            self.c.send('檔案庫為空'.encode())
            return
        else:
            self.c.send(b'OK')
            time.sleep(0.1)
        files = ''
        for file in file_list:
            if file[0] != '.' and os.path.isfile(file_path+file):
                files = files + file + '#'
        self.c.sendall(files.encode())

    def do_get(self,filename):
        try:
            fd = open(file_path + filename,'rb')
        except:
            self.c.send('檔案不存在'.encode())
            return
        self.c.send(b'OK')
        time.sleep(0.1)
        #傳送檔案
        while True:
            data = fd.read(1024)
            if not data:
                time.sleep(0.1)
                self.c.send(b'##')
                break
            self.c.send(data)
        print('檔案傳送完畢')
        fd.close()

    def do_upload(self,filename):
        try: 
            fd = open(file_path+filename,'wb')
        except:
            self.c.send('檔案上傳失敗'.encode())
            return
        self.c.send(b'OK')    
        while True:
            data = self.c.recv(1024)
            if data == b'##':
                break
            fd.write(data)
        fd.close()
        print('%s上傳完畢'%filename)

#建立套接字,接收客戶端連線,建立新的流程
def main():
    sockfd = socket()
    sockfd.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
    sockfd.bind(ADDR)
    sockfd.listen(5)

    #處理子程序退出
    signal.signal(signal.SIGCHLD,signal.SIG_IGN)
    print('listen the port 5000...')

    while True:
        try:
            c,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()
            ftp=FtpServer(c)
            #判斷客戶請求
            while True:
                data=c.recv(1024).decode()
                if not data:
                    c.close()
                    sys.exit('客戶端退出')
                elif data[0]=='L':
                    ftp.do_list()
                elif data[0]=='G':
                    filename=data.split(' ')[-1]
                    ftp.do_get(filename)
                elif data[0]=='U':
                    filename=data.split(' ')[-1]
                    ftp.do_upload(filename)
            
        #父程序或者建立失敗,都繼續等待下個客戶端連線
        else:
            c.close()
            continue

if __name__ == '__main__':
    main()
  • 客戶端程式
  • from socket import *
    from menu import show_menu
    import sys
    import time
    
    #基本檔案操作功能寫在類裡
    class FtpClient(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('檔案列表展示完畢\n')
            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_upload(self,filename):       
            try:
                fd=open(filename,'rb')
            except:
                print('檔案開啟失敗')
                return
            self.sockfd.send(('U '+filename).encode())
            data=self.sockfd.recv(1024).decode()
            if data=='OK':
                while True:
                    data=fd.read(1024)
                    if not data:
                        time.sleep(0.1)
                        self.sockfd.send(b'##')
                        break
                    self.sockfd.send(data)
                fd.close()
                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()
        try:
            sockfd.connect(ADDR)
        except:
            print('連線伺服器失敗')
            return 
    
        ftp = FtpClient(sockfd) #功能類物件
        while True:
            show_menu()        
            cmd = input('請輸入命令>>:')
            if not cmd:
                break
            elif cmd.strip()== '1' :
                ftp.do_list()
            elif cmd.strip()[0] =='2':
                filename=cmd.split(' ')[-1]
                ftp.do_get(filename)
            elif cmd.strip()[0]=='3':
                filename=cmd.split(' ')[-1]
                ftp.do_upload(filename)
            elif cmd.strip()[0]=='4':
                sys.exit(0)
            else:
                print('請輸入正確命令!!!')
                continue
    
        # #關閉套接字
        # sockfd.close()
    
    if __name__ == '__main__':
        main()