py小强

  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 :: 管理 ::

我们把功能细化,然后用函数包装一下,这样可读性好而且加入新功能的时候也比较容易。

  • 实现步骤
    • 先把除了import和环境变量部分全部装到run()函数中,并且在最后加入if name == 'main' 部分
    • 看run()里面有没有实现某个功能的部分,在run()上面定义该功能的函数,把代码块全部剪切到该函数中
    • 看看该函数用到run里的什么变量,作为参数加进去即可

server.py

import socket
import struct
import json
import os.path

# 定义一个共享用文件夹的路径,注意:这部分一般应该写在配置文件中
SHARE_DIR = r'C:\temp'


def get(conn, cmds):  # get内容放入函数中
    filename = cmds[1]

    # 第一步:生成数据的报头信息数据(非固定长度)
    header_dic = {
        'filename': filename,
        'md5': 'xxxx',
        'total_size': os.path.getsize(os.path.join(SHARE_DIR, filename))
    }

    # 将报头encode(网络传输准备)
    header_json = json.dumps(header_dic)
    header_bytes = header_json.encode('utf-8')

    # 第二部:发送报头长度
    conn.send(struct.pack('i', len(header_bytes)))

    # 第三部:再发报头
    conn.send(header_bytes)

    # 第四部:最后发真实数据
    # 以read的模式读取文件内容(由于用于网络传输,所以是rb模式),并且发送给客户端
    with open(os.path.join(SHARE_DIR, filename), 'rb') as f:
        # conn.send(f.read())  # 如果文件很大,内存会占满
        for line in f:  # 这样更加节省内存
            conn.send(line)


def run():
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server.bind(('127.0.0.1', 8080))
    server.listen(3)

    while True:
        conn, addr = server.accept()  # 等待连接,连接成功时实例化conn对象用来接下来的通信
        while True:
            try:
                # 收命令
                res = conn.recv(1024)  # 接收客户端命令(bytes类型),格式get a.txt
                if not res: break  # 异常处理:防止客户端强制断开连接,导致linux系统死循环
                # 解析命令,提取相应命令参数
                cmds = res.decode('utf-8').split()  # 解码后用空格分割
                if cmds[0] == 'get':
                    get(conn, cmds)  # 用函数封装了get的内容
            except ConnectionResetError as e:
                print(e)
                break
        conn.close()
    server.close()


if __name__ == '__main__':  # 这个写法是防止这个py文件被当做模块调用时,执行run()
    run()

client.py

import socket
import struct
import json
import os.path

# 定义下载的目录路径,注意:这部分一般应该写在配置文件中
DOWNLOAD_DIR = r'C:\download'


def get(client):
    # 接受服务器发来的文件内容,以write模式打开新文件,接受服务端发来的文件内容并写入
    # 第一步:先收报头长度(4bytes)的数据
    obj = client.recv(4)  # 接收服务器发来的,由struct打包的4bytes包
    header_size = struct.unpack('i', obj)[0]  # 解包,获得报头长度

    # 第二部:收报头
    header_bytes = client.recv(header_size)  # 根据报头长度收报头

    # 第三部:从报头中解析出真实数据的描述信息
    header_json = header_bytes.decode('utf-8')
    header_dic = json.loads(header_json)
    total_size = header_dic['total_size']
    filename = header_dic['filename']

    # 第四部:接受真实收据
    with open(os.path.join(DOWNLOAD_DIR, filename), 'wb') as f:
        recv_size = 0
        while recv_size < total_size:  # 循环直到收完指定长度的包
            line = client.recv(1024)
            f.write(line)
            recv_size += len(line)
            print('总大小%s,已经下载%s' % (total_size, recv_size))  # 这样我们可以知道进度


def run():
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client.connect(('127.0.0.1', 8080))  # 连接服务器IP和端口
    while True:
        # 发命令
        cmd = input(">>>>:").strip()
        if not cmd:continue
        client.send(cmd.encode('utf-8'))  # 注意:这部分不需要放在get()函数中,因为发送数据是通用的操作
        cmds = cmd.split()
        if cmds[0] == 'get':
            get(client)
    client.close()


if __name__ == '__main__':
    run()
posted on 2019-08-04 17:53  py小强  阅读(175)  评论(0)    收藏  举报