Python3学习之路~8.4 利用socket实现文件传送+MD5校验

利用socket实现文件传送,大约分为如下几步:

1.读取文件名
2.检测文件是否存在
3.打开文件(别忘了最后关闭文件)
4.检测文件大小
5.发送文件大小给客户端
6.等客户端确认
7.开始边读边发数据
8.md5验证

实例1:实现步骤1-7

运行代码

#Author:Zheng Na

#ftp服务端
import socket
import os

server = socket.socket()

server.bind(('localhost',6969))

server.listen()

while True:
    print("等待新客户端连接")
    conn,addr = server.accept()
    print("new conn: ",addr)

    while True:
        print("等待新指令")
        data = conn.recv(1024)
        if not data:
            print("客户端已断开")
            break
        cmd,filename = data.decode().split() # 1.读取文件名
        print("文件名:",filename)
        if os.path.isfile(filename): # 2.判断文件是否存在
            f = open(filename,'rb') # 3.打开文件
            file_size = os.stat(filename).st_size # 4.检测文件大小
            conn.send(str(file_size).encode()) # 5.发送文件大小给客户端
            print("等待客户ack应答...")
            client_final_ack = conn.recv(1024)  # 6.等待客户端确认
            print("客户应答:", client_final_ack.decode("UTF-8"))
            for line in f: # 7.开始边读边发数据
                conn.send(line)
            f.close() # 关闭文件
            print("send done")

server.close()
ftp_socket_server1_simple.py
#Author:Zheng Na

#ftp客户端
import socket

client = socket.socket()

client.connect(('localhost',6969))

while True:
    cmd = input(">>: ").strip()
    if len(cmd) == 0: continue
    if cmd.startswith("get"):
        client.send(cmd.encode())
        server_response = client.recv(1024)
        print("server response: ",server_response)
        client.send(b'ready to recv file')
        file_total_size = int(server_response.decode())
        received_size = 0
        filename = cmd.split()[1]
        f = open(filename+'.new','wb')
        while received_size < file_total_size:
            data = client.recv(1024)
            received_size += len(data)
            f.write(data)
            # print(file_total_size,received_size)
        else:
            print("file recv done",file_total_size,received_size)
            f.close()

client.close()
ftp_socket_client1_simple.py

输出结果

[root@hadoop my-test-files]# python3 ftp_socket_server1_simple.py 
等待新客户端连接
new conn:  ('127.0.0.1', 34556)
等待新指令
文件名: test.txt
等待客户ack应答...
客户应答: ready to recv file
send done
等待新指令

[root@hadoop my-test-files]# python3 ftp_socket_client1_simple.py 
>>: get test.txt
server response:  b'5028317'
file recv done 5028317 5028317
>>: 

[root@hadoop my-test-files]# ll test*
-rw-r--r-- 1 root root 5028317 12月  5 18:28 test.txt
-rw-r--r-- 1 root root 5028317 12月  6 16:16 test.txt.new
[root@hadoop my-test-files]# diff test.txt test.txt.new # 比较两个文件是否相同
[root@hadoop my-test-files]# 
输出结果

实例2:实现步骤8:md5验证

接下来,我们来给传送的文件进行MD5验证。

思路:

在服务端对要发送的文件进行MD5加密,得到一个16进制格式的hash1,发送给客户端;
在客户端对接收到的文件进行MD5加密,得到一个hash2,并与hash1进行对比,如果相同,说明接收到的文件与服务器文件一致。
由于文件比较大,可以打开文件后一行一行的进行md5加密,这样操作与整个文件一起加密效果是一致的。
比如下面两段代码效果是一致的:

# 代码1
import hashlib
hash = hashlib.md5()
hash.update(b'Hello ')
hash.update(b'World')
print(hash.hexdigest()) # b10a8db164e0754105b7a99be72e3fe5

# 代码2
import hashlib
hash = hashlib.md5()
hash.update(b'Hello World')
print(hash.hexdigest()) # b10a8db164e0754105b7a99be72e3fe5
md5关键代码

另外,由于服务端先发送了文件,再发送md5,为了防止两次send之间的粘包,我们可以在两次send之间加一段 客户确认 的代码,但是这次我们不这么做:
由于在客户端接收文件时是循环接收,我们可以在接收文件前,加一个判断,判断最后一次接收文件的大小,如果小于1024字节,那么最后一次只接收文件最后剩下的部分。
这样就可以保证最后接收的文件正好是发送文件的大小,从而不会导致粘包。

运行代码

#Author:Zheng Na

#ftp服务端
import socket
import os
import hashlib

server = socket.socket()

server.bind(('localhost',6969))

server.listen()

while True:
    print("等待新客户端连接")
    conn,addr = server.accept()
    print("new conn: ",addr)

    while True:
        print("等待新指令")
        data = conn.recv(1024)
        if not data:
            print("客户端已断开")
            break
        cmd,filename = data.decode().split() # 1.读取文件名
        print("文件名:",filename)
        if os.path.isfile(filename): # 2.判断文件是否存在
            f = open(filename,'rb') # 3.打开文件
            file_size = os.stat(filename).st_size # 4.检测文件大小
            conn.send(str(file_size).encode()) # 5.发送文件大小给客户端
            print("等待客户ack应答...")
            client_final_ack = conn.recv(1024)  # 6.等待客户端确认
            print("客户应答:", client_final_ack.decode("UTF-8"))
            m = hashlib.md5() # 8.1 md5
            for line in f: # 7.开始边读边发数据
                m.update(line) # 8.2 md5
                conn.send(line)

            server_file_md5 = m.hexdigest() # 8.3 md5
            print("server file md5: ",server_file_md5)
            conn.send(server_file_md5.encode()) # 8.4 md5

            f.close() # 关闭文件
            print("send done")

server.close()
ftp_socket_server2_md5.py
#Author:Zheng Na

#ftp客户端
import socket
import hashlib

client = socket.socket()

client.connect(('localhost',6969))

while True:
    cmd = input(">>: ").strip()
    if len(cmd) == 0: continue
    if cmd.startswith("get"):
        client.send(cmd.encode())
        server_response = client.recv(1024)
        print("server response: ",server_response)
        client.send(b'ready to recv file')
        file_total_size = int(server_response.decode())
        received_size = 0
        filename = cmd.split()[1]
        f = open(filename+'.new','wb')
        m = hashlib.md5()
        while received_size < file_total_size: # 接收文件,防止粘包
            if file_total_size - received_size > 1024: # 要收的不止一次
                size = 1024
            else:# 最后一次了,剩多少收多少
                size = file_total_size -received_size
                print("last receive:",size)

            data = client.recv(size)
            received_size += len(data)
            m.update(data)
            f.write(data)
            # print(file_total_size,received_size)
        else:
            client_file_md5 = m.hexdigest()
            print("file recv done",file_total_size,received_size)
            f.close()

        server_file_md5 = client.recv(1024).decode()
        print("server file md5: ",server_file_md5)
        print("client file md5: ",client_file_md5)
        if server_file_md5 == client_file_md5:
            print("server file md5 and client file md5 are same")
        else:
            print("server file md5 and client file md5 are different")

client.close()
ftp_socket_client2_md5.py

注意:使用md5加密,运行结果会变慢。

输出结果

[root@hadoop my-test-files]# python3 ftp_socket_server2_md5.py 
等待新客户端连接
new conn:  ('127.0.0.1', 34566)
等待新指令
文件名: test.txt
等待客户ack应答...
客户应答: ready to recv file
server file md5:  01e16e921c663c9e90246ddb7d9a746f
send done
等待新指令

[root@hadoop my-test-files]# python3 ftp_socket_client2_md5.py 
>>: get test.txt
server response:  b'5028317'
last receive: 335
file recv done 5028317 5028317
server file md5:  01e16e921c663c9e90246ddb7d9a746f
client file md5:  01e16e921c663c9e90246ddb7d9a746f
server file md5 and client file md5 are same
>>: 

[root@hadoop my-test-files]# ll test*
-rw-r--r-- 1 root root 5028317 12月  5 18:28 test.txt
-rw-r--r-- 1 root root 5028317 12月  6 17:44 test.txt.new
[root@hadoop my-test-files]# diff test.txt test.txt.new 
[root@hadoop my-test-files]# 
输出结果

 

posted @ 2019-03-10 22:50  zhengna  阅读(368)  评论(0编辑  收藏  举报