多进程并发模型--TFTP文件服务器

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()

 

上一篇:Socket原理详解


下一篇:select实现简单TCP通信(ubuntu 18.04)