Python使用socket传输文件

来源:互联网 发布:天涯明月刀情缘 知乎 编辑:程序博客网 时间:2024/05/17 09:01

昨晚的高网实验要求用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 socketimport structimport hashlibHOST = 'localhost'PORT = 1307BUFFER_SIZE = 1024HEAD_STRUCT = '128sIq32s'   # Structure of file headdef 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 socketimport structimport osimport timeimport hashlibHOST = 'localhost'PORT = 1307BUFFER_SIZE = 1024FILE_NAME = '001.dmg'   # Change to your fileFILE_SIZE = os.path.getsize(FILE_NAME)HEAD_STRUCT = '128sIq32s'  # Structure of file headdef 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值也会不一样。
0 0