Простой способ симулировать медленную сеть в python - PullRequest
6 голосов
/ 02 декабря 2009

Сценарий. У меня есть клиент с двумя сетевыми подключениями к серверу. Одно соединение использует мобильный телефон, а другое использует беспроводное соединение.

Способ, который я решил, заключается в том, чтобы сервер прослушивал два порта. Но мобильное соединение должно быть медленнее, чем беспроводное соединение. Данные, которые отправляются, обычно представляют собой просто строки текста. Я решил проблему с более медленным соединением, разрешив мобильному соединению отправлять данные с интервалом в пять секунд, а беспроводное соединение - с интервалом в одну секунду.

Но есть ли лучший способ замедления? Возможно, отправив меньшие пакеты, что означает, что мне нужно отправить больше данных?

Есть идеи о том, как замедлить одно из соединений?

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

Ответы [ 3 ]

10 голосов
/ 02 декабря 2009

Помимо использования внешнего инструмента для симуляции интересующей вас сети, одним хорошим подходом является использование альтернативной реализации сокета.

Это включает в себя создание конструкции сокета в качестве параметра вашей функции вместо импорта модуля сокета и его непосредственного использования. Для нормальной работы вы передадите реальный тип сокета, но когда вы хотите протестировать различные неблагоприятные условия сети, вы можете передать реализацию, которая имитирует эти условия. Например, вы можете создать тип сокета, который параметризует задержку и пропускную способность (непроверенный код, будьте осторожны):

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 явно разделяет идею «протоколов» - объектов, которые знают, как интерпретировать байты из сети, и генерируют новые байты для отправки в сеть, - от «транспортов» - объектов, которые знают, как получить байты от сети и положить байты на него. Разделение облегчает тестирование и позволяет использовать новые конфигурации, подобные этой, где транспорт обеспечивает имитацию некоторых других условий сети (которые могут быть трудны для реального производства).

4 голосов
/ 02 декабря 2009

С риском не ответить на заданный вами вопрос, я бы искал программное обеспечение, которое делает это на более низком уровне.

Netlimiter делает это для Windows. Я думаю BWMeter и Контроллер пропускной способности может сделать это тоже.

pyshaper - аналогичный инструмент для Linux. Открытый исходный код. Вы можете просто импортировать его в свою программу на Python.

(Еще одна вещь, которую следует учитывать, это то, что у вас уже может быть маршрутизатор, способный формировать трафик так, как вы хотите. Однако это довольно большая зависимость для добавления в ваше программное обеспечение, и его настройка может потребовать больше усилий.) *

2 голосов
/ 02 декабря 2009

Что делает сетевое соединение «более медленным», чем другое, связано с задержкой и / или пропускной способностью. Поэтому, если вы хотите иметь реалистичное моделирование, вам нужно найти пропускную способность вашего мобильного телефона, а также его задержку и смоделировать это в вашей клиентской программе.

Но вы, похоже, подразумеваете, что отправляете очень мало данных, поэтому пропускная способность, вероятно, не повлияет на скорость вашего соединения. Таким образом, вы можете просто имитировать задержку, и это делает то, что вы делаете: сон (задержка) между каждым отправленным пакетом. 5 секунд, похоже, много.

Но если вы думаете, что пропускная способность может быть релевантной, на самом деле очень просто смоделировать: пропускная способность - это максимальное количество байтов в секунду, которое вы можете отправить, а задержка - это длительность, необходимая для достижения цели.

Как это сделать: иметь глобальную временную метку «заблокирован», представляющую время, когда ваше соединение снова становится свободным для отправки данных. Инициализируйте значение 0 в начале вашей программы, поскольку мы предполагаем, что она еще не используется. Затем, каждый раз, когда у вас есть пакет для отправки, если «_blockedUntil» меньше чем now (), установите его в now (). Затем подсчитайте время, которое потребуется для записи в вымышленный «провод», выполнив: packet.size () / bandwidth, который даст вам продолжительность, добавит задержку и добавит ее в «blockUntil».

Теперь вычислите dt = blockUntil - now (), добавьте пакет в очередь и добавьте таймер, запускающий в «dt», который вставит первый пакет в очередь и отправит его.

Итак, вы смоделировали пропускную способность и задержку.

Редактировать: как кто-то упомянул, есть и вопрос об отброшенных пакетах. Вы можете смоделировать это, имея вероятность сброса пакетов. Примечание: Это возможно только при манипулировании пакетами из неподключенного протокола, такого как Ethernet или UDP. Например, в случае TCP он не будет работать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...