при использовании recvmsg я использую MSG_TRUNC
а также MSG_PEEK
вот так:
msgLen = recvmsg(fd, &hdr, MSG_PEEK | MSG_TRUNC)
это дает мне размер буфера, чтобы выделить для следующего сообщения
мой вопрос, как я могу получить размер буфера, который я должен выделить для msg_control
поле внутри заголовка
Боюсь, что вы не можете получить это значение из API-интерфейсов Posix.1g. Не уверен во всех реализациях, но в Linux это невозможно. Как вы можете заметить, поток управления не предоставляется в буферах вспомогательных данных, поэтому вам придется реализовать его самостоятельно, если вы отправляете много информации между процессами. С другой стороны, для обычных случаев вы уже знаете, что собираетесь получить во время компиляции (но вы, вероятно, уже знаете это). Если вам необходимо реализовать собственный поток управления, учтите, что в Linux вспомогательные данные похоже ведет себя как потоковый сокет.
Тем не менее, вы можете получить / установить длину буфера худший случай сценарий в /proc/sys/net/core/optmem_max
, увидеть CMSG (3). Итак, я думаю, вы могли бы установить разумное значение и объявить буфер таким большим.
На основе документ, вам нужно выделить буфер для msg_control
размера msg_controllen
, Чтобы узнать размер заранее, вы можете позвонить, как вы сделали recvmsg(fd, &hdr, MSG_PEEK | MSG_TRUNC)
, MSG_PEEK не удалит сообщение, а MSG_TRUNC позволит вернуть размер сообщения, даже если буфер слишком мал.
несколько решений:
recvmsg(fd, &hdr, MSG_PEEK | MSG_TRUNC)
и инициализировать буфер в hdr на основе возвращенного размера, и вызвать его снова без флагов.Я не могу говорить о других платформах, кроме 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