Эффект SO_SNDBUF

#c #linux #sockets #setsockopt

#c #linux #сокеты #setsockopt

Вопрос:

Я не могу понять, как и почему работают следующие сегменты кода :

     /* Now lets try to set the send buffer size to 5000 bytes */
    size = 5000;
    err = setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF,  amp;size, sizeof(int));
    if (err != 0) {
        printf("Unable to set send buffer size, continuing with default sizen");
    }
  

Если мы проверим значение буфера отправки, оно действительно правильно установлено в 5000 * 2 = 10000.
Однако, если мы попытаемся отправить больше, чем размер буфера отправки, он отправит все это. Например:

     n = send(sockfd, buf, 30000, 0);

    /* Lets check how much us actually sent */
    printf("No. of bytes sent is %dn", n);
  

Выводится значение 30000.

Как именно это сработало? Не повлиял ли тот факт, что размер буфера отправки был ограничен 10000? Если это произошло, что именно произошло? Какая-то фрагментация?

ОБНОВЛЕНИЕ: Что произойдет, если сокет находится в неблокирующем режиме? Я попробовал следующее:

  1. Изменение размера буфера на 10000 (5000 * 2) приводит к отправке 16384 байт
  2. Изменение размера буфера на 20000 (10000 * 2) приводит к отправке 30000 байт

Еще раз, почему?

Ответ №1:

Эффект от установки SO_SNDBUF параметра отличается для TCP и UDP.

  • Для UDP это устанавливает ограничение на размер дейтаграммы, т.е. все, что больше, будет отброшено.
  • Для TCP это просто устанавливает размер буфера в ядре для данного сокета (с некоторым округлением до границы страницы и с верхним пределом).

Поскольку похоже, что вы говорите о TCP, эффект, который вы наблюдаете, объясняется тем, что сокет находится в режиме блокировки, поэтому send(2) блокируется до тех пор, пока ядро не сможет принять все ваши данные, и / или сетевой стек асинхронно выводит данные из очереди и отправляет их на сетевую карту, освобождая таким образом место в буфере.

Кроме того, TCP является потоковым протоколом, он не сохраняет никакой структуры «сообщения». Один send(2) может соответствовать нескольким recv(2) s на другой стороне, и наоборот. Обрабатывайте это как поток байтов.

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

1. и что произойдет, если сокет находится в неблокирующем режиме? Я попробовал следующее: 1) Изменение размера буфера на 10000 приводит к отправке 16384 байт 2) Изменение размера буфера на 20000 приводит к повторной отправке 30000 байт, почему?

2. @Arun: это просто округление.. почему это вообще имеет значение? вы не должны полагаться на тот факт, что буфер состоит из ровно n байт. ядро может выделить больше.

3. Во-первых, ядро не принимает размер вашего буфера как есть, как указывает @yi_H. Тогда сетевой стек работает асинхронно по отношению к вашему процессу. Попробуйте следующее — начните запись в сокет, но не читайте в другом размере . В конечном итоге вы получите короткое сообщение (ядро принимает меньше байтов, чем вы ему даете) или сбой записи.

4. Ну, у меня ничего не происходит в TCP-сокете. Я создал клиент-серверное приложение, в котором сервер после принятия соединения переходит в режим ожидания на неопределенное время, а клиент продолжает отправлять сообщения. Буфер никогда не переполняется, ни мой вызов отправки не блокируется, как сказано linux.die.net/man/2/send

Ответ №2:

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