Я разрабатываю инструмент для проведения нагрузочного тестирования на сервере UDP (с использованием C # /. NET 4.0, работающего на NT 6.x , хотя это менее актуально).Сервер общается с десятками тысяч клиентов, где связь между каждым клиентом очень низкая и редко встречается.Все сообщения выполняются по схеме «запрос-ответ», когда одна из сторон инициирует связь с другой стороной, которая затем отвечает.Когда серверу необходимо что-то отправить клиенту, он ищет последнюю известную конечную точку (IP + порт) клиента и отправляет пакет UDP, а также ожидает ответа на один известный порт, который используется для получения сообщений от всехклиентов.Когда клиент инициирует связь, он уже знает конечную точку сервера и просто отправляет пакет с временного порта и ожидает ответа на тот же порт.Один и тот же эфемерный порт используется для жизни клиента.
Конструкция инструмента нагрузочного тестирования довольно проста;подражать поведению, состоянию и принятию решений каждым клиентом, до низкой, но достаточной сложности.Поскольку связь с каждым клиентом носит случайный характер (каждые несколько секунд), а объем обработки, требуемый для каждого сеанса связи, минимален, наилучший подход, который я могу придумать, - это использовать один поток с одним сокетом для выполнения всего сеанса связи.для большого количества смоделированных клиентов, которые, скорее всего, все еще не будут поддерживать поток полностью занятым и насыщенным сокетом.К сожалению, с этим подходом я столкнулся с двумя проблемами, возникающими из-за того, что каждый клиент отправляет и получает данные со своего собственного порта:
- Сокет будет разрешать отправку UDP-пакета только с эфемерного, выделенного системойпорт или определенный порт, к которому привязан сокет.
- Сокет будет принимать пакеты UDP только от порта, к которому он привязан.
Один сокет на клиента
Эти два ограничения, по-видимому, означают, что я должен создать сокет для каждого клиента, поскольку UDP-пакет должен исходить из определенного порта, и на этот порт будет отправлен ответ.Итак, первое возможное решение - сделать это, создать сокет для симулируемого клиента.Допустим, мы моделируем 30 000 клиентов на одной машине:
- Возможно ли даже создание 30 000 сокетов?Это лучшая практика?Это исполнитель?Позволит ли Windows даже связать 30 000 сокетов с 30 000 различных портов?
- Как проверить через 30 000 клиентских сокетов, отправлены ли какие-либо данные сервером?Периодически ли я опрашиваю все сокеты, чтобы узнать, были ли получены какие-либо данные?Есть ли способ подождать на всех 30 000 сокетов и получить первый пакет, который поступит на любой из них?
- Какие ресурсы выделяет операционная система для каждого сокета?Каковы ограничения каждого из этих ресурсов и каковы последствия их достижения?
Один сокет для всех клиентов
Другой подход заключается в использовании одного сокета,но две проблемы, о которых я упоминал ранее, сначала должны быть решены каким-либо образом:
- Первая проблема, связанная с отправкой UDP-пакета из порта, отличного от порта Socket, разрешима.Он включает в себя создание необработанного сокета и создание UDP-заголовка самостоятельно, что означает, что вы можете указать любой порт источника и назначения, какой захотите.Единственная сложность заключается в вычислении необязательной, но важной контрольной суммы UDP , для которой требуются не только заголовок и полезная нагрузка UDP, но также IP-адрес источника и назначения, причем первый является проблематичным, поскольку для его вызова требуется API-интерфейс Win32.( GetBestInterface и GetAdaptersInfo ), который включает в себя несколько собственных структур и большое количество неуправляемых распределений памяти, потенциальная ошибка надежности с точки зрения .NET, но это может быть сделано.
- Вторая проблема, связанная с использованием одного сокета для приема пакетов UDP из списка (или диапазона) портов, остается нерешенной мной.Даже с необработанным сокетом операционная система требует, чтобы я связал сокет с конкретным портом, прежде чем разрешить мне выполнять операции приема.Есть способ сделать это?Всегда есть методы перехвата пакетов, но я бы предпочел их избегать, если это не может быть сделано из управляемого кода надежным и несколько простым способом (в этом случае я открыт для предложений).
Другие подходы?
Есть ли другой подход, о котором я не думал?Я не на том пути?Можете ли вы придумать лучшее решение?Я хотел бы услышать любые ваши предложения.