python 网络传输_Python3中使用零拷贝技术来提高网络文件传输时的系统吞吐量

首先,推荐一篇好文,这篇文章细致地描述了零拷贝技术的原理,以及其与传统拷贝过程的区别:http://www.linuxjournal.com/article/6345?page=0,0

从总体上来简单总结一下零拷贝技术可以通过对比来理解:

传统的拷贝过程大致是这样一个过程:

1. 通过直接内存访问数据进入操作系统内核的缓存(数据拷贝到内核空间)——CPU将数据拷贝到用户空间——CPU将数据写入到套接字缓存——内存直接访问拷贝发送数据;

零拷贝技术的过程是这样的:

2. 通过直接内存访问数据进入操作系统内核缓存(数据拷贝到内核空间)——CPU直接调用sendfie系统调用直接将内核缓存拷贝到套接字缓存——内存直接访问拷贝发送数据;甚至通过进一步的优化,内核之内的那次数据复制也可以省掉,这是通过将内核缓存的相关信息传递给套结字缓存,系统利用这些信息直接读取内核缓存发送数据。

至于具体效果如何,可以使用Python3提供的相关系统调用的封装来测试对比一下。

首先,要实现一个简单的文件接收服务器,实现起来非常简单(zerocopyserver.py):

'''

Created on Aug 28, 2015

@author: felix

'''

import socket

import hashlib

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

server_address = ('0.0.0.0', 10000)

print('starting up on %s port %s' % server_address)

sock.bind(server_address)

sock.listen(1)

while True:

print('Waiting for a connection')

connection, client_address = sock.accept()

size = 0

try:

i = 0

while True:

data = connection.recv(65536)

#print('Received data: "%s"' % data)

i += 1

if data:

size += len(data)

#print(data)

#cnt += data

if i % 10000 == 0:

print('Current loop: ', i)

#print('[%s]' % data)

else:

print('Done!')

break

print('I:', i)

print('Total size: ', size)

#print('Hash of received data :', hashlib.md5(cnt).hexdigest())

finally:

connection.close()

可以看到,我们统计了最终收到的数据的大小,以判断两种方式发送的文件是否是相同的。

下面是传统的拷贝文件的程序:copyclient.py

'''

Created on Aug 29, 2015

@author: felix

'''

import socket

import time

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

server_address = ('127.0.0.1', 10000)

sock.connect(server_address)

start = time.time()

try:

with open(r'/home/felix/Downloads/Forrest.Gump.1994.1080p.BluRay.x264.anoXmous/Forrest.Gump.1994.1080p.BluRay.x264.anoXmous_.mp4', 'rb') as f:

message = f.read()

sock.sendall(message)

finally:

sock.close()

end = time.time()

print('Total time: ', end-start)

下面是使用零拷贝实现的版本zerocopyclient.py

'''

Created on Aug 29, 2015

@author: felix

'''

import os

import socket

import time

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

server_address = ('127.0.0.1', 10000)

sock.connect(server_address)

start = time.time()

try:

with open(r'/home/felix/Downloads/Forrest.Gump.1994.1080p.BluRay.x264.anoXmous/Forrest.Gump.1994.1080p.BluRay.x264.anoXmous_.mp4', 'rb') as f:

#message = f.read()

#sock.sendall(message)

ret = 0

offset = 0

while True:

ret = os.sendfile(sock.fileno(), f.fileno(), offset, 65536)

offset += ret

if ret == 0:

break

finally:

sock.close()

end = time.time()

print('Total time: ', end-start)

在启动文件接收服务之后,我们就可以分别来测试两种传输方式的差别了:

copyclient.py

zerocopyclient.py

Total time:  1.2538435459136963

Total time:  0.5658297538757324

Total time:  1.0714919567108154

Total time:  0.5623219013214111

可以明显看到,同样传输2.4GB的数据,使用零拷贝技术的客户端所消耗的时间要比传统的拷贝方式更快一些。由于是本机测试,没有经过物理网卡,如果经过网卡的话,总的传输时间会受限于网卡的传输速度。

这样,对于只需要传输文件,而不用用户程序去处理文件内容的情况下,这种零拷贝技术能够极大提高本机的吞吐量,如果这样的拷贝操作非常频繁的话,通过零拷贝优化还是可以省出可观的时间的,同时也节省了CPU的时钟周期(可以用来做其它事情)。

PS:请使用Python3来运行以上的测试程序;在测试开始前先启动测试的文件接收服务(zerocopyserver.py);零拷贝技术需要系统内核支持(>=2.4.19),故测试之前请确认系统版本是否支持零拷贝系统调用。