12月9日更新:完美实现&修复BUG《不同网络架构下的文件下载服务协议设计》

杭电胡老师的实验四:

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

我用了6种方式实现,随便选就行了:
1、单线程, 迭代服务器
2、多线程, 并发服务器
3、多进程, 并发服务器
4、gevent库, 协程服务器
5、非阻塞模式服务器
6、asyncio库, 协程服务器

服务器

"""
@author: Bre Athy
@contact: https://www.zhihu.com/people/you-yi-shi-de-hu-xi
@productware: PyCharm
@file: 文件服务器_re3.0.py
@time: 2019/12/6 17:25
"""
import socket, gevent, threading, multiprocessing, asyncio,sys
from gevent import monkey

def get_file_content(file_name):
    try:
        with open(file_name, 'rb')as f:
            print("   获取成功!")
            content = f.read()
            if len(content) > 10 ** 10:print("文件过大!")
            else:return "{:010d}".format(len(content)).encode()+content
    except:
        print("    文件不存在!")

def fuck_one(client_socket, client_addr):
    try:file_name = client_socket.recv(1024).decode("utf-8")
    except:return False
    if not file_name: return True
    print(client_addr, "请求文件", file_name, end="")
    file_content = get_file_content(file_name)
    if file_content: client_socket.send(file_content)
    client_socket.close()

def fuck_two(client_socket, clientAddr):
    threading.Thread(target=fuck_one, args=(client_socket, clientAddr)).start()

def fuck_three(client_socket, clientAddr):
    multiprocessing.Process(target=fuck_one, args=(client_socket, clientAddr)).start()
    client_socket.close()

def fuck_four(client_socket, clientAddr):
    gevent.spawn(fuck_one, client_socket, clientAddr)

def fuck_five(server_socket):
    server_socket.setblocking(False)
    g_socket_list = []
    while True:
        try:new_socket = server_socket.accept()
        except:pass
        else:
            new_socket[0].setblocking(False)
            g_socket_list.append(new_socket)
        for g_socket in g_socket_list:
            try:result = fuck_one(g_socket[0], g_socket[1])
            except:pass
            else:
                if result:g_socket_list.remove(g_socket)

def fuck_six(ADDRESS, PORT):
    async def handle(reader, writer):
        while True:
            try:file_name = (await reader.read(1024)).decode("utf-8")
            except:break
            if not file_name: break
            client_addr = writer.get_extra_info('peername')
            print(client_addr, "请求文件", file_name, end="")
            file_content = get_file_content(file_name)
            if file_content: writer.write(file_content)
            await writer.drain()
            writer.close()
    loop = asyncio.get_event_loop()
    coro = asyncio.start_server(handle, ADDRESS, PORT, loop=loop)
    loop.run_until_complete(coro)
    loop.run_forever()

def main(choice, meta):
    ADDRESS = "127.0.0.1"
    PORT = 6666
    print(f"******************** {meta[int(choice) - 1]}启动 ********************")
    if choice == '5': monkey.patch_all()
    if choice == '6': fuck_six(ADDRESS, PORT)
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server_socket.bind((ADDRESS, PORT))
    server_socket.listen(128)
    fuck_boss = None
    if choice == '1':fuck_boss = fuck_one
    elif choice == '2':fuck_boss = fuck_two
    elif choice == '3':fuck_boss = fuck_three
    elif choice == '4':fuck_five(server_socket)
    elif choice == '5' :fuck_boss = fuck_four
    else:
        print("输入错误!")
        sys.exit()
    while True:
        new_client_socket, new_client_socket_addr = server_socket.accept()
        fuck_boss(new_client_socket, new_client_socket_addr)

if __name__ == "__main__":
    meta = ["单线程, 迭代服务器", "多线程, 并发服务器", "多进程, 并发服务器", "非阻塞模式服务器", "gevent库, 协程服务器", "asyncio库, 协程服务器"]
    choice = input(f"服务器正在启动,请选择启动模式:\n1、{meta[0]}\n2、{meta[1]}\n3、{meta[2]}\n4、{meta[3]}\n5、{meta[4]}\n6、{meta[5]}\n\n【输入序号】:")
    main(choice, meta)

客户端

"""
@author: Bre Athy
@contact: https://www.zhihu.com/people/you-yi-shi-de-hu-xi
@productware: PyCharm
@file: 文件客户端.py
@time: 2019/11/28 13:34
"""
from socket import *
import re,os

def main():
    server_ip = "127.0.0.1"
    server_port = 6666
    root_dir = ""
    print("******************** 客户端启动,最大支持文件大小:1G ********************")
    while True:
        tcp_client_socket = socket(AF_INET, SOCK_STREAM)
        try:tcp_client_socket.connect((server_ip, server_port))
        except:
            print("服务器异常,请检查地址是否正确!")
            break
        file_path = input("请输入文件路径【直接回车退出程序】:")
        if file_path == "": break
        tcp_client_socket.send(file_path.encode())
        recv_length = tcp_client_socket.recv(10)
        if "\\" in file_path: file_path = re.match(r".*\\(.*$)", file_path).group(1)
        if recv_length:
            while os.path.exists(file_path):file_path = "[接收]" + file_path
            file_path = root_dir +file_path
            with open(file_path, "ab")as f:
                length = int(recv_length.decode())
                print(f"请求文件:{file_path}\n文件大小:{length} 字节 ")
                while length>0:
                    recv_data = tcp_client_socket.recv(1024)
                    f.write(recv_data)
                    length -= 1024
            print("接收完成,文件已保存在当前路径:", file_path)
        else:print(f"服务器不存在该文件!请检查!")

    tcp_client_socket.close()
    print("******************** 客户端已退出 ********************")
    
if __name__ == "__main__":
    main()

运行截图:
在这里插入图片描述
在这里插入图片描述
其中单线程模式是不支持并发运行客户端的,所以我又添加了单线程非阻塞模式,运行时先运行服务器,再运行客户端。

12.9 日更新:
修复了客户端被强制断开报错的bug,
修复了没有后缀的文件不能保存的bug,
封装了文件属性,允许接收1G以内的文件,
设置了文件保存路径,支持反复接收相同文件

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值