Задержка в recv()

У меня есть две программы, которые используют программирование сокетов для связи. Сначала я укажу номер. прыжков относительно того, сколько времени они должны обмениваться сообщениями между собой. Каждый раз, когда он получает сообщение, он добавляет к нему свой идентификатор. Следовательно, строка каждый раз увеличивается в размере. Моя программа работает нормально для 8000 прыжков, но после того, как она пересекает 8000, хотя программа p1 отправляет строку длиной 16388, p2 идентифицирует, что в сокете есть только 16385, готовых для чтения. Я использую ioctl() для определения количества символов, готовых к recv() в сокете, а затем получаю их в переменной char *...

Это потому, что есть задержка в send() в p1 и recv() в p2, что p2 идентифицирует только 16385 символов в сокете?

Например: если P1 отправляет длину (16388)

P2 получает только следующую длину (16385)


person Manoj Kumar    schedule 28.10.2011    source источник
comment
UDP, TCP или другой сокет? Кроме того, вы используете ioctl() для получения доступных символов, так ли это, как p2 идентифицирует только 16385 байтов?   -  person Jake    schedule 29.10.2011
comment
@Jake: я использую SOCK_STREAM для создания сокета. Хотя p1 отправляет 16388, ioctl() видит только 16385, поэтому я запутался.   -  person Manoj Kumar    schedule 29.10.2011


Ответы (2)


Скажем, я пытаюсь отправить вам 8 тыкв. Я положил 6 из них на стол. Вы думаете: «Я жду 8 тыкв, а не 6. Подожду, пока он положит на стол последние две». Я думаю: «Я не хочу слишком много тыкв «в полете» сразу. Я подожду, пока он возьмет 2 из этих 6, прежде чем я положу последние 2 на стол». Застряли. Мы ждем друг друга. Мы будем ждать вечно.

Вам не разрешено ждать, пока будут получены дополнительные байты, прежде чем принять уже полученные байты. Причина этого проста: ни один сетевой протокол не может позволить каждой стороне ждать другую. Поскольку TCP позволяет отправляющей стороне ожидать в этом контексте, он не может позволить также ждать принимающей стороне.

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

person David Schwartz    schedule 28.10.2011
comment
Я использую SOCK_STREAM, также я использую ioctl, чтобы заранее узнать, сколько байтов доступно для чтения. Теперь проблема в том, что, поскольку ioctl() идентифицирует меньшее количество , в P2 я не получаю всю строку, отправленную P1. Что вы имеете в виду под «Не ждать, пока другая сторона отправит их все сразу»? У меня есть одна функция recv(), предназначенная для одновременного получения всех данных, отправленных P1. Если я запускаю свою программу с VALGRIND, я не получаю такого поведения. P2 может принимать все, что отправляет P1, и программа работает корректно. - person Manoj Kumar; 29.10.2011
comment
Нельзя предполагать, что можно получить все данные сразу. Отправителю разрешается отказаться от отправки последней части, пока вы не примете первую часть. Таким образом, вам не может быть позволено отказаться принять первую часть, пока он не пришлет последнюю часть. Да, иногда это будет работать, иногда нет. Если вы будете следовать правилам, это будет работать каждый раз. - person David Schwartz; 29.10.2011
comment
Итак, вы предлагаете мне выполнять recv() до тех пор, пока я не дойду до конца сообщения, в котором есть завершение «\ 0»? - person Manoj Kumar; 29.10.2011
comment
Да, точно. Вы не можете ждать, пока все сообщение будет получено. Вы должны принять его, как только он появится, иначе вы замедлите/остановите передатчик. - person David Schwartz; 29.10.2011
comment
С TCP вы не можете предположить, что количество вызовов чтения/записи будет равно количеству сообщений. Это и есть сокет SOCK_STREAM. Это не было бы ошибкой для чтения и записи, чтобы обрабатывать только 1 байт за раз, просто ужасно медленно. В реальной жизни чтение/запись tcp обычно ограничивается несколькими килобайтами. - person cdleonard; 29.10.2011

Вы, вероятно, достигли предела буфера ядра. Вы, вероятно, можете увеличить SO_RCVBUF на приемнике, и он будет работать так, как вы ожидаете: SIOCINQ в конечном итоге вернет полный размер непрочитанных данных.

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

Вы должны реструктурировать код, чтобы вам никогда не приходилось спрашивать ядро, сколько байтов доступно. Просто прочитайте до разумного предела (например, 4096) и обработайте одно сообщение уровня приложения, разбитое на несколько частей. Если вам нужны длины/границы сообщений, вы ДОЛЖНЫ реализовать их самостоятельно поверх TCP.

Вот какой-то глупый код для чтения сообщения с длинным заголовком:

int ret, len = 0, have_read;
have_read = 0;
while (have_read < sizeof(len)) {
    // This will likely always return sizeof(len) the first time.
    ret = read(fd, ((char*)&len) + have_read, sizeof(len) - have_read);
    if (ret <= 0) {
        // Handle error.
    }
    have_read += ret;
}
char* buf = malloc(len);
if (!buf) {
    // Handle error.
}
have_read = 0;
while (have_read < len) {
    ret = read(fd, buf + have_read, len - have_read);
    if (ret <= 0) {
        // Handle error.
    }
    have_read += ret;
}
// Handle message in buf.
person cdleonard    schedule 28.10.2011
comment
Мне нужно спросить, сколько байтов доступно, чтобы я мог выполнить malloc() соответственно для получения данных в буфер в recv(). Итак, моя цель состояла в том, чтобы заранее узнать, сколько данных отправляется p1, читая буфер с помощью ioctl(), а затем выполняя malloc() для переменной, чтобы получить в нее данные. Однако, поскольку возвращается неправильный размер, у меня есть проблемы с логикой моего кода. - person Manoj Kumar; 29.10.2011
comment
@ManojKumar - Размер правильный, он просто меньше того, что вы написали. Это ioctl сколько байт сразу доступно в tcp буфере, который ограничен. Если вы прочитаете все представленные данные, появится больше. Если вам нужно выделить весь размер вашего сообщения, напишите длину в отдельном заголовке и заполните буфер несколькими вызовами чтения. Вы должны рассмотреть возможность обработки данных на лету без каких-либо mallocs. - person cdleonard; 29.10.2011
comment
Но если я разделяю сообщения и длину в двух разных send() из P1, сообщения иногда объединяются. P2 видит их как одно сообщение, и P2 начинает бесконечно ждать в следующем recv(). Следовательно, мне пришлось упаковать длину и сообщение в один объект. Итак, мой ioctl не видит всех данных. Как я могу узнать размер данных, которые я получу в recv(), или как я могу получить все данные в p2 в recv()? - person Manoj Kumar; 29.10.2011
comment
@ManojKumar: Забудьте о ioctl. Выделите буфер разумного размера, скажем, 16 КБ. Всегда старайтесь получить 16 КБ. Затем посмотрите, что у вас получилось, и выясните, что это значит. Вы пытаетесь научить TCP своему протоколу. Позвольте TCP дать вам все, что у него есть, и вы реализуете протокол. - person David Schwartz; 29.10.2011
comment
Что делать, если размер входящих данных превышает 16 КБ? Я хочу выделить буфер на основе моих входящих данных. Также теперь я могу делать несколько вызовов recv(), пока не получу все данные. Но возникла новая проблема: если P1 отправляет данные длиной 20000, P2 получает первые 16384, а при следующем вызове recv() получает оставшееся сообщение и добавляет его к предыдущему. Но в некоторых случаях второй recv() не может получить оставшиеся данные. Так что он теряет часть исходного сообщения. - person Manoj Kumar; 29.10.2011
comment
Также ioctl() запускается до заполнения буфера оставшимися данными, скажем, в случае 20000. Первоначально заполняется 16384, и до заполнения оставшихся данных в буфере выполняется ioctl и возвращается 0. - person Manoj Kumar; 29.10.2011