Как задержать TCP-трафик на сокете?

Как задержать TCP-трафик на сокете?

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

Для этого я надеялся задержать TCP-соединения на разную величину (чтобы мне не пришлось явно переписывать ни клиент, ни сервер).

Я надеялся сделать что-то вроде этого:

socat tcp-listen:$pin system:"delaycat $delay |socat - 'tcp:localhost:$pout'"

где $pin— порт, к которому подключается клиент, $delay— количество секунд задержки, $pout— порт, который слушает сервер. И delaycat— воображаемая программа, которая задерживает входной поток на n секунд.

Есть ли какая-то существующая программа, которая делает то, что я хочу? Или мне написать delaycat?

Редактировать:Обратите внимание, что задержка в масштабах всей системы не была бы идеальным вариантом, поскольку мне бы хотелось задерживать отдельные сокеты на разную величину, если это возможно.

решение1

Я не смог найти ничего подходящего, поэтому написал скрипт на Python, чтобы сделать это (вероятно, он глючит):

#!/usr/bin/env python26
"""Add latency to a tcp connection"""
from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter
from functools import partial

from twisted.internet.reactor import (
    listenTCP, connectTCP, callLater, run
)
from twisted.internet.protocol import (
    ServerFactory, ReconnectingClientFactory, Protocol
)


class ConnectionToServer(Protocol):
    def __init__(self, connection_from_client, delay_to_client):
        self._delay_to_client = delay_to_client
        self._connection_from_client = connection_from_client

    def connectionMade(self):
        self._connection_from_client.connection_to_server = self
        if self._connection_from_client.buffer_from_client:
            self.transport.write(
                self._connection_from_client.buffer_from_client
            )

    def dataReceived(self, data):
        callLater(
            self._delay_to_client,
            self._connection_from_client.transport.write, data
        )

    def connectionLost(self, reason):
        callLater(
            self._delay_to_client,
            self._connection_from_client.transport.loseConnection
        )


class ConnectionFromClient(Protocol):
    def __init__(self, server_host, server_port, delay_to_client, delay_to_server):
        self._delay_to_server = delay_to_server
        self.connection_to_server = None
        self.buffer_from_client = ''
        server_connection_factory = ReconnectingClientFactory()
        server_connection_factory.protocol = partial(
            ConnectionToServer, self, delay_to_client
        )
        self._server_connector = connectTCP(
            server_host, server_port, server_connection_factory
        )

    def dataReceived(self, data):
        callLater(self._delay_to_server, self._write, data)

    def connectionLost(self, reason):
        callLater(
            self._delay_to_server, self._server_connector.disconnect
        )

    def _write(self, data):
        if self.connection_to_server:
            self.connection_to_server.transport.write(data)
        else:
            self.buffer_from_client += data


def main():
    """Add latency to a tcp connection"""
    parser = ArgumentParser(
        description=main.__doc__,
        formatter_class=ArgumentDefaultsHelpFormatter
    )
    parser.add_argument(
        'client_port', type=int, help='client connects to this port'
    )
    parser.add_argument(
        'server_port', type=int, help='server listens on this port'
    )
    parser.add_argument(
        '-t', '--server-host', default='localhost',
        help='server is running on this host'
    )
    parser.add_argument(
        '-c', '--delay-to-client', default=0, type=float,
        help='messages to client are delayed by this many seconds'
    )
    parser.add_argument(
        '-s', '--delay-to-server', default=0, type=float,
        help='messages to server are delayed by this many seconds'
    )
    args = parser.parse_args()
    client_connection_factory = ServerFactory()
    client_connection_factory.protocol = partial(
        ConnectionFromClient, args.server_host, args.server_port,
        args.delay_to_client, args.delay_to_server
    )
    listenTCP(args.client_port, client_connection_factory)
    run()
if __name__ == '__main__':
    main()

решение2

На Linux-машине вы можете использовать tc сНетем.

Например, команда tc qdisc add dev eth0 root netem delay 100msзадержитвсеисходящие пакеты на 100 миллисекунд. Для задержки входящих пакетов можно использоватьifb - Промежуточный функциональный блок

Связанный контент