Каков хороший размер буфера для программирования сокетов?
Мы используем.Net и сокеты. Сервер использует метод Socket.Sender(bytes[]), поэтому он просто отправляет всю полезную нагрузку. С другой стороны, мы являемся клиентами, потребляющими данные. Socket.Receive(буфер []). Во всех примерах от Microsoft (и других) они, похоже, придерживаются размера буфера 8192. Мы использовали этот размер, но время от времени мы отправляем данные клиентам, которые превышают этот размер буфера.
Есть ли способ определить, сколько данных отправил нам метод, отправленный сервером? Каков наилучший размер буфера?
5 ответов
Даже если вы отправляете больше данных, чем это, они могут быть недоступны в одном вызове для получения.
Вы не можете определить, сколько данных отправил сервер - это поток данных, и вы просто читаете порции за раз. Вы можете прочитать часть того, что сервер отправил за один звонок, или вы можете прочитать данные двух посылок за один звонок. 8 Кбайт - это разумный размер буфера - не такой большой, чтобы тратить много памяти, и не такой маленький, чтобы вам приходилось использовать множество потерянных вызовов-получателей. 4К или 16К тоже вполне подойдут... Лично я бы не стал превышать 16К для сетевых буферов - подозреваю, что вы их редко заполняете.
Вы можете поэкспериментировать, пытаясь использовать очень большой буфер и регистрировать, сколько байтов было получено при каждом вызове - это даст вам некоторое представление о том, какой объем обычно доступен, - но на самом деле это не покажет эффект использования меньшего буфера. Какие у вас проблемы с использованием буфера 8K? Если это производительность, есть ли у вас доказательства того, что этот аспект вашего кода является узким местом производительности?
К сожалению, ответ Джона Скита оставляет большую часть картины - размер буфера отправки и продукт задержки полосы пропускания канала, по которому вы пишете.
Если вы пытаетесь отправить данные по большому каналу, используя один сокет, и вы хотите, чтобы TCP заполнил этот канал, вам нужно использовать размер буфера отправки, который эквивалентен произведению канала с задержкой пропускной способности. В противном случае TCP не будет заполнять канал, потому что он не будет всегда оставлять достаточное количество "байтов в полете".
Рассмотрим соединение, которое имеет скорость 1 гигабит и имеет одностороннюю задержку в среднем 10 миллисекунд. Время прохождения туда-обратно (иначе говоря, количество времени, которое проходит между вашим сокетом, отправляющим пакет, и временем, которое он получает подтверждение для этого пакета и, таким образом, знает, что отправляет больше данных), обычно вдвое больше времени ожидания.
Таким образом, если у вас 1 гигабитное соединение и RTT 20 миллисекунд, то этот канал будет иметь 1 гигабит / с * 20 миллисекунд == 2,5 мегабайта данных в полете за все время, если он полностью используется.
Если ваш буфер отправки TCP меньше 2,5 мегабайт, то этот один сокет никогда не будет полностью использовать канал - вы никогда не получите гигабит / сек производительности из вашего сокета.
Если ваше приложение использует много сокетов, то суммарный размер всех буферов отправки TCP должен составлять 2,5 МБ, чтобы полностью использовать этот гипотетический канал RTT 1 гигабит / 20 мс. Например, если вы используете 8192-байтовые буферы, вам нужно 306 одновременных сокетов TCP для заполнения этого канала.
Это зависит от вашего протокола. Если вы ожидаете, что сообщения превысят 8192 байта, вам следует соответственно увеличить размер буфера. Но имейте в виду, что этот размер буфера только для одного вызова Receive
, Если вы действительно хотите / должны, вы можете перебрать Receive
несколько раз и скопируйте полученные данные в произвольно большую структуру данных или буфер.
Также имейте в виду, что это хорошая практика, чтобы позвонить Receive
несколько раз, пока вы не убедитесь, что прочитали все данные для данного сообщения; даже если одно сообщение меньше размера буфера, оно не может быть получено одним Receive
вызов.
На самом деле это не связано с Microsoft, но я просто экспериментирую с многопоточным эхо-сервером C++, использующим порт TCP (не сокет домена Unix), чтобы увидеть пропускную способность. Синхронизация входа 4M с различными размерами буфера дала следующие результаты:
1024 - real 0m0,102s; user 0m0,018s; sys 0m0,009s
2048 - real 0m0,112s; user 0m0,017s; sys 0m0,009s
8192 - real 0m0,163s; user 0m0,017s; sys 0m0,007s
256 - real 0m0,101s; user 0m0,019s; sys 0m0,008s
16 - real 0m0,144s; user 0m0,016s; sys 0m0,010s
Кажется, что чтение в 1024-байтовых чанках уменьшает издержки TCP, в то время как время обработки (просто повторяя входные данные) не зависело от размера буфера. 8192 байта кажутся высокими, и действительно низкие значения (например, 16) тоже не годятся.
8192 было бы идеально. Если у вас есть данные, которые превышают этот размер, вам будет лучше отправлять данные в пакетах постоянной длины.
Размер данных, отправляемых сервером, можно проверить с помощью функции recv в WINSOCK, которая имеет параметр, определяющий длину буфера.