Это будет трудно получить полностью правильно только на стороне слушателя, поскольку он может фактически пропустить последний пакет в чипе сетевого интерфейса, что помешает вашей программе когда-либо иметь шанс увидеть его.
UDP-код операционной системы был бы лучшим местом, чтобы попытаться справиться с этим, поскольку он будет получать новые пакеты, даже если он решит их отбросить, поскольку у него уже слишком много очередей. Тогда он мог бы принять решение об отказе от старого или отбрасывании нового, но я не знаю, как сказать ему, что это именно то, что вы хотели бы сделать.
Вы можете попытаться справиться с этим на приемнике, имея одну программу или поток, который всегда пытается прочитать в самом новом пакете, и другой, который всегда пытается получить этот самый новый пакет. Как это сделать, будет зависеть от того, сделаете ли вы это как две отдельные программы или как два потока.
В качестве потоков вам понадобится мьютекс (семафор или что-то в этом роде) для защиты указателя (или ссылки) на структуру, используемую для хранения 1 полезной нагрузки UDP и всего, что вам нужно (размер, IP-адрес отправителя, порт отправителя, отметка времени и т. д.).
Поток, который фактически читает пакеты из сокета, будет хранить данные пакета в структуре, получит мьютекс, защищающий этот указатель, заменит текущий указатель на указатель на только что созданную структуру, освободит мьютекс, сообщит процессору Поток, которому нужно что-то сделать, а затем очистить структуру, на которую он только что получил указатель, и использовать ее для хранения следующего входящего пакета.
Поток, который фактически обрабатывал полезную нагрузку пакета, должен ожидать сигнала от другого потока и / или периодически (возможно, для этого вам понадобится 500 мс или около того, и вы решите) и получить мьютекс, поменять местами его указатель на структура полезной нагрузки UDP с той, которая существует, освобождает мьютекс, и затем, если структура имеет какие-либо пакетные данные, она должна обработать ее и затем ждать следующего сигнала. Если у него не было никаких данных, он должен просто идти вперед и ждать следующего сигнала.
Поток процессора, вероятно, должен работать с более низким приоритетом, чем слушатель UDP, так что слушатель с меньшей вероятностью когда-либо пропустит пакет. При обработке последнего пакета (того, который вас действительно волнует) процессор не будет прерван, потому что нет новых пакетов, которые слушатель мог бы услышать.
Вы можете расширить это, используя очередь, а не просто один указатель в качестве места обмена для двух потоков. Единственный указатель - это просто очередь длиной 1, которую очень легко обрабатывать.
Вы также можете расширить это, попытавшись заставить поток слушателя определить, есть ли несколько ожидающих пакетов, и только фактически поместить последние из них в очередь для потока процессора. То, как вы это сделаете, будет зависеть от платформы, но если вы используете * nix, это должно вернуть 0 для сокетов, ничего не ожидая:
while (keep_doing_this()) {
ssize_t len = read(udp_socket_fd, my_udp_packet->buf, my_udp_packet->buf_len);
// this could have been recv or recvfrom
if (len < 0) {
error();
}
int sz;
int rc = ioctl(udp_socket_fd, FIONREAD, &sz);
if (rc < 0) {
error();
}
if (!sz) {
// There aren't any more packets ready, so queue up the one we got
my_udp_packet->current_len = len;
my_udp_packet = swap_udp_packet(my_ucp_packet);
/* swap_udp_packet is code you would have to write to implement what I talked
about above. */
tgkill(this_group, procesor_thread_tid, SIGUSR1);
} else if (sz > my_udp_packet->buf_len) {
/* You could resize the buffer for the packet payload here if it is too small.*/
}
}
udp_packet должен быть выделен для каждого потока, а также 1 для указателя замены. Если вы используете очередь для обмена, то у вас должно быть достаточно udp_packets для каждой позиции в очереди - поскольку указатель - это просто очередь длиной 1, для которой нужно только 1.
Если вы используете систему POSIX, подумайте о том, чтобы не использовать сигнал реального времени для сигнализации, потому что они стоят в очереди. Использование обычного сигнала позволит вам обрабатывать сигналы, передаваемые много раз, так же, как сигналы только один раз, пока сигнал не будет обработан, а сигналы в режиме реального времени выстраиваются в очередь. Периодическое пробуждение для проверки очереди также позволяет обрабатывать возможность получения последнего сигнала сразу после того, как вы проверили, есть ли у вас какие-либо новые пакеты, но перед вызовом pause
для ожидания сигнала.