2010-12-13 3 views
3

Я пишу два приложения (на C), которые совершают несколько вызовов для отправки и получения (например, я реализую копию удаленного файла).Причины медленного вызова recv

Я всегда отправляю 64 байтовый заголовок, который содержит длину следующего тела сообщения и другую информацию.

При тестировании моего приложения на некоторых файлах я узнал, что некоторые вызовы recv занимают довольно много времени (около 40 мс). Используя strace, я узнал, что это происходит сначала при отправке тела сообщения из 377 байтов (что в этом случае копирует весь контент моего файла).

Приложение для сервера начинает отправлять тело сообщения, которое занимает около 48 us. Теперь клиентское приложение потребляет около 38 мс для получения этих байтов.

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

Трассирование сервера

[PID 27158] +1292236124,465827 посыла (6, «\ 0 \ 0 \ 1 \ 271 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0core.fwrite \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 "..., 64, 0) = 64 < 0,000031>

[pid 27158] 1292236124.466074 отправить (6," \ 0 \ 0 \ 0 \ 1 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 10 \ 0 \ 0 \ 0 \ 1 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 1 \ 0 \ 0 \ 0 \ 0" ..., 377, 0) = 377 < 0,000048>

клиента Трассирование

[pid 27159] 1292236124.466364 recv (4, "\ 0 \ 0 \ 1 \ 271 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0core.fwrite \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 "..., 64, 0) = 64 < 0,000027>

[pid 27159] 1292236124.466597 recv (4," \ 0 \ 0 \ 0 \ 1 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 10 \ 0 \ 0 \ 0 \ 1 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 0 \ 1 \ 0 \ 0 \ 0 \ 0" ..., 377, 0) = 377 < 0.037456>

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

Любые советы были бы высоко оценены.

+0

нравится, как говорится, это может быть нагло на работе. однако, если вы копируете файлы, не является пропускной способностью названия игры? Я действительно не понимаю. «С того времени на каждый прием звонки потребляет столько времени, поскольку каждый из них блокирует прием и ждет ответа». сервер ждет клиента? – lijie

ответ

7

Звучит как Nagle's algorithm для меня. Вы не отправляете достаточное количество данных, поэтому это может задержать некоторое время, если будет больше. Вы можете отключить его через опцию сокета и повторите попытку.

+2

Опция 'TCP_NODELAY' и объясняется в tcp (7) [http://linuxmanpages.com/man7/tcp.7.php] – salva

+0

Обратите внимание: для копии файла вы, вероятно, не хотите ее отключать, так как более эффективно позволять стеку TCP решать, как связывать сообщения. – nos

0

Да, это определенно алгоритм Нэгла. Я предлагаю вам прочитать об этом, так как если вы отправляете большие куски данных, которые он должен отправить, как только у него будет «много» данных. Не читая себя, я не совсем уверен, сколько «лотов», но это, вероятно, настраивается.

Однако, если вы делаете один файл для каждого соединения, тогда у вас не должно быть проблем с небольшими файлами, если вы отправляете заголовок и контент за один раз - что вы должны делать в любом случае для повышения пропускной способности. Как говорит Лиджи (более или менее) «пропускная способность - это название игры». Вы должны буферизировать свой заголовок вместе с файловыми данными. Это одна из немногих ситуаций, когда я могу рекомендовать многопоточность - один поток для заполнения буфера из файла, а другой - для загрузки сокета из буфера. Вам необходимо защитить буфер и связанные переменные с помощью мьютекса, и я также рекомендую использовать переменную условия.Поток чтения файла сигнализирует, когда он добавил больше данных и ждет, когда буфер заполнен, и поток записи сокета сообщает, когда он читал и ждет, когда буфер пуст. Поток чтения файла не должен сигнализироваться после записи 64-байтового заголовка. Пусть сначала загружает полный буфер данных.

Вы также можете попробовать иметь два буфера и чередовать их, чтобы уменьшить задержки блокировки мьютекса. Если вы это исправите, поток чтения файла будет записываться в буфер A, в то время как поток записи сокета будет считывать буфер B и наоборот, а потоки будут ожидать на мьютексе реже.

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

+0

оказалось, что алгоритм Нагля на самом деле был проблемой. Спасибо за помощь. – herzrasen

Смежные вопросы