利用IO多路复用实现的ftp的上传与下载

程序的基本框架图

这个代码我写的时候偷了点懒,没有写关于data的内容  我将需要下载的文件和需要上传的文件都各自放在其client或者server下

其实这个代码很简单,只要理解了IO多路复用,get与put其实两个可逆的过程,注意 发送 与 接受 的位置顺序就可以了

我是新手 希望大家指教

代码 客户端 :

import socket
import os
import hashlib
import time
#client的登陆页面我后续在写
class myclient(object):
    def __init__(self):
        pass
    def login(self):
        self.client=socket.socket()
        #我测试的时候应该往linux上服务器端传输文件
        # 手动输入账户与端口号码
        self.client.connect(("localhost",23))
    def interactive(self):#设计一个用来交互的函数可用来输入命令
        while True:
            command=input("请输入命令").strip()#命令模式参考read me
            if not command:
                continue
            else:
                cmd=command.split()[0]
                if hasattr(self,cmd):
                    func=getattr(self,cmd)
                    func(command)
            #通过这个函数 将下面的dowload与put链接在了一起
    #下载文件
    def get(self,command):
        #把文件名字发过去看是否可以执行(其实就是发过去看服务器端到底有没有这个文件可以下载)
        self.client.sendall(command.encode())
        #根据返回的状态码我们来判断是否该继续进行
        filename=command.split()[1]
        status_code=self.client.recv(1024).decode()
        if status_code=="203":
            print("我们可以进行下载".center(50,"-"))
        #接下来我们要继续判断是否已经下载了一部分,1.继续下载 2.从零开始下载
            if os.path.isfile(filename):#判断文件是否已经存在过
                self.client.sendall("000".encode())#发送一个 000命令告诉服务器 这个文件在客户端已经下载过
                recive_size=os.stat(filename).st_size#已经接受的尺寸
                self.client.sendall(str(recive_size).encode())#将尺寸发送给服务器端
                status_code=self.client.recv(1024).decode()
                if status_code=="426":#文件曾经下载过
                    print("我们需要接着继续传输")
                elif status_code=="312":
                    print("文件已经下载完毕不需要在下载了")
            else:
                self.client.sendall("402".encode())
                print("开始传输")
                recive_size=0
            filesize=self.client.recv(1024).decode()
            print(filesize)#所需要下载的文件的尺寸
            filesize=int(filesize)
            m=hashlib.md5()
            with open(filename,"ab") as f:
                filesize+=recive_size
                print("源文件的总大小为",filesize)
                print("已接受文件大小为",recive_size)
                while recive_size < filesize:
                    a=filesize-recive_size
                    print(a)
                    if a>1024:
                        size=1024
                    else:
                        size=a
                    data=self.client.recv(size)
                    recive_size+=len(data)
                    f.write(data)
                    m.update(data)

                new_hexdigest=m.hexdigest()
                server_hexdigest=self.client.recv(1024).decode()
                if new_hexdigest==server_hexdigest:
                    print("文件一致")


        else:
            print("文件在服务器端不存在!")

#上传文件将client端的文件传送给server端
    def put(self,command):
        self.client.sendall(command.encode())
        filename=command.split()[1]#客户端发来的判断这里是否有这个文件
        if os.path.isfile(filename):
            self.client.sendall("203".encode())#有这个文件可以上传
            file_allsize=os.stat(filename).st_size #获得本地文件的大小
            while True:
                try:
                    status_code=self.client.recv(1024).decode()#获取状态码后继续执行
                    break
                except:
                    continue
            if status_code=="402":
                print("服务器端未下载过,从头开始发送")
                file_hasrecivesize=0
                self.client.sendall(str(file_allsize).encode())
            elif status_code=="000":#此状态码说明客户端已经下载过
                file_hasrecivesize=self.client.recv(1024).decode()
                file_hasrecivesize=int(file_hasrecivesize)
                if file_hasrecivesize<file_allsize:#如果已接受文件尺寸小于总尺寸那么传一个继续下载信号
                    self.client.sendall("426".encode())
                elif file_hasrecivesize==file_allsize:
                    self.client.sendall("312".encode())
                    print("文件已经下载过无需继续进行下载")

                shouldsendsize=file_allsize-file_hasrecivesize
                self.client.sendall(str(shouldsendsize).encode())

            m=hashlib.md5()
            with open(filename,"rb") as file:
                file.seek(file_hasrecivesize)
                for line in file:
                    m.update(line)
                    self.client.sendall(line)
            self.client.sendall(m.hexdigest().encode())
            print(self.client.recv(1024).decode())



        else:
            print("服务器端无此文件可供下载")
            self.client.sendall("110".encode())
#之后我们写的是命令行的操作指示传递



d=myclient()
d.login()
d.interactive()

代码 服务器端:

import hashlib
import socket
import os
import selectors
global command
global conn
import time
sel=selectors.DefaultSelector()
#创建一个 sel实例

def accept(sock,mask):
    sock=sock
    mask=mask
    conn,addr=sock.accept()
    conn.setblocking(False)
    sel.register(conn,selectors.EVENT_READ,read)
def read(conn, mask):
    data = conn.recv(1000)  # 获得命令
    if data:
        print(data)
        command_str = data.decode()
        print(command_str)
        if command_str.split()[0] == 'get':
            get(conn, command_str)
        elif command_str.split()[0]=="put":
            put(conn,command_str)

        else:
            conn.send(b'404') # Hope it won't block
    else:
        print('closing', conn)
        sel.unregister(conn)
        conn.close()
#与客户端的下载进行交互
def get(conn,command):
    filename=command.split()[1]#客户端发来的判断这里是否有这个文件
    if os.path.isfile(filename):
        conn.sendall("203".encode())#有这个文件可以下载
        file_allsize=os.stat(filename).st_size #获得本地文件的大小
        while True:
            try:
                status_code=conn.recv(1024).decode()#获取状态码后继续执行
                break
            except:
                continue
        if status_code=="402":
            print("客户端未下载过,从头开始发送")
            file_hasrecivesize=0
            conn.sendall(str(file_allsize).encode())
        elif status_code=="000":#此状态码说明客户端已经下载过
            while True:
                try:
                    file_hasrecivesize=conn.recv(1024).decode()#获取状态码后继续执行
                    break
                except:
                    continue
            file_hasrecivesize=int(file_hasrecivesize)
            print(file_hasrecivesize)
            if file_hasrecivesize < file_allsize:#如果已接受文件尺寸小于总尺寸那么传一个继续下载信号
                conn.sendall("426".encode())
                print("文件小于总尺寸")
            elif file_hasrecivesize == file_allsize:
                conn.sendall("312".encode())
                print("文件等于总尺寸")
            shouldsendsize=file_allsize-file_hasrecivesize
            conn.sendall(str(shouldsendsize).encode())

        m=hashlib.md5()
        with open(filename,"rb") as file:
            file.seek(file_hasrecivesize)
            for line in file:
                m.update(line)
                conn.sendall(line)
        conn.sendall(m.hexdigest().encode())
        return

    else:
        print("服务器端无此文件可供下载")
        conn.sendall("110".encode())

def put(conn,command):
    filename=command.split()[1]
    while True:
        try:
            status_code=conn.recv(1024).decode()#获取状态码后继续执行
            break
        except:
            continue
    if status_code=="203":
        print("我们可以进行下载".center(50,"-"))
    #接下来我们要继续判断是否已经下载了一部分,1.继续下载 2.从零开始下载
        if os.path.isfile(filename):#判断文件是否已经存在过
            conn.sendall("000".encode())#发送一个 000命令告诉服务器 这个文件在客户端已经下载过
            recive_size=os.stat(filename).st_size#已经接受的尺寸
            conn.sendall(str(recive_size).encode())#将尺寸发送给服务器端
            while True:
                try:
                    status_code=conn.recv(1024).decode()#获取状态码后继续执行
                    break
                except:
                    continue
            if status_code=="426":#文件曾经下载过
                print("我们需要接着继续传输")
            elif status_code=="312":
                print("文件已经下载完毕不需要在下载了")
        else:
            conn.sendall("402".encode())
            print("开始传输")
            recive_size=0
        while True:
            try:
                filesize=conn.recv(1024).decode()#所需要下载的文件的尺寸
                break
            except:
                continue

        filesize=int(filesize)
        print(filesize)
        m=hashlib.md5()
        with open(filename,"ab") as f:
            filesize+=recive_size
            print("源文件的总大小为",filesize)
            print("已接受文件大小为",recive_size)
            while recive_size < filesize:
                a=filesize-recive_size
                print(a)
                if a>1024:
                    size=1024
                else:
                    size=a
                data=conn.recv(size)
                recive_size+=len(data)
                f.write(data)
                m.update(data)
            new_hexdigest=m.hexdigest()
            while True:
                try:
                    server_hexdigest=conn.recv(1024).decode()
                    break
                except:
                    continue

            if new_hexdigest==server_hexdigest:
                print("文件一致")
                conn.send("文件一致".encode())
    else:
        print("文件在服务器端不存在!")



server=socket.socket()#创建一个实例
#监听的链接 我选择从客户端接收
server.bind(("localhost",23))
server.listen(1000)

server=socket.socket()
server.bind(("localhost",23))
server.listen(1000)
server.setblocking(False)#设置为非阻塞模式
sel.register(server,selectors.EVENT_READ,accept)
while True:
    events=sel.select()
    for key,mask in events:
        callback=key.data#相当于callback=accept
        callback(key.fileobj,mask)#调用accept keyfileobj相当于一个实例作为形参传进去

其实这个程序要理解 多路复用的原理,在服务器端 服务器一般是不会等待接受的 如果100个链接过来都没有那么就会立即返回一个信号结束,所以我们要用一个while循环来等recv信息继续进行

才能正确的运行代码!!!!

原文地址:https://www.cnblogs.com/shidi/p/7475476.html