Как получить размер буфера msg_control для recvmsg?

при использовании recvmsg я использую MSG_TRUNC а также MSG_PEEK вот так:

msgLen = recvmsg(fd, &hdr, MSG_PEEK | MSG_TRUNC)

это дает мне размер буфера, чтобы выделить для следующего сообщения

мой вопрос, как я могу получить размер буфера, который я должен выделить для msg_control поле внутри заголовка

7

Решение

Боюсь, что вы не можете получить это значение из API-интерфейсов Posix.1g. Не уверен во всех реализациях, но в Linux это невозможно. Как вы можете заметить, поток управления не предоставляется в буферах вспомогательных данных, поэтому вам придется реализовать его самостоятельно, если вы отправляете много информации между процессами. С другой стороны, для обычных случаев вы уже знаете, что собираетесь получить во время компиляции (но вы, вероятно, уже знаете это). Если вам необходимо реализовать собственный поток управления, учтите, что в Linux вспомогательные данные похоже ведет себя как потоковый сокет.

Тем не менее, вы можете получить / установить длину буфера худший случай сценарий в /proc/sys/net/core/optmem_max, увидеть CMSG (3). Итак, я думаю, вы могли бы установить разумное значение и объявить буфер таким большим.

1

Другие решения

На основе документ, вам нужно выделить буфер для msg_control размера msg_controllen, Чтобы узнать размер заранее, вы можете позвонить, как вы сделали recvmsg(fd, &hdr, MSG_PEEK | MSG_TRUNC), MSG_PEEK не удалит сообщение, а MSG_TRUNC позволит вернуть размер сообщения, даже если буфер слишком мал.

несколько решений:

  • вызов recvmsg(fd, &hdr, MSG_PEEK | MSG_TRUNC) и инициализировать буфер в hdr на основе возвращенного размера, и вызвать его снова без флагов.
  • Выделите достаточно большой буфер, если вы заранее знаете размер ваших сообщений, и вызовите recvmsg. Если возникает ошибка (возвращается -1), проверьте код ошибки, если сообщение было усечено (MSG_TRUNC или MSG_CTRUNC)
3

Я не могу говорить о других платформах, кроме macOS (ядро которого основано на ядре FreeBSD, поэтому, возможно, оно не отличается и в BSD-системах), и стандарт POSIX также не полезен, так как он оставляет практически все детали, которые должны быть определены протокол, но по умолчанию поведение recvmsg на macOS для сокета UDP вообще не нужно передавать никаких управляющих данных. Неважно, какой размер вы установите msg_control на входе, это всегда будет 0 на выходе. Если вы хотите получить какие-либо управляющие данные, вы должны сначала явно включить их для сокета.

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

int yes = 1;
setsockopt(soc, IPPROTO_IP, IP_RECVDSTADDR, &yes, sizeof(yes));

И теперь вы получите адрес назначения для сокетов IPv4, задокументированный как

Поле msg_control в структуре msghdr указывает на буфер, который
содержит структуру cmsghdr, за которой следует IP-адрес. Cmsghdr
поля имеют следующие значения:

cmsg_len = sizeof(struct in_addr)
cmsg_level = IPPROTO_IP
cmsg_type = IP_RECVDSTADDR

Это означает, что вам нужно предоставить как минимум 16 байт для хранения в моей системе, так как struct cmsghdr в одной системе всегда 12 байт (четыре раза по 32 бита), а IPv4-адрес — это еще 4 байта, то есть 16 байтов вместе. Это значение должно быть правильно округлено, используя CMSG_SPACE макрос, но в моей системе макрос только гарантирует, что он кратен 32-битному, а 16-байтовый уже является таким кратным, поэтому CMSG_SPACE(16) возвращается 16 для меня.

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

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

Если ваш буфер управляющих данных был слишком мал, MSG_CTRUNC флаг устанавливается на выходе всегда, даже если вы не установили никаких флагов на входе, тогда вам нужно увеличить размер буфера управляющих данных и повторить попытку (со следующим пакетом или с тем же пакетом, если вы использовали MSG_PEEK в качестве флага ввода), пока вы однажды не смогли сделать этот вызов, не получив MSG_CTRUNC флаг на выходе. Наконец, посмотрите на то, что msg_control поле говорит. На входе это объем доступного буферного пространства, но на выходе он содержит точный объем буферного пространства, который был фактически использован. Это точный размер буфера, который вам нужен для получения управляющих данных всех будущих пакетов этого сокета, если только вы не измените параметры, которые приведут к отправке большего / меньшего количества управляющих данных, а затем вам просто придется снова определить этот размер так же, как до.

Для более полного примера вы также можете взглянуть на:
https://stackoverflow.com/a/49308499/15809

1
По вопросам рекламы [email protected]