LINUX.ORG.RU

Совместное использование одного UDP порта двумя сокетами

 , ,


0

1

Суть проблемы в следующем. По порту приходит смешанный поток данных: RTP поток и команды от оборудования. В своей программе я создал 2 потока и в обоих потоках ловлю события от порта при помощи select(). Вот настройка первого потока, который должен вылавливать RTP из порта: select() задается вполне стандартно:

select(n, readfds, writefds,exceptfds, p_timeout);

При создании сокета добавляю SO_REUSEADDR:

*sock = socket(af, SOCK_DGRAM, IPPROTO_UDP);
if (*sock == -1)
  return RETURN_ERROR(get_native_netos_error());
  else {
    int32_t val = 1;
    sock_setsockopt(*sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));

2й сокет в другом потоке открывается также, как и первый и тоже биндится без ошибок:

if ((s = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
  return -1;
  }
yes = 1;

if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (uint8 *) &yes, sizeof (yes)) < 0) {
        close (s);
        return -1;
    }

Оба сокета открываются, у обоих положительные дескрипторы, но сообщения приходят только на один сокет почему-то. Хотя я на нем читаю эти сообщения с флагом MSG_PEEK специально, чтобы эти же сообщения оставлять для второго сокета, который для RTP предназначен, но этого почему-то не происходит:

len = recvfrom(serv, &req[0], 128, MSG_PEEK,(struct sockaddr *) &rem_serv, &serv_from_len);
if (bytes_parse(req, len, &my_struct) != P_SUCCESS)
	return P_FAILURE;
len = recvfrom(serv, &req[0], len, 0,(struct sockaddr *) &rem_serv, &serv_from_len);

То есть, 1-м recvfrom читаю сообщение и оставляю его в буфере, если это команда (не RTP), я его читаю еще раз, чтобы удалить из буфера, а если RTP, то просто выхожу из обработчика и его, по идее, должен как раз 2-й поток прочитать, но этого не происходит. Помогите, кто чем может.



Последнее исправление: leonopulos (всего исправлений: 1)

если RTP, то просто выхожу из обработчика и его, по идее, должен как раз 2-й поток прочитать

Боюсь даже спросить, почему ты так решил, и какие строки стандартов гарантируют тебе, что select на втором треде вообще проснется.

Ты придумал какую-то фигню. Переделай все без MSG_PEEK и демультиплексируй данные сам без этого изврата.

t184256 ★★★★★
()

Идея слать на 1 порт параллельно разные потоки данных не очень хорошая.

Чтобы работало как ты хочешь - надо открывать один сокет и шарить его уже открытый дескриптор между всеми кто хочет читать («оставить для другого сокета» нельзя, пакет уже пришёл в какой-то из них, оставить его можно только для другого читателя этого же сокета). Но, поскольку у тебя всё в одном процессе, непонятно почему ты вообще возишься с этими peek а не просто отправляешь прочитанный пакет в нужный обработчик.

firkax ★★★★★
()
Последнее исправление: firkax (всего исправлений: 2)
Ответ на: комментарий от firkax

У меня там процесс один, а потоки разные. И каждый занимается своим делом. А то, что данные смешанные - это разработчик оборудования так сделал. Да, я уже думаю, как сделать. Наверно придется открывать один порт и рассылать пакеты в разные потоки pipe-ми. Но меня вот это описание смущает на SO_REUSEPORT:

SO_REUSEPORT (since Linux 3.9) Permits multiple AF_INET or AF_INET6 sockets to be bound to an identical socket address. This option must be set on each socket (including the first socket) prior to calling bind(2) on the socket. To prevent port hijacking, all of the processes binding to the same address must have the same effective UID. This option can be employed with both TCP and UDP sockets.

For TCP sockets, this option allows accept(2) load distribution in a multi-threaded server to be improved by using a distinct listener socket for each thread. This provides improved load distribution as compared to traditional techniques such using a single accept(2)ing thread that distributes connections, or having multiple threads that compete to accept(2) from the same socket.

For UDP sockets, the use of this option can provide better distribution of incoming datagrams to multiple processes (or threads) as compared to the traditional technique of having multiple processes compete to receive datagrams on the same socket.

Там четко сказано, что именно порт можно шарить. Я добавил SO_REUSEPORT в настройки порта в обоих пакетах, но select() на 2-м порту все равно молчит.

leonopulos
() автор топика
Последнее исправление: leonopulos (всего исправлений: 1)
Ответ на: комментарий от leonopulos

У меня там процесс один, а потоки разные...

А зачем дважды создавать сокет? Почему нельзя использовать один и тот же сокет в обоих потоках?

Может попробовать вместо всяких recvfrom() забиндить локальный/удаленный адреса через bind()/connect()? Изменится ли как-то поведение?

pathfinder ★★★★
()
Ответ на: комментарий от leonopulos

For UDP sockets, the use of this option can provide better distribution of incoming datagrams to multiple processes (or threads) as compared to the traditional technique of having multiple processes compete to receive datagrams on the same socket.

Если ты про это, то либо они сами что-то напутали, либо непонятно так выразились. Например, может быть имелось ввиду что можно забиндить для сокета на один локальный порт, но установить им разные адреса пиров (с помощью connect), тогда в зависимости от исходящего адреса на той стороне пакеты будут приходить в разные сокеты. Но прийти в два сокета одновременно udp не может. А где ты это описание прочитал?

Наверно придется открывать один порт и рассылать пакеты в разные потоки pipe-ми

Ну нафига? У тебя один процесс, общее адресное пространство, зачем городить ipc внутри одного процесса? Просто раскладывай пакеты в разные очереди.

А так то, как уже я тебе писал, и потом ещё раз повторили, можешь даже не рассылать ничего а просто делать свои MSG_PEEK из общего сокета. Сокет никак к треду не привязан. Но у этого способа есть два минуса:

1) если идут например сначала 10 пакетов для первого треда, а потом один для второго, то пока первый тред всё не разберёт - второй свой пакет не увидит

2) лишние recv(MSG_PEEK) без которых можно обойтись если читать пакет сразу до конца и уже самостоятельно класть нужному треду на обработку (не пайпами, это опять лишние сисколлы, а просто записью в fifo-список в памяти, со спинлоком (он юзерспейсный) для защиты от race.

firkax ★★★★★
()
Последнее исправление: firkax (всего исправлений: 2)
Ответ на: комментарий от firkax

Спасибо всем за ответы. Ни с SO_REUSEADDR ни с SO_REUSEPORT ничего не получилось, я вернулся к старому варианту с одним сокетом из него пакеты получаю и далее сортирую.

leonopulos
() автор топика