多进程并发模型--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()