Python利用tcp_socket实现文件下载器

42 篇文章 1 订阅
14 篇文章 1 订阅

Python利用tcp_socket实现简单文件下载功能

一、客户端

客户端比较好写一点,我们先来实现客户端的功能

客户端实现原理

  • 创建tcp_socket, 通过IP地址与端口连接服务端
  • 通过input函数获取filename并发送到服务端
  • 接收到来自服务端发来的文件内容
  • 判断发来的文件内容是否为空,如果为空,代表服务端发送异常,停止创建文件
  • 如果不为空,创建新文件,写入服务端发来的内容

服务端发送的都是byte类型的数据,所以我们用wb方式写入文件

代码如下:

import os
import socket

def main():
    tcp_download_socket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    tcp_download_socket.connect((b'127.0.0.1',7788))
    while True:
        file_name=input('请输入要下载的文件内容')
        tcp_download_socket.send(file_name.encode('utf-8'))  #发送filename
        recv_data=tcp_download_socket.recv(1024*1024)  #最大限制1MB
        print('开始写入文件---')
        if recv_data:
            with open('new_'+ file_name,'bw') as doenload_file:
                doenload_file.write(recv_data)
                doenload_file.close()
                if os.path.isfile('new_'+ file_name):
                    print('下载文件成功')
                answer = input("是否下载其他文件")
                if answer == 'y' or answer == 'Y':
                    continue
                else:
                    break
        else:
            print("服务端不存在该文件,请检查后重试")
            continue

if __name__ == '__main__':
    main()

二、服务端

客户端没问题了,我们实现一下服务端

服务端实现原理:

  • 创建服务端socket,并绑定7788端口
  • 开始持续监听7788端口,此时阻塞
  • 客户端发送连接请求,连接客户端成功,服务端解阻塞
  • 接收到客户端发送的filename,调用os模块判断文件是否存在
  • 若存在,使用with以rb方式查看文件,并赋值data=file.read(),向客户端发送data
  • 若不存在文件,不返回任何内容,此时服务端也会做出相应动作

服务端代码如下:

import socket
import os

def main():
    server_socket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    server_socket.bind(('',7788))
    server_socket.listen(128)  #最多允许128个客户端同时连接
    while True:
        print("===开始接收请求===")
        new_server_socket,client_addr=server_socket.accept()
        print(f'客户端已建立连接,客户端地址是{client_addr}')
        while True:
            if new_server_socket:
                file_name=new_server_socket.recv(1024)  #接收filename
                if os.path.isfile(file_name):
                    with open(file_name,'rb') as file:
                        data=file.read()
                        new_server_socket.send(data)
                else:
                    print("文件不存在")
                    new_server_socket.close()
                    break
            else:
                print("客户端关闭连接")
                break

if __name__ == "__main__" :
    main()

三、成果展示

为了方便,我们用pycharm来演示

3.1 创建测试文件

aaa.txt
在这里插入图片描述

3.2 下载测试文件

客户端操作如下:
在这里插入图片描述
服务端响应如下:
在这里插入图片描述

3.3 查看下载文件

在这里插入图片描述

四、文件下载器升级版

之前的文件下载虽然实现了下载的功能,但是如果是陌生的服务器,我们并不知道对方服务器的文件到底有哪些,于是我就升级了一下程序

4.1、更新公告

  • 服务端会首先向客户端发送程序所在目录的文件列表,以供客户端下载
  • 使用logging模块,打印标准日志,更易观察
  • 修复已知BUG

代码如下:

4.1.1 升级版客户端代码
import ast
import logging
import os
import socket
from prettytable import PrettyTable

u = PrettyTable(['文件名', '文件大小'])

Format = logging.Formatter('%(levelname)s  %(asctime)s %(filename)s  %(funcName)s %(message)s ')
logger = logging.getLogger()
logger.setLevel('DEBUG')
console_handle = logging.StreamHandler()
console_handle.setLevel(level='INFO')
console_handle.setFormatter(Format)
logger.addHandler(console_handle)


def get_file_info():
    tcp_download_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    tcp_download_socket.connect((b'127.0.0.1', 7789))
    tcp_download_socket.send('getfile'.encode('utf-8'))
    filelist = tcp_download_socket.recv(1024)
    # 使用ast模块的literal_eval,将str转换为dict
    file_info = ast.literal_eval(filelist.decode('utf-8'))
    # 通过for循环与preaytable模块,创建表格
    for key, value in file_info.items():
        u.add_row([key, str(value)])
    print(u)
    tcp_download_socket.close()


def main():
    print('目标服务器文件列表如下')
    get_file_info()
    while True:
        tcp_download_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        tcp_download_socket.connect((b'127.0.0.1', 7788))
        print('\n')
        file_name = input('请输入要下载的文件内容:')
        tcp_download_socket.send(file_name.encode('utf-8'))  # 发送filename
        recv_data = tcp_download_socket.recv(1024 * 1024)  # 最大限制1MB
        logger.info('开始写入文件---')
        if recv_data:
            if recv_data.decode('utf-8') == 'filenotexists':
                logger.error('file not exists')
                tcp_download_socket.close()
                continue
            else:
                with open('new_' + file_name, 'bw') as doenload_file:
                    doenload_file.write(recv_data)
                    doenload_file.close()
                    if os.path.isfile('new_' + file_name):
                        logger.info('下载文件成功')
                        tcp_download_socket.close()
                    answer = input("是否下载其他文件")
                    if answer == 'y' or answer == 'Y':
                        continue
                    else:
                        break


if __name__ == '__main__':
    main()
4.1.2 升级版服务端代码
import socket
import os
import logging
import threading

Format = logging.Formatter('%(levelname)s  %(asctime)s %(filename)s %(funcName)s %(message)s ')
logger = logging.getLogger()
logger.setLevel('DEBUG')
console_handle = logging.StreamHandler()
console_handle.setLevel(level='INFO')
console_handle.setFormatter(Format)
logger.addHandler(console_handle)


def send_file_info():
    while True:
        logger.info("===开始接收发送信息请求===")
        file_info = {}
        files = os.listdir('.')
        for file in files:
            # 获取文件大小,因为文件太小多以取两位小数
            file_size = round(os.path.getsize(file) / 1024, 3)
            file_info[file] = f'{file_size}kb'
        tcp_getlist_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 为避免端口冲突,使用其他端口
        tcp_getlist_socket.bind(('', 7789))
        tcp_getlist_socket.listen(128)
        logger.info('创建发送文件信息套接字')
        new_server_socket, client_addr = tcp_getlist_socket.accept()
        logger.info(f'接收信息客户端已建立连接,客户端地址是{client_addr}')
        new_server_socket.send(str(file_info).encode('utf-8'))
        logger.info('成功发送客户端信息')
        # new_server_socket.close()
        # tcp_getlist_socket.close()
        logger.info('关闭发送文件信息套接字')


def main():
    # 创建send_file线程
    t1 = threading.Thread(target=send_file_info)
    t1.start()
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.bind(('', 7788))
    server_socket.listen(128)  # 最多允许128个客户端同时连接
    while True:
        logger.info("===开始接收下载请求===")
        new_server_socket, client_addr = server_socket.accept()
        logger.info(f'下载客户端已建立连接,客户端地址是{client_addr}')
        while True:
            if new_server_socket:
                file_name = new_server_socket.recv(1024).decode('utf-8')  # 接收filename

                if os.path.isfile(file_name):
                    with open(file_name, 'rb') as file:
                        data = file.read()
                        logger.info('发送文件{0}内容'.format(file_name))
                        new_server_socket.send(data)
                        new_server_socket.close()
                        break
                else:
                    logger.error("文件不存在")
                    new_server_socket.send('filenotexists'.encode('utf8'))
                    new_server_socket.close()
                    break
            else:
                print("客户端关闭连接")
                break


if __name__ == "__main__":
    main()

4.2 升级版成果展示

服务端启动服务开始监听
在这里插入图片描述
客户端启动:
首先会打印服务端文件内容,以列表形式展示
在这里插入图片描述
输入下载文件名 aaa.txt

在这里插入图片描述
成功下载
在这里插入图片描述
服务端日志如下:
很明显看到我是建立了两次链接的,前一次是为了接受文件列表信息
在这里插入图片描述

当文件不存在时,服务端会发送 file not exists 消息,客户端收到消息后,做出相应动作
在这里插入图片描述
重新填写正确文件,下载成功
在这里插入图片描述

问题处理

1、发送文件函数只发送一次,当客户端第二次启动时,会报连接失败
原因:发送文件函数只调用一次,第二次客户端连接时,函数socket实际上已关闭
解决方法:
给函数加上while True,去掉close方法,创建多线程

    # 创建send_file线程
    t1 = threading.Thread(target=send_file_info)
    t1.start()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陈小c

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值