Я программирую сервер ретрансляции UDP на C ++. Но у меня есть проблема.
У меня есть базовый цикл, который просто вызывает recvfrom()
, проверяет наличие ошибок в пакете, затем считывает «цель» из пакета и использует sendto()
на том же сокете, чтобы отправить пакет целевому клиенту, который также ретранслирует на том же сервере.
Проблема в том, что почти все пакеты теряются, если я не добавлю задержку до sendto()
(эта задержка зависит от скорости соединения, поэтому я не могу установить ее статически).
m_iSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
bind(m_iSocket, (SOCKADDR*)&m_oSockAddress, sizeof(SOCKADDR_IN));
...
while(true) {
recvfrom(m_iSocket, (char*)m_pRecvBuffer, m_nBufferSize, 0, (SOCKADDR*)&remoteAddr, &remoteAddrLen);
...
sendto(m_iSocket, reinterpret_cast<char*>(rw.getData()), rw.getBufferSize(), 0, targets address , address size);
}
Есть идеи?
Есть идеи?
Скорее всего, ваши исходящие UDP-пакеты теряются, потому что где-то переполнен буфер. Наиболее вероятное место для его переполнения — собственный буфер исходящих данных вашего сокета; если это так, вы можете уменьшить количество отброшенных пакетов, позвонив setsockopt(m_iSocket, SOL_SOCKET, SO_SNDBUF, ...)
чтобы увеличить размер исходящего буфера вашего сокета (то есть достаточно большой, чтобы отправка удерживала все пакеты, которые вы передаете через вызовы sendto () за один раз).
Если этого недостаточно для решения проблемы, то следующее, что вы можете сделать, — реализовать собственную буферизацию в приложении; то есть вместо того, чтобы просто вызывать sendto (), помещать пакетные данные в конец структуры данных FIFO, а затем вызывать sendto () только тогда, когда сокет select () готов к записи (и когда это происходит, вытолкните следующий пакет из головы FIFO и вызовите sendto () с этим). Таким образом, вы всегда отправляете данные только с той скоростью, с которой буфер сокета может их принять, вместо того, чтобы предполагать, что буфер сокета всегда будет достаточно большим, чтобы сразу принимать все, что вы на него бросаете.
(Другое место, где буфер может быть переполнен, находится в приемном буфере сокета принимающего приложения, в этом случае вызов setsockopt(s, SOL_SOCKET, SO_RCVBUF, ...)
на сокете принимающего приложения также может помочь)
Целевой процесс не читает достаточно быстро, поэтому датаграммы удаляются, потому что его буфер приема сокета заполнен. Тогда возникает вопрос, что такое правильное смягчение:
Я рекомендую (1), если это возможно, и (3). Не добавляйте задержки в сетевой код. Они не решают проблемы: они только меняют их.
Если у вас есть доступ ко всем соответствующим исходным кодам, вы должны увеличить размер всех соответствующих буферов отправки и получения сокетов.