socket编程实现文件传输功能

socket编程实现文件传输功能

先建立两个进程的TCP连接,然后client先向server发送文件信息(包括文件名和文件大小以及MD5值),这个文件信息的大小是预先设定好的,也就是client和server都知道,这样server才能准确判断接收的数据哪些是文件信息哪些是真正的文件。这里用到了struct库对文件信息进行处理。这里的struct类似于c中的结构体,可以把变量转换成具有c结构体形式的字符串。比如我的例子中定义了这样一个struct形式:

1
HEAD_STRUCT = '128sIq32s'

它就相当于c中如下结构体:

它就相当于c中如下结构体:

1
2
3
4
5
6
struct{
char fileName[128]; //文件名
int fileNameSize; //文件名长度
long long fileSize; //文件大小
char MD5[32]; //MD5值
}

这个结构体的空间大小是固定的,于是当client按照这个格式发送文件信息的时候,server也以相同的格式接收信息。由于文件名的长度不固定,于是这里我还发送了一个文件名的长度,这样server就可以根据这个长度对fileName进行截取。
然后再发送文件,这里我是用1024B的缓冲区进行接收(最后一次小于或等于1024B)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# 服务端

import socket
import hashlib
import struct

HOST = 'localhost'
PORT = 1307
BUFFER_SIZE = 1024
HEAD_STRUCT = '128sIq32s'
info_size = struct.calcsize(HEAD_STRUCT)


def cal_md5(file_path):
with open(file_path, 'rb') as fr:
md5 = hashlib.md5()
md5.update(fr.read())
md5 = md5.hexdigest()
return md5


def unpack_file_info(file_info):
file_name, file_name_len, file_size, md5 = struct.unpack(HEAD_STRUCT, file_info)
file_name = file_name[:file_name_len]
return file_name, file_size, md5


def recv_file():
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = (HOST, PORT)
sock.bind(server_address)
sock.listen(1)
client_socket, client_address = sock.accept()
print "Connected %s successfully" % str(client_address)

file_info_package = client_socket.recv(info_size)
file_name, file_size, md5_recv = unpack_file_info(file_info_package)

recved_size = 0
with open(file_name, 'wb') as fw:
while recved_size < file_size:
remained_size = file_size - recved_size
recv_size = BUFFER_SIZE if remained_size > BUFFER_SIZE else remained_size
recv_file = client_socket.recv(recv_size)
recved_size += recv_size
fw.write(recv_file)
md5 = cal_md5(file_name)
if md5 != md5_recv:
print 'MD5 compared fail!'
else:
print 'Received successfully'
except socket.errno, e:
print "Socket error: %s" % str(e)
finally:
sock.close()

if __name__ == '__main__':
recv_file()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#客户端
import socket
import os
import hashlib
import struct

HOST = 'localhost'
PORT = 1307
BUFFER_SIZE = 1024
HEAD_STRUCT = '128sIq32s'


def cal_md5(file_path):
with open(file_path, 'rb') as fr:
md5 = hashlib.md5()
md5.update(fr.read())
md5 = md5.hexdigest()
return md5


def get_file_info(file_path):
file_name = os.path.basename(file_path)
file_name_len = len(file_name)
file_size = os.path.getsize(file_path)
md5 = cal_md5(file_path)
return file_name, file_name_len, file_size, md5


def send_file(file_path):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = (HOST, PORT)

file_name, file_name_len, file_size, md5 = get_file_info(file_path)
file_head = struct.pack(HEAD_STRUCT, file_name, file_name_len, file_size, md5)

try:
print "Start connect"
sock.connect(server_address)
sock.send(file_head)
sent_size = 0

with open(file_path) as fr:
while sent_size < file_size:
remained_size = file_size - sent_size
send_size = BUFFER_SIZE if remained_size > BUFFER_SIZE else remained_size
send_file = fr.read(send_size)
sent_size += send_size
sock.send(send_file)
except socket.errno, e:
print "Socket error: %s" % str(e)
finally:
sock.close()
print "Closing connect"

if __name__ == '__main__':
file_path = raw_input('Please input file path:')
if not file_path:
file_path = 'test.txt'
send_file(file_path)

注意:

  • 在某些时候(比如传输大文件)会发生丢包的情况,这时候接收的文件大小会小于发送文件的大小,两个MD5值也会不一样。

原文链接:https://blog.csdn.net/Thare_Lam/article/details/49506565