Python_模拟FTP文件服务器

分为服务端和客户端,要求可以有多个客户端同时操作。
客户端可以查看服务器文件库中有什么文件。
客户端可以从文件库中下载文件到本地。
客户端可以上传一个本地文件到文件库。
使用print在客户端打印命令输入提示,引导操作

服务端

# 导入相关模块
from socket import *
from multiprocessing import Process
import signal, os, time

# 绑定IP地址
IP = "127.0.0.1"
# 绑定端口
PORT = 8888
ADDR = (IP, PORT)
# 绑定服务器的指定目录
DIR = "/home/max/ftp"


# 处理查看文件请求
def browse(c):
    # 列表方式查看文件
    list = os.listdir("%s" % DIR)
    # 按通信协议格式组织数据
    msg = "B  " + ";".join(list)
    # 发送到客户端
    c.send(msg.encode())


# 处理客户端下载文件请求
def download(c, file):
    # 判断文件是否存在且是否是文件
    if file in os.listdir(DIR) and os.path.isfile("%s/%s" % (DIR, file)):
        # 打开文件
        f = open("%s/%s" % (DIR, file), "rb")
        # 发送下载代码,告知客户端进入"D"
        c.send(("D %s " % file).encode())
        # 等待客户端先进入"D"模式下的recv阻塞函数
        time.sleep(0.1)
        # 循环发送文件
        while True:
            data = f.read(1000)
            if not data:
                # 设置间隔,等待文件传输完整
                time.sleep(0.1)
                # 约定的信息让客户端退出接收循环
                c.send(b"finished")
                break
            c.send(data)
        f.close()
    # 否则按协议格式发送错误代码
    else:
        c.send(b"D fileerror ")


# 处理客户端上传文件请求
def upload(c, file):
    # 创建文件
    f = open("%s/%s" % (DIR, file), "wb")
    # 循环接收文件
    while True:
        data = c.recv(1024)
        # 收到约定的信息退出循环
        if data == b"finished":
            break
        f.write(data)
    f.close()


# 处理客户端退出请求
def login_out(c):
    # 按协议格式组织信息
    msg = "O closed "
    # 发给客户端的recv_msg进程
    c.send(msg.encode())


def deal(c):
    # 发送代码告知客户端连接成功
    c.send(b"K  ")
    while True:
        # 循环接收客户端请求,约定通信协议为"* ** ****"格式
        msg = c.recv(1024)
        # 如果客户端崩了,解除该子进程
        if not msg:
            break
        # 解析请求
        req = msg.decode().split(" ", 2)
        # 处理查看文件请求,跳入browse函数
        if req[0] == "B":
            browse(c)
        # 处理客户端下载文件请求,跳入download函数
        elif req[0] == "D":
            download(c, req[1])
        # 处理客户端上传文件请求,跳入upload函数
        elif req[0] == "U":
            upload(c, req[1])
        # 处理客户端退出请求,跳入login_out函数
        elif req[0] == "O":
            login_out(c)
            # 跳出循环,结束子进程
            break


# 主程序,父进程用于接收客户端请求并循环创建子进程,子程序处理请求
def main():
	# 创建tcp套接字
	s = socket()
	# 绑定服务器地址
	s.bind(ADDR)
	# 设置监听套接字
	s.listen()
	# 处理僵尸进程
	signal.signal(signal.SIGCHLD, signal.SIG_IGN)
    while True:
        # 连接客户端
        c, addr = s.accept()
        # 创建子进程,用以处理客户端请求,跳入deal函数
        p = Process(target=deal, args=(c,))
        # 子程序开始执行
        p.start()


if __name__ == '__main__':
    main()

客户端

# 导入相关模块
from socket import *
import os, sys, time

# 绑定服务端IP地址
IP = "127.0.0.1"
# 绑定服务端端口
PORT = 8888
ADDR = (IP, PORT)


# 收到约定的信息退出循环
# 发送消息进程
def send_msg(s):
    # 等待接收进程先运行到"K"分支
    time.sleep(0.1)
    while True:
        try:
            # 输入指令
            data = input(">>>")
        except:
            # 客户端错误,向服务端发送O
            data = "O"
        if data == "B":  # 查看目录
            msg = "B  "
            s.send(msg.encode())
        elif data == "D":  # 下载文件
            # 输入想要下载的文件
            want = input("download ? file:")
            msg = "D %s " % want
            s.send(msg.encode())
        elif data == "U":  # 上传文件
            file = input("upload ? file:")
            # 判断文件是否在客户端文件所在的目录且是文件
            if file in os.listdir(os.getcwd()) and os.path.isfile("%s/%s" % (os.getcwd(), file)):
                msg = "U %s " % file
                s.send(msg.encode())
                f = open("%s" % file, "rb")
                # 等待服务端进入upload的recv阻塞函数
                time.sleep(0.1)
                while True:
                    data = f.read(1000)
                    if not data:
                        # 设置间隔,等待文件传输完整
                        time.sleep(0.1)
                        # 约定的信息让客户端退出接收循环
                        s.send(b"finished")
                        break
                    s.send(data)
                f.close()
                print("upload succussfully")
            # 否则按协议格式显示错误代码
            else:
                print("file not exist\ninput BDUO to forward\n", end="")
        elif data == "O":  # 断开连接
            msg = "O  "
            s.send(msg.encode())
            sys.exit()
        # 指令错误
        else:
            print("input error")


# 接收消息进程
def recv_msg(s):
    while True:
        data = s.recv(1024)
        # 解析数据
        msg = data.decode().split(" ", 2)
        if msg[0] == "K":  # 登录成功反馈
            print(
                "login in successfully\ninput B to browse,D to download,U to upload,O to login out")
        elif msg[0] == "B":  # 查看目录反馈
            # 如果文件库不为空
            if msg[2]:
                print("files:", msg[2], "\n>>>", end="")
            else:
                print("files: no files", "\n>>>", end="")
        elif msg[0] == "D":
            # 服务端文件不存在
            if msg[1] == "fileerror":
                print("file not exist\ninput BDUO to forward\n>>>", end="")
                continue
            # 服务端文件存在
            else:
                f = open("%s" % msg[1], "wb")
                while True:
                    data = s.recv(1024)
                    # 收到约定的信息退出循环
                    if data == b"finished":
                        break
                    f.write(data)
                f.close()
                print("download successfully\n>>>", end="")
        elif msg[0] == "O":  # 收到来自发送消息进程发送到服务端的断开请求
            # 进程退出并打印提示
            sys.exit("login out successfully")


# 主程序
def main():
    # 创建tcp套接字
    s = socket()
    # 连接服务端
    s.connect(ADDR)
    # 创建多进程,子进程用于发送消息,父进程用于接收消息
    pid = os.fork()
    if pid < 0:
        print("system error")
    # 子进程
    elif pid == 0:
        send_msg(s)
    # 父进程
    else:
        recv_msg(s)


if __name__ == '__main__':
    main()

你可能感兴趣的:(tiny_project)