在python中模拟慢速网络的简单方法

Posted

技术标签:

【中文标题】在python中模拟慢速网络的简单方法【英文标题】:Simple way to simulate a slow network in python 【发布时间】:2010-12-22 10:59:39 【问题描述】:

情景。我有一个与服务器有两个网络连接的客户端。一种连接使用手机,另一种使用wlan连接。

我解决这个问题的方法是让服务器监听两个端口。但是,移动连接应该比 wlan 连接慢。发送的数据通常只是一行文本。我已经解决了连接速度较慢的问题,允许移动连接以 5 秒的间隔发送数据,而 wlan 连接的间隔为 1 秒。

但是有没有更好的方法来降低速度?也许通过发送较小的数据包,这意味着我需要发送更多数据?

关于减慢其中一个连接的好方法有什么想法吗?

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()

【问题讨论】:

你为什么要放慢速度? 相关:***.com/questions/1094760/… 这是一个难题。移动连接意味着更长的旅行时间/更多的包裹丢失等。仅以较慢的间隔发送它们并不是真正的模拟。 @特里:只是为了有一个连接,发送数据需要更长的时间。 @ Christophe:是的,我读过这个问题,但我想让这个问题尽可能简单,我觉得外部软件会使它复杂化很多。 //@Shrinkrin:我的错。应该更详细地说明问题。我不是在对移动网络进行真实模拟之后。只需降低移动连接速度即可。 相关:***.com/questions/3536249/… 【参考方案1】:

除了使用外部工具来模拟您感兴趣的网络类型之外,一种好的方法是使用套接字的替代实现。

这涉及将套接字构造作为函数的参数,而不是导入套接字模块并直接使用它。对于正常操作,您将传入真正的套接字类型,但是当您要测试各种不利的网络条件时,您可以传入模拟这些条件的实现。例如,您可以创建一个参数化延迟和带宽的套接字类型(未经测试的代码,请注意):

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)

如果您实现了其他套接字方法,connect、recv 等,您可以将此类的实例替换为实际套接字类型的实例。这使您的程序的所有其余部分完全无需了解任何模拟知识,从而简化了它,同时还允许您通过实现模拟它们的新套接字类型来尝试许多不同的网络配置。

这个想法是 Twisted 明确区分“协议”概念的原因之一 - 知道如何解释来自网络的字节并生成新字节以发送到网络的对象 - 与“传输” - 知道如何从网络中获取字节并将字节放入其中。这种分离简化了测试并允许像这样的新配置,其中传输提供了一些其他网络条件的模拟(可能难以真实生成)。

【讨论】:

我将研究一下这种方法,因为我还需要比移动连接更慢的连接。在这里我不必担心告诉服务器所有数据都已发送。相当优雅的解决方案。谢谢。 :)【参考方案2】:

冒着无法回答您提出的问题的风险,我会寻找在较低级别执行此操作的软件。

Netlimiter 为 Windows 执行此操作。我认为BWMeter 和Bandwidth Controller 也可以。

pyshaper 是一个类似的 Linux 工具。开源。您也许可以将其导入您的 Python 程序。

(要考虑的另一件事是,您可能已经拥有一个能够按照您想要的方式调整流量的路由器。不过,添加到您的软件中是一个相当大的依赖项,而且配置起来可能需要更多工作。)

【讨论】:

我确实考虑过使用能够做到这一点的软件。但是流量在我的机器上是本地的,或者在我的计算机上的虚拟机和计算机本身之间。所以流量永远不会到达我的电脑之外。所以我认为最简单的解决方案是直接在客户端实现。 顺便说一句。感谢 pyshaper 的提示。【参考方案3】:

嗯,网络连接比另一个“慢”的原因是延迟和/或带宽。所以如果你想要一个真实的模拟,你需要找到你的手机连接的带宽,以及它的延迟,并在你的客户端程序中进行模拟。

但您似乎暗示您发送的数据很少,因此带宽可能不会真正影响您的连接速度。所以你可以模拟延迟,这就是你所做的:在每个发送的数据包之间休眠(延迟)。不过 5 秒似乎很多。

但是,如果您认为带宽可能相关,那么模拟实际上非常简单:带宽是每秒可以发送的最大字节数,延迟是到达目的地所需的持续时间。

怎么做:有一个全局时间戳“blockedUntil”,表示您的连接再次空闲以发送数据之前的时间。在程序开始时初始化为 0,因为我们假设它还没有被使用。然后,每次你有一个数据包要发送,如果“_blockedUntil”小于now(),将它设置为now()。然后通过执行以下操作计算写入虚构“线路”所需的时间:packet.size() / bandwidth,这将为您提供持续时间,添加延迟,并将其添加到“blockedUntil”。

现在计算 dt = blockedUntil - now(),将数据包添加到队列中,并在“dt”中添加一个计时器触发,它将弹出队列中的第一个数据包,并发送它。

到此,您已经模拟了带宽和延迟。

编辑:正如有人提到的,还有丢包的问题。您可以通过丢弃数据包的概率来模拟它。 注意:只有在处理来自未连接协议(例如以太网或 UDP)的数据包时才有可能。以 TCP 为例,它不起作用。

【讨论】:

当您在套接字层工作时,您不能真正导致数据包被丢弃。您可以模拟效果,这是一个额外的延迟(超时+重新传输),其概率与丢包的概率相同。 好吧,如果数据包被丢弃,就不要发送它。我就是这个意思。 TCP 不能那样工作。连接是“可靠的”,这意味着如果携带一些 TCP 有效负载的 IP 数据报被丢弃,数据将被重新发送。从应用程序级别来看,使用 TCP 套接字时,您无法判断何时会发生这种情况,也无法模拟它。丢弃一些传递给 socket.send 的字节来模拟丢包是对丢包的错误模拟。 你是 100% 正确的,我被冲昏了头脑,上次我是在操作以太网帧,所以它可以正常工作。我对 OP 的假设相同,但显然不是这样,被冲昏了头脑。我的错。 模拟不需要是现实的,只要结果是与WLAN连接相比,通过移动连接发送数据需要更长的时间。所以将数据分成多个包并在每个包之间休眠是一种可行的方法。只需要在发送所有数据时告诉服务器。看起来很简单。【参考方案4】:

使用poorconn Python 包可以轻松实现慢速连接的模拟:

pip install poorconn

例如,在下面的代码中,函数client_socket()/server_socket()返回一个客户端/服务器套接字对象,该对象每发送1024字节消息延迟大约2秒。

from socket import socket
from poorconn import delay_before_sending, make_socket_patchable

def client_socket():
    s = socket()
    s = make_socket_patchable(s)
    delay_before_sending(s, 2, 1024)
    return s

def server_socket():
    s = socket()
    s = make_socket_patchable(s)
    delay_before_sending_upon_acceptance(s, 2, 1024)
    return s

然后,像普通套接字对象一样使用返回的套接字对象。

免责声明:我是poorconn 的主要作者。欢迎反馈。

【讨论】:

以上是关于在python中模拟慢速网络的简单方法的主要内容,如果未能解决你的问题,请参考以下文章

在 Python 上模拟键盘和鼠标的最简单方法是啥?

通过慢速网络连接在 git 中工作的最快方法是啥?

我如何模拟慢速网络连接[重复]

在 Windows 上使用 jCIFS 列出慢速文件

模拟本地主机上的慢速连接

Web测试中定位bug方法