您的位置:首页 > 编程语言 > Python开发

Python使用socket传输文件

2015-10-30 09:33 741 查看
昨晚的高网实验要求用Socket编程实现两台机器的文件传输,还要记录传输时间并进行MD5验证,于是便用python简单实现了下。

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

HEAD_STRUCT = '128sIq32s'


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

struct{
char fileName[128];
int fileNameSize;
long long fileSize;
char MD5[32];
}


这个结构体的空间大小是固定的,于是当client按照这个格式发送文件信息的时候,server也以相同的格式接收信息。由于文件名的长度不固定,于是这里我还发送了一个文件名的长度,这样server就可以根据这个长度对fileName进行截取。

然后再发送文件,这里我是用1024B的缓冲区进行接收(最后一次小于或等于1024B)。

***server.py***

import socket
import struct
import hashlib

HOST = 'localhost'
PORT = 1307
BUFFER_SIZE = 1024
HEAD_STRUCT = '128sIq32s'   # Structure of file head

def recv_file():
# Create a TCP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Enable reuse address/port
# sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# Bind the socket to the port
server_address = (HOST, PORT)
# Bind server address
sock.bind(server_address)
print "Starting server on %s port %s" % server_address
# Listen to clients
sock.listen(1)
print "Waiting to receive from client"
client_socket, client_address = sock.accept()
print "Socket %s:%d has connect" % client_address

# Receive file info
info_struct = struct.calcsize(HEAD_STRUCT)
file_info = client_socket.recv(info_struct)
file_name2, filename_size, file_size, md5_recv = struct.unpack(HEAD_STRUCT, file_info)
file_name = file_name2[:filename_size]
fw = open(file_name, 'wb')
recv_size = 0
print "Receiving data..."
while (recv_size < file_size):
if(file_size - recv_size < BUFFER_SIZE):
file_data = client_socket.recv(file_size - recv_size)
recv_size = file_size
else:
file_data = client_socket.recv(BUFFER_SIZE)
recv_size += BUFFER_SIZE
fw.write(file_data)
fw.close()
print "Accept success!"
print "Calculating MD5..."
fw = open(file_name, 'rb')
md5_cal = hashlib.md5()
md5_cal.update(fw.read())
print "  Recevie MD5 : %s" %md5_recv
print "Calculate MD5 : %s" % md5_cal.hexdigest()
fw.close()

if __name__ == '__main__':
recv_file()


***client.py***

import socket
import struct
import os
import time
import hashlib

HOST = 'localhost'
PORT = 1307
BUFFER_SIZE = 1024
FILE_NAME = '001.dmg'   # Change to your file
FILE_SIZE = os.path.getsize(FILE_NAME)
HEAD_STRUCT = '128sIq32s'  # Structure of file head

def send_file():
# Create a TCP/IP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Connect the socket to the server
server_address = (HOST, PORT)

#Calculate MD5
print "Calculating MD5..."
fr = open(FILE_NAME, 'rb')
md5_code = hashlib.md5()
md5_code.update(fr.read())
fr.close()
print "Calculating success"

# Need open again
fr = open(FILE_NAME, 'rb')
# Pack file info(file name and file size)
file_head = struct.pack(HEAD_STRUCT, FILE_NAME, len(FILE_NAME), FILE_SIZE, md5_code.hexdigest())

try:
# Connect
sock.connect(server_address)
print "Connecting to %s port %s" % server_address
# Send file info
sock.send(file_head)
send_size = 0
print "Sending data..."
time_start = time.time()
while(send_size < FILE_SIZE):
if(FILE_SIZE - send_size < BUFFER_SIZE):
file_data = fr.read(FILE_SIZE - send_size)
send_size = FILE_SIZE
else:
file_data = fr.read(BUFFER_SIZE)
send_size += BUFFER_SIZE
sock.send(file_data)
time_end = time.time()
print "Send success!"
print "MD5 : %s" % md5_code.hexdigest()
print "Cost %f seconds" % (time_end - time_start)
fr.close()
sock.close()
except socket.errno, e:
print "Socket error: %s" % str(e)
except Exception, e:
print "Other exception : %s" % str(e)
finally:
print "Closing connect"

if __name__ == '__main__':
send_file()


注意:
- 在使用的时候可把server和client放在两个不同文件夹下,待发文件和client放在同一文件夹下,client.py中的FILE_NAME需要修改成待发的文件名。
- 在某些时候(比如传输大文件)会发生丢包的情况,这时候接收的文件大小会小于发送文件的大小,两个MD5值也会不一样。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: