生活不易 我用pythonPython自学与资料分享程序员

Python3.6:socket实现文件传输

2018-02-13  本文已影响22人  james_chang

既然我们实现了简单的数据传输和简单的ssh客户端,那么我们能不能实现传输文件呢?

先理一理思路,实现简单ssh客户端时有个要点就是接收的数据有时候会大于我们给定的1024,需要循环接收,那么文件不也是一个道理?先发送文件大小,只要还没有收到这个大小的数据就说明没有收完,一直到收到的数据的大小和发送来的数据大小一样就行了为了防止出错,我们还可以加上md5验证,这样就不会出错了

服务器端:

# 1.读取客户端发来的文件名
# 2.查找文件是否存在
# 3.打开该文件
# 4.检测文件大小
# 5.发送大小给客户端
# 6.等待客户端确认
# 7.开始边读边发
# 8.发送md5校验

import socket
import os
import hashlib

server = socket.socket()
server.bind(('localhost', 8080))
server.listen()
while True:
    conn, addr = server.accept()
    print('等待指令:')
    while True:
        data = conn.recv(1024)
        if not data:
            print('客户端断开')
            break

        # 第一次接收的是命令,包括get和文件名,用filename接受文件名
        cmd, filename = data.decode().split()

        # 接收到的文件名判断是不是一个文件
        if os.path.isfile(filename):

            # 如果是,读模式打开这个文件
            f = open(filename, 'rb')

            # 生成md5对象
            m = hashlib.md5()

            # 将文件大小赋值给file_size
            file_size = os.stat(filename).st_size

            # 发送文件大小
            conn.send(str(file_size).encode())

            # 接收确认信息
            conn.recv(1024)

            # 开始发文件
            for line in f:

                # 发一行更新一下md5的值,因为不能直接md5文件,到最后读完就是一个文件的md5的值
                m.update(line)

                # 一行一行发送
                conn.send(line)

            # 打印挣个文件的md5
            print('file md5:', m.hexdigest())

            # 关闭文件
            f.close()

            # send md5
            conn.send(m.hexdigest().encode())
        print('send done')
    break
server.close()

客户端:

import socket
import hashlib
client = socket.socket()
client.connect(('localhost', 8080))

while True:

    # 获得命令和想要下载的文件   格式:get xxxx
    cmd = input('what do u want?:').strip()

    # 判断如果获得的字符串中有'get'开始执行一下步骤
    if cmd.startswith('get'):

        # 发送指令
        client.send(cmd.encode())

        # 接收文件大小
        response = client.recv(1024)
        print('file size:', response.decode())

        # 发送确认信息
        client.send(b'111')

        # 转整形,方便下面大小判断
        file_size = int(response.decode())

        # 赋值方便最后打印大小
        new_file_size = file_size

        # 将命令分割获得文件名
        filename = cmd.split()[1]

        # 写模式创建文件
        f = open(filename, 'wb')

        # 生成md5对象
        m = hashlib.md5()

        # 进入循环判断收文件
        while new_file_size > 0:
            data = client.recv(1024)
            
            # 收多少减多少
            new_file_size -= len(data)
            
            # 同步服务器端,收一次更新一次md5
            m.update(data)
            
            # 写入数据
            f.write(data)
        else:
            
            # 得到下载完的文件的md5
            new_file_md5 = m.hexdigest()
            
            # 打印下载文件大小
            print('file recv done', file_size)
            f.close()
            
        # 接收服务器端的文件的md5
        server_file_md5 = client.recv(1024)
        
        # 打印两端的md5值,看是否相同
        print('server file md5:', server_file_md5)
        print('recv file md5:', new_file_md5)

    else:
        continue

其实只要理顺了逻辑,就能很轻易的写出来,当然要注意一些容易犯错的点

在客户端跟服务器端的交互中,服务器端的发送接收跟客户端的发送接收是一一对应的,这是重点中的重点,只要记住这个,就可以实现自己的逻辑

还有就是粘包的问题,尽量避免两条send语句紧挨,如果出现紧挨的情况就要想办法解决粘包的问题

文件不能直接md5,需要读取一行数据更新一次md5的值
还有就是数据的编码解码问题

上一篇 下一篇

猜你喜欢

热点阅读