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

时间:2009-12-02 15:18:56

标签: python networking network-programming

方案。我有一个客户端与服务器的两个网络连接。一个连接使用移动​​电话,另一个连接使用wlan连接。

我解决这个问题的方法是让服务器在两个端口监听。但是,移动连接应该比wlan连接慢。发送的数据通常只是一行文本。我通过允许移动连接以五秒的间隔发送数据解决了较慢的连接问题,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 个答案:

答案 0 :(得分:10)

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

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

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

答案 1 :(得分:4)

如果没有回答你问的问题,我会寻找能够在较低级别执行此操作的软件。

Netlimiter为Windows执行此操作。我认为BWMeterBandwidth Controller也可以做到这一点。

pyshaper是Linux的类似工具。开源。您可能只能将其导入Python程序。

(另一件需要考虑的事情是你可能已经拥有了一台能够按照你想要的方式塑造流量的路由器。但是,添加到你的软件这是一个相当大的依赖性,而且配置可能还需要做更多的工作。) p>

答案 2 :(得分:2)

那么,网络连接“慢”的原因是延迟和/或带宽。因此,如果您想要进行逼真的模拟,则需要找到手机连接的带宽及其延迟,并在客户端程序中模拟。

但您似乎暗示您发送的数据非常少,因此带宽可能不会影响您的连接速度。所以你可以模拟延迟,这就是你做的事情:发送的每个数据包之间的睡眠(延迟)。 5秒虽然看起来很多。

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

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

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

你去了,你已经模拟了带宽和延迟。

编辑:正如有人提到的那样,还有丢包的问题。您可以通过丢弃数据包的概率来模拟它。 注意:当一个人正在处理来自未连接协议(如以太网或UDP)的数据包时,这是唯一可行的。例如,在TCP的情况下,它将无法工作。

答案 3 :(得分:1)

使用 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 的主要作者。欢迎反馈。