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

#c #sockets #send #recv #ioctl

#c #сокеты #Отправить #recv #ioctl

Вопрос:

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

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

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

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

Комментарии:

1. UDP, TCP или другой сокет? Кроме того, вы используете ioctl() для получения доступных символов, так ли p2 идентифицирует только 16385 байт?

2. @Jake: Я использую SOCK_STREAM для создания сокета. Хотя p1 отправляет 16388 , ioctl () видит только 16385, вот почему я в замешательстве.

Ответ №1:

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

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

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

Комментарии:

1. Я использую SOCK_STREAM, также я использую ioctl, чтобы заранее знать, сколько байтов доступно для чтения. Проблема сейчас в том, что, поскольку ioctl() идентифицирует меньшее количество, в P2 я не получаю всю строку, отправленную P1. Что вы подразумеваете под «Не ждите, пока другая сторона отправит их все сразу»? У меня есть одна функция recv (), предназначенная для одновременного получения всех данных, отправленных P1> Если я запускаю свою программу с помощью VALGRIND, я не получаю такого поведения. P2 может точно получать все, что отправляет P1, и программа работает правильно.

2. Вы не можете предположить, что возможно получить все данные сразу. Отправителю разрешается отказаться от отправки последней части, пока вы не примете первую часть. Таким образом, вам не может быть разрешено отказаться от принятия первой части, пока он не отправит последнюю часть. Да, иногда это будет работать, иногда нет. Если вы будете следовать правилам, это будет работать каждый раз.

3. Итак, вы предлагаете мне выполнить recv (), пока я не дойду до конца сообщения, в котором есть, скажем, завершение ‘ 0’ ?

4. Да, именно. Вы не можете ждать, пока не будет получено все сообщение. Вы должны принять это по мере его включения, иначе вы замедлите / остановите передатчик.

5. С TCP вы не можете предположить, что количество вызовов чтения / записи будет равно количеству сообщений. Это и есть сокет SOCK_STREAM. Это не было бы ошибкой для чтения и записи, чтобы обрабатывать только 1 байт за раз, просто ужасно медленно. В реальной жизни чтение / запись tcp обычно ограничены несколькими килобайтами.

Ответ №2:

Вероятно, вы достигли предела буфера ядра. Вероятно, вы можете увеличить 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*)amp;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.
 

Комментарии:

1. Мне нужно спросить, сколько байтов доступно, чтобы я мог выполнить malloc() соответственно для получения данных в буфер в recv(). Итак, моя цель состояла в том, чтобы заранее узнать, сколько данных отправляется p1, прочитав буфер с помощью ioctl(), а затем выполнив malloc() для переменной, чтобы получить в нее данные. Однако, поскольку возвращается неправильный размер, у меня есть проблемы в логике моего кода.

2. @ManojKumar — Размер не является неправильным, он просто меньше того, что вы написали. Это ioctl, сколько байтов сразу доступно в буфере tcp, который ограничен. Если вы прочитаете все сообщенные данные, появится больше. Если вам нужно указать весь размер вашего сообщения, запишите длину в отдельный заголовок и заполните буфер несколькими вызовами чтения. Вам следует рассмотреть возможность обработки данных «на лету» без каких-либо mallocs.

3. Но если я разделяю сообщения и длину в двух разных send() из P1, сообщения иногда объединяются вместе. P2 видит их как одно сообщение, и P2 начинает неопределенно ждать следующего recv(). Следовательно, мне пришлось упаковать как длину, так и сообщение в единый объект. Итак, мой ioctl не видит все данные. Как я могу узнать о размере данных, которые я получу в recv (), или как я могу получить все данные в p2 в recv () ?

4. @ManojKumar: Забудьте ioctl . Выделите буфер разумного размера, скажем, 16 КБ. Всегда пытайтесь получить 16 КБ. Затем посмотрите на то, что вы получили, и выясните, что это значит. Вы пытаетесь обучить TCP своему протоколу. Пусть TCP предоставит вам все, что у него есть, и вы реализуете протокол.

5. Что, если размер моих входящих данных станет больше 16 КБ? Я хочу выделить буфер на основе моих входящих данных. Также теперь я могу выполнять несколько вызовов recv(), пока не получу все данные. Но возникла новая проблема: если P1 отправляет данные длиной 20000, P2 получает первые 16384, а при следующем вызове recv() получает оставшееся сообщение и добавляет его к предыдущему. Но в некоторых случаях второй recv() не может получить оставшиеся данные. Так что это теряет часть исходного сообщения.