在Python中模拟慢速网络的简单方法
场景:我有一个客户,他有两条网络连接到一个服务器。一条是用手机连接,另一条是用无线网络(wlan)连接。
我解决这个问题的方法是让服务器在两个端口上监听。但是,手机连接的速度应该比无线网络连接慢。通常发送的数据只是一些文本。我通过让手机连接每五秒发送一次数据,而无线网络连接每秒发送一次数据,来解决这个速度慢的问题。
不过,有没有更好的方法来让连接变慢呢?比如说发送更小的数据包,这样我就需要发送更多的数据了?
有没有什么好的办法来减慢其中一个连接的速度呢?
Orjanp
这是一个简单的客户端示例,只包含一个连接。
def client():
import sys, time, socket
port = 11111
host = '127.0.0.1'
buf_size = 1024
try:
mySocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
mySocket.connect((host, port))
except socket.error, (value, message):
if mySocket:
mySocket.close()
print 'Could not open socket: ' + message
sys.exit(1)
mySocket.send('Hello, server')
data = mySocket.recv(buf_size)
print data
time.sleep(5)
mySocket.close()
client()
这是一个简单的服务器,监听一个端口。
def server():
import sys, os, socket
port = 11111
host = ''
backlog = 5
buf_size = 1024
try:
listening_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
listening_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
listening_socket.bind((host, port))
listening_socket.listen(backlog)
except socket.error, (value, message):
if listening_socket:
listening_socket.close()
print 'Could not open socket: ' + message
sys.exit(1)
while True:
accepted_socket, adress = listening_socket.accept()
data = accepted_socket.recv(buf_size)
if data:
accepted_socket.send('Hello, and goodbye.')
accepted_socket.close()
server()
4 个回答
网络连接“慢”的原因主要是延迟和带宽。如果你想要一个真实的模拟,就需要找出你手机连接的带宽和延迟,然后在你的客户端程序中模拟这些参数。
不过你似乎是说你发送的数据很少,所以带宽可能对你的连接速度影响不大。这样的话,你只需要模拟延迟,也就是在每次发送数据包之间“睡眠”一段时间,延迟时间就是你设定的。5秒听起来有点长。
如果你觉得带宽可能有影响,其实模拟起来也很简单:带宽就是你每秒能发送的最大字节数,而延迟则是数据到达目的地所需的时间。
具体怎么做呢:你可以设置一个全局的时间戳“blockedUntil”,表示你的连接在什么时候可以再次发送数据。程序开始时把它初始化为0,假设此时连接还没有被使用。每当你有一个数据包要发送时,如果“blockedUntil”小于当前时间,就把它设置为当前时间。然后计算写入你虚拟“线路”所需的时间,方法是用数据包的大小除以带宽,这样就能得到一个时间段,再加上延迟,把这个总时间加到“blockedUntil”上。
接下来计算dt = blockedUntil - 当前时间,把数据包放入队列,并设置一个定时器,在“dt”时间后触发,这样就会发送队列中的第一个数据包。
这样,你就模拟了带宽和延迟。
补充:有人提到丢包的问题。你可以通过设置一个丢包的概率来模拟这个情况。注意:这只适用于处理像以太网或UDP这样的无连接协议的数据包。在TCP的情况下,这种方法就不适用了。
虽然我可能没有直接回答你问的问题,但我建议你可以找一些更底层的软件来实现这个功能。
Netlimiter 是一个适用于Windows的工具。我觉得 BWMeter 和 Bandwidth Controller 也可以做到这一点。
pyshaper 是一个类似的工具,适用于Linux,而且是开源的。你可能可以把它导入到你的Python程序里。
(另外,你也可以考虑一下,你的路由器可能已经能够按照你想要的方式来管理流量。不过,这样的话,你的软件就需要依赖这个路由器,配置起来可能会比较麻烦。)
除了使用外部工具来模拟你感兴趣的网络情况,还有一个不错的方法就是使用替代的socket实现。
这个方法的核心是把socket的创建过程变成你函数的一个参数,而不是直接导入socket模块并使用它。正常情况下,你会传入真实的socket类型,但当你想测试一些不好的网络条件时,就可以传入一个模拟这些条件的实现。例如,你可以创建一个socket类型,它可以设置延迟和带宽(注意,这段代码未经测试):
import time, socket
class ControllableSocket:
def __init__(self, latency, bandwidth):
self._latency = latency
self._bandwidth = bandwidth
self._bytesSent = 0
self._timeCreated = time.time()
self._socket = socket.socket()
def send(self, bytes):
now = time.time()
connectionDuration = now - self._timeCreated
self._bytesSent += len(bytes)
# How long should it have taken to send how many bytes we've sent with our
# given bandwidth limitation?
requiredDuration = self._bytesSent / self._bandwidth
time.sleep(max(requiredDuration - connectionDuration, self._latency))
return self._socket.send(bytes)
如果你实现了其他socket方法,比如connect、recv等,你就可以用这个类的实例替代真实socket类型的实例。这样,你程序的其他部分就完全不需要知道你在做模拟,简化了程序,同时也让你可以通过实现一个新的socket类型来尝试许多不同的网络配置。
这个想法是Twisted明确区分“协议”和“传输”的原因之一。“协议”是指那些知道如何解释网络字节并生成新字节发送到网络的对象,而“传输”则是指那些知道如何从网络获取字节并将字节放到网络上的对象。这种区分简化了测试,并允许像这样的新配置,其中由传输提供了某些其他网络条件的模拟(这些条件在现实中可能很难实现)。