不同网络架构下的文件下载服务协议设计

使用TCP协议设计一个文件下载服务协议,客户端发送要下载的文件路径给服务器,服务器将对应的文件内容送给客户端,客户端将文件存储到本地磁盘。注意,当文件不存在时给出提示。要求服务的实现分别采用以下三种方法实现:
(1)单线程,迭代服务器(依次服务每一个客户端)
(2)多线程,并发服务器
(3)异步方式(select模型或poll模型)
(4)asyncio库
 

实现了单线程、多线程、select模型异步

Base

from socket import *
from threading import Thread
import os
import struct
import time
import select


class BaseMessageHead:
    "所有消息头的基类"
    formatStr = ""
    headLength = 0


class MessageHeadServiceProvider(BaseMessageHead):
    "默认消息头的实现"
    formatStr = "HHL"  # 定义各字段打包格式
    headLength = struct.calcsize(formatStr)

    def __init__(self):

        self.tupleField = (0, 0, 0)  # 按顺序存储以下字段值

        # 消息头字段
        self.type = 0  # 消息类型 0代表通知,1文件,2代表请求文件
        self.mode = 0  # 保留字段
        self.msgLength = 0  # 消息大小 小于(2^32-1)B,约4GB

    def UpdateField(self) -> None:
        "利用tupleField更新消息头字段"
        self.type = self.tupleField[0]
        self.mode = self.tupleField[1]
        self.msgLength = self.tupleField[2]

    def ExportBytes(self) -> bytes:
        "将消息头导出字节数组"
        result = struct.pack(MessageHeadServiceProvider.formatStr,self.tupleField[0], self.tupleField[1], self.tupleField[2])
        return result

    def ImportBytes(self, head: bytes) -> None:
        "将字节数组导入消息头"
        self.tupleField = struct.unpack(MessageHeadServiceProvider.formatStr, head)
        self.UpdateField()

    def SetField(self, tupleField: tuple) -> None:
        "设置消息头字段"
        self.tupleField = tupleField
        self.UpdateField()

    def GetField(self) -> tuple:
        "获取消息头字段"
        return self.tupleField

    def Parse(self, dataSocket,fileName=None) -> None:
        "利用dataSocket解析者,解析消息头"
        # type=0 消息体为字符串
        if self.type == 0:

            noticeBytes = dataSocket.recv(self.msgLength)

            notice = noticeBytes.decode()
            print(f"Received notice: {notice}")
        # type=1 消息体为文件
        elif self.type == 1:

            recvedSize=0
            
            f = open(time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime())+" "+fileName, "wb")
            while recvedSize < self.msgLength:
            
                if self.msgLength - recvedSize > FileData.oneReadSize:
                    currentData = dataSocket.recv(FileData.oneReadSize)
                    f.write(currentData)
                    recvedSize += len(currentData)
    
                else:
                    currentData = dataSocket.recv(self.msgLength - recvedSize)
                    recvedSize += len(currentData)
                    f.write(currentData)
            f.close()
        # type=2文件请求消息
        elif self.type == 2:
            filePathBytes = dataSocket.recv(self.msgLength)

            filePath = filePathBytes.decode()
            fd = FileData()

            # 文件不存在 处理
            if not fd.InitFileData(filePath):
                # 服务端处理
                print(f"{filePath} doesn't exist")
                # 客户端处理
                sd = StringData()
                sd.InitStringData(f"{filePath}File doesn't exist")
                self.SetField((0, 0, sd.strSize))
                dataSocket.send(self.ExportBytes())
                dataSocket.send(sd.strContent.encode())
                # 流程处理
                return

            # 发送头部与文件
            self.SetField((1, 0, fd.fileSize))
            dataSocket.send(self.ExportBytes())
            with open(fd.filePath, "rb") as temp:
                while True:
                    data = temp.read(FileData.oneReadSize)
                    if not data:
                        break
                    dataSocket.send(data)
        else:
            # 接收到非法信息
            print(f"{dataSocket.getpeername()[0]}:{dataSocket.getpeername()[1]} is refused")
            print(f"{dataSocket.getpeername()[0]}:{dataSocket.getpeername()[1]} disconnected")
            dataSocket.close()
            return


class FileData:
    "保存已读入文件元数据"
    # 在InitFileData成功后,存入FileData对象
    fileList = []
    oneReadSize = 2048

    def __init__(self):
        self.fileSize = 0
        self.filePath = ""

    def InitFileData(self, filePath):
        "返回布尔值"
        if not os.path.exists(filePath):
            return False
        self.fileSize = os.path.getsize(filePath)
        self.filePath = filePath

        FileData.fileList.append(self)
        return True


class StringData:
    "保存已读入字符串元数据"
    # 在InitStringData成功后,存入StringData对象
    strList = []

    def __init__(self):
        self.strSize = 0
        self.strContent = ""

    def InitStringData(self, str):
        if not str:
            return False
        self.strSize = len(str.encode("UTF-8"))
        self.strContent = str

        StringData.strList.append(self)
        return True


class BaseMode:
    "所有服务器实现的基类"

    def __init__(self, ip, port) -> None:
        self.IP = ip
        self.Port = port


class SingleThreadMode(BaseMode):
    "单线程"
    MAXCONNECTED = 8  # 最大连接数

    def __init__(self, ip, port):

        BaseMode.__init__(self, ip, port)

        self.waitList = []  # dataSocket等待队列
        self.currentDataSocket = None  # 当前服务的客户端

    def Start(self):
        # 主线程,监听连接
        listenSocket = socket(AF_INET, SOCK_STREAM)
        listenSocket.bind((self.IP, self.Port))
        listenSocket.listen(SingleThreadMode.MAXCONNECTED)
        print(f"Server started,port {self.Port} is open...")

        # 创建子线程
        th = Thread(target=self.DefaultClientHandler)
        th.start()

        while True:
            dataSocket, addr = listenSocket.accept()
            print(f"{addr[0]}:{addr[1]} connected")
            self.waitList.append(dataSocket)

    def DefaultClientHandler(self):
        "此线程用于处理waitList中的socket对象,只被创建一次"
        while True:
            # 等待列表为空时,每次暂停运行一秒,减少资源开销
            if self.waitList == []:
                time.sleep(1)
                continue
            self.currentDataSocket = self.waitList.pop(0)

            while True:

                # 读入消息头
                # 客户端断开链接处理
                try:
                    head = self.currentDataSocket.recv(
                        MessageHeadServiceProvider.headLength)
                    if not head:
                        print(
                            f"{self.currentDataSocket.getpeername()[0]}:{self.currentDataSocket.getpeername()[1]} disconnected")
                        self.currentDataSocket = None
                        break
                except:
                    print(
                        f"{self.currentDataSocket.getpeername()[0]}:{self.currentDataSocket.getpeername()[1]} disconnected")
                    self.currentDataSocket = None
                    break

                msgHead = MessageHeadServiceProvider()
                msgHead.ImportBytes(head)
                msgHead.Parse(self.currentDataSocket)


class MultipleThreadMode(BaseMode):
    "多线程"
    MAXCONNECTED = 8  # 最大连接数

    def __init__(self, ip, port):
        BaseMode.__init__(self, ip, port)

    def Start(self):
        # 主线程,监听连接
        listenSocket = socket(AF_INET, SOCK_STREAM)
        listenSocket.bind((self.IP, self.Port))
        listenSocket.listen(MultipleThreadMode.MAXCONNECTED)
        print(f"Server started,port {self.Port} is open...")

        while True:
            dataSocket, addr = listenSocket.accept()
            print(f"{addr[0]}:{addr[1]} connected")
            # 创建子线程
            th = Thread(target=self.DefaultClientHandler,
                        args=(dataSocket, addr))
            th.start()

    def DefaultClientHandler(self, dataSocket, addr):
        "此线程用于单独地处理客户端对象,每连进一个客户端就创建一次"
        while True:
            # 读入消息头
            # 客户端断开链接处理
            try:
                head = dataSocket.recv(MessageHeadServiceProvider.headLength)
                if not head:
                    print(
                        f"{dataSocket.getpeername()[0]}:{dataSocket.getpeername()[1]} disconnected")
                    dataSocket = None
                    break
            except:
                print(
                    f"{dataSocket.getpeername()[0]}:{dataSocket.getpeername()[1]} disconnected")
                dataSocket = None
                break

            msgHead = MessageHeadServiceProvider()
            msgHead.ImportBytes(head)
            msgHead.Parse(dataSocket)


class AsynchronousMode(BaseMode):
    "异步"
    MAXCONNECTED = 8  # 最大连接数

    def __init__(self, ip, port):

        BaseMode.__init__(self, ip, port)
        self.rList = []

    def Start(self):
        # 主线程,利用select监听 读等待队列
        listenSocket = socket(AF_INET, SOCK_STREAM)
        listenSocket.bind((self.IP, self.Port))
        listenSocket.listen(AsynchronousMode.MAXCONNECTED)
        print(f"Server started,port {self.Port} is open...")
        # 第一个是listenSocket,后面每一个都是dataSocket
        self.rList = [listenSocket, ]

        while True:
            rl, wl, el = select.select(self.rList, [], [], 10)

            for eachSocket in rl:
                # listenSocket处理
                if eachSocket == listenSocket:
                    dataSocket, addr = eachSocket.accept()
                    print(f"{addr[0]}:{addr[1]} connected")
                    # 将新建立的socket添加到rList列表
                    self.rList.append(dataSocket)

                # dataSocket处理
                else:
                    try:
                        head = eachSocket.recv(MessageHeadServiceProvider.headLength)
                        if not head:
                            print(
                                f"{eachSocket.getpeername()[0]}:{eachSocket.getpeername()[1]} disconnected")
                            self.rList.remove(eachSocket)
                            continue

                    except:
                        print(
                            f"{eachSocket.getpeername()[0]}:{eachSocket.getpeername()[1]} disconnected")
                        self.rList.remove(eachSocket)
                        continue
                    msgHead = MessageHeadServiceProvider()
                    msgHead.ImportBytes(head)
                    msgHead.Parse(eachSocket)


class ServerInstallation:
    "通用服务器,初始化时需传入启动模式"

    def __init__(self, mode) -> None:
        self.mode = mode

    def Start(self):
        self.mode.Start()

Server

from socket import *
from threading import Thread
from Base import *

IP = ''
PORT = 60666


mode1=SingleThreadMode(IP,PORT)
mode2=MultipleThreadMode(IP,PORT)
mode3=AsynchronousMode(IP,PORT)

modeList=[]
modeList.append(mode1)
modeList.append(mode2)
modeList.append(mode3)

print("======================================")
print("                                      ")
print("            choose mode               ")
print("                                      ")
print("======================================")

while True:
    print("SingleThread(0)")
    print("MultipleThread(1)")
    print("Asynchronous(2)")

    tempMode=input()
    if tempMode=="exit":
        exit(0)
    try:
        
        mode=modeList[int(tempMode)]

        break
    except:
        print("Not a legal value")

server=ServerInstallation(mode)
server.Start()

Client

from socket import *
from Base import *

# socket初始化
IP = "127.0.0.1"
PORT = 60666
dataSocket = socket(AF_INET, SOCK_STREAM)
dataSocket.connect((IP, PORT))

# 生成对象
msgHead = MessageHeadServiceProvider()
str = StringData()

while True:
    optionNum=-1

    print(f"Notice(input n)\nGetFile(input f)")
    
    # 输入选项
    while True:
        option = input(">>> ")
        if option == "n":
            optionNum=0

            print("Please input notice")
            break

        elif option == "f":
            optionNum=2
            print("Please input file path")
            break
        elif option=="exit":
            dataSocket.close()
            exit()

        else:
            print("Not a legal value")

    #输入通知或文件路径
    while True:
        temp = input(">>> ")
        if str.InitStringData(temp):
            break
        else:
            print("Not a legal value")

    #构建消息头
    msgHead.SetField((optionNum, 0, str.strSize))
    #发送消息头
    dataSocket.send(msgHead.ExportBytes())
    #发送消息体
    dataSocket.send(str.strContent.encode())
    

    if optionNum==2:
        fileName=str.strContent.split("\\")[-1]
        head=dataSocket.recv(MessageHeadServiceProvider.headLength)
        msgHead=MessageHeadServiceProvider()
        msgHead.ImportBytes(head)
        msgHead.Parse(dataSocket,fileName)

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值