Форум программистов, компьютерный форум, киберфорум
Наши страницы
С++ для начинающих
Войти
Регистрация
Восстановить пароль
 
Leardjiny
2 / 2 / 0
Регистрация: 22.09.2013
Сообщений: 183
1

Одновременный прием по двум портам

13.02.2018, 12:24. Просмотров 256. Ответов 15
Метки нет (Все метки)

День добрый возник вопрос - можно ли сделать одновременный прием по двум портам в одном потоке? (по всем сразу не подходит, т.к. в другом потоке хочу использовать другие порты)
С одним портом все просто, сделал так:
Функция создания сокета:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
{       
        memset(&m_socket, 0, sizeof(m_socket));
        m_socket = SOCKET_ERROR;
        try
        {
            //create socket
            if ( (m_socket=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == SOCKET_ERROR )
            {               
                return false;
            }
            
            DWORD timeout = 4000;
            if ( setsockopt(m_socket, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout)) == SOCKET_ERROR )
            {               
                return false;
            }
 
            int buffsize = kMaxPacketSize;
            if ( setsockopt(m_socket, SOL_SOCKET, SO_RCVBUF, reinterpret_cast<const char*>(&buffsize), sizeof(buffsize)) == SOCKET_ERROR )
            {               
                return false;
            }
 
            //configure listen address
            sockaddr_in listen_addr;
            memset((char *) &listen_addr, 0, sizeof(listen_addr));
            listen_addr.sin_family = AF_INET;
            listen_addr.sin_port = htons((u_short)UDP_PORT);//1 порт
            listen_addr.sin_addr.S_un.S_addr = INADDR_ANY;
 
            //we must bind incoming message port for recv() (or call sendto() first)
            if( bind(m_socket ,(struct sockaddr *)&listen_addr, sizeof(listen_addr)) == SOCKET_ERROR )
            {
                int res = WSAGetLastError ();               
                return false;
            }                           
        }
        catch (std::wstring s)
        {           
            return false;
        }
        catch(...)
        {           
            return false;
        }
 
        sockaddr_in sender_addr;
        int sender_addr_size = sizeof(sender_addr);
        sender_addr.sin_addr.S_un.S_addr = inet_addr(FPGA_IP_ADDR);
 
 
        int bytes_recv = recvfrom(m_socket, m_recv_buffer.data(), kMaxPacketSize, 0, (struct sockaddr *) &sender_addr, &sender_addr_size);  
 
        if ( bytes_recv == SOCKET_ERROR )
        {
            int error_code = WSAGetLastError();
            Stop();
            return false;
        }       
 
    }

Функция приема данных:
C++
1
2
3
4
5
6
7
8
9
10
    sockaddr_in sender_addr;
    int sender_addr_size = sizeof(sender_addr);
    sender_addr.sin_addr.S_un.S_addr = inet_addr(FPGA_IP_ADDR);
    int bytes_recv = recvfrom(m_socket, m_recv_buffer.data(), kMaxPacketSize, 0, (struct sockaddr *) &sender_addr, &sender_addr_size);      
    if ( bytes_recv == SOCKET_ERROR )
    {
        int error_code = WSAGetLastError();
        Stop();
        return -1;
    }
По сути я в бесконечном цикле вызываю функцию приема, и если есть данные, то их обрабатываю.

Но это все работает с 1 портом (либо сразу со всеми).
Подскажите - как можно переделать код так, чтобы принимать только по двум определенным портам.
Либо как это вообще можно сделать? (например создать два сокета и их синхронизировать как-то внутри одного потока?)
1
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
13.02.2018, 12:24
Ответы с готовыми решениями:

Прием данных с COM портам
Можно ли организовать сразу прием данных с com порта в БД??? Сейчас получаю в List а потом пишу в...

Одновременный прием и передача по uart в stm32f103c8t6
Вообщем есть задача создать устройство которое будет опрашивать счётчики (по 485-му интерфейсу) и...

Асинхронные сокеты: Как организовать разделение на прием сообщений и прием файлов
Изучив синхронные сокеты, перешел к изучению асинхронных. Столкнулся вот с чем, как, используя...

Поиск значения по двум двум параметрам (вертикали и горизонтали)
Добрый день! Прошу, пожалуйста, помощи. Никак не могу понять ,какие формулы необходимо...

Атака по портам
Вчера Nod32 орал что идет атака по открытым портам, сегодня орет про Dns атаку и &quot;Обнаружена атака...

15
outoftime
║XLR8║
842 / 741 / 223
Регистрация: 25.07.2009
Сообщений: 3,711
Записей в блоге: 5
13.02.2018, 16:50 2
Цитата Сообщение от Leardjiny Посмотреть сообщение
можно ли сделать одновременный прием по двум портам
Можно: либо многопоточность либо concurrency.

Цитата Сообщение от Leardjiny Посмотреть сообщение
в одном потоке
Если создавать еще один поток не вариант - методом исключения остается только concurrency. Вроде как ::boost::asio такое умеет http://www.boost.org/doc/libs/1_66_0/doc/html/boost_asio.html
0
Leardjiny
2 / 2 / 0
Регистрация: 22.09.2013
Сообщений: 183
13.02.2018, 16:58  [ТС] 3
Цитата Сообщение от outoftime Посмотреть сообщение
Можно: либо многопоточность либо concurrency.
А иного варианта нет?

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

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

Можно еще их попеременно опрашивать, но не знаю насколько это правильно.

насчет concurrency - попробую почитать
0
Ygg
1581 / 372 / 146
Регистрация: 10.02.2018
Сообщений: 806
13.02.2018, 17:11 4
Ожидание с нескольких сокетов можно сделать через select. По выходу из select проверка FD_SET-ов и приём/отсылка/коннект "просигналивших" сокетов.
1
outoftime
║XLR8║
842 / 741 / 223
Регистрация: 25.07.2009
Сообщений: 3,711
Записей в блоге: 5
13.02.2018, 17:13 5
Leardjiny, Ну так при concurrency, как раз таки, вы всё время читаете мелкими кусками чтобы можно было читать много точек. Даже если один сокер всё вреям отдает данные мы не читаем его всё время, ты спрашиваем есть ли данные от других соединений, и если таковых нет, продолжаем читать. Так оно работает.

Цитата Сообщение от Leardjiny Посмотреть сообщение
насчет concurrency - попробую почитать
Можете начать с ::boost::asio, учебные материалы посмотреть и т.д. мб кто на youtube выложил что интересное, чтобы понять "как оно работает"
0
Leardjiny
2 / 2 / 0
Регистрация: 22.09.2013
Сообщений: 183
13.02.2018, 18:36  [ТС] 6
Цитата Сообщение от Ygg Посмотреть сообщение
Ожидание с нескольких сокетов можно сделать через select. По выходу из select проверка FD_SET-ов и приём/отсылка/коннект "просигналивших" сокетов.
Я смотрел в эту сторону, просто не совсем понял как это применить. Во всех примерах сокет биндился к одному порту (который все ловит), а потом по селекту уже выдергивал нужные.

Я, наверно, не совсем это понял. Т.к. мне показалось, что остальные порты он тоже ловить будет, просто т.к. их в селекте не будет, то он их пропустит. А как это повлияет на такие порты в другом потоке?

Добавлено через 1 минуту
Цитата Сообщение от outoftime Посмотреть сообщение
Ну так при concurrency, как раз таки, вы всё время читаете мелкими кусками чтобы можно было читать много точек. Даже если один сокер всё вреям отдает данные мы не читаем его всё время, ты спрашиваем есть ли данные от других соединений, и если таковых нет, продолжаем читать. Так оно работает.
Отлично, значит такой вариант имеет место быть.

Цитата Сообщение от outoftime Посмотреть сообщение
Можете начать с ::boost::asio, учебные материалы посмотреть и т.д
Спасибо, я поищу и почитаю на эту тему тогда. В любом случае будет полезно
0
Ygg
1581 / 372 / 146
Регистрация: 10.02.2018
Сообщений: 806
13.02.2018, 19:10 7
Там нет ничего сверх сложного, только первый и последний аргументы немного отличаются для разных ОС. Общий алгоритм примерно такой.

Цикл
for ( ; ; )
Заполняете массив сокетами, с которых хотите получать данные.
fd_set readset;
FD_ZERO(&readset);
FD_SET(socket1, &readset);
FD_SET(socket2, &readset);
...


Ждёте получения данных
select(0, &readset, 0, 0, 0);
По выходу массивы сокетов изменятся, в них останутся только те сокеты, которые нужно обработать. В вашем случае, сокеты с не прочитанными данными.

Выполняете проверку наличия сокета в массиве. Если он есть, то вызывате для него recvfrom без боязни блокировки, там точно что-то есть.
if (FD_ISSET(socket1, &readset))
{ recvfrom(socket1, ...); ... }
if (FD_ISSET(socket2, &readset))
{ recvfrom(socket2, ...); ... }


К началу цикла
2
Leardjiny
2 / 2 / 0
Регистрация: 22.09.2013
Сообщений: 183
13.02.2018, 19:27  [ТС] 8
Ygg, ага, вроде понял.
Я так понимаю, что даже если один из сокетов будет не пустым, то select пройдет. Т.е. заполнять fd_set надо при каждой итерации цикла, т.к. иначе select их выкинуть может, если данных нет?

В целом кажется очень похоже на идею с последовательным опросом двух сокетов в неблокирующем режиме, только тут проверку на себя select берет.
0
Ygg
1581 / 372 / 146
Регистрация: 10.02.2018
Сообщений: 806
13.02.2018, 22:16 9
Leardjiny, да, по выходу из селекта в массиве может быть один или два сокета готовых к приёму. Перед очередным вызовом селекта проще полностью заново заполнить список сокетов.

Вы можете с помощью ioctlsocket(FIONBIO) установить неблокирующий режим.
С помощью ioctlsocket(FIONREAD) без блокирования можете узнать есть ли данные.
С помощью setsockopt(SO_RCVTIMEO) можете установить минимальный интервал ожидания.
Но, вы придёте к одному из двух вариантов. В отсутствии передачи данных цикл будет крутиться в холостую, что приведёт к загрузке одного процессорного ядра. Вы можете поставить слип или использовать небольшое ожидание приёма для снятия нагрузки, но тогда получите задержку приёма данных. А если у вас будет не два сокета, а больше? Селект как раз предназначался для решения подобных проблем, по моему)
0
Leardjiny
2 / 2 / 0
Регистрация: 22.09.2013
Сообщений: 183
13.02.2018, 22:22  [ТС] 10
Цитата Сообщение от Ygg Посмотреть сообщение
В отсутствии передачи данных цикл будет крутиться в холостую, что приведёт к загрузке одного процессорного ядра. Вы можете поставить слип или использовать небольшое ожидание приёма для снятия нагрузки, но тогда получите задержку приёма данных. А если у вас будет не два сокета, а больше? Селект как раз предназначался для решения подобных проблем, по моему)
Да, собственно это и останавливает от неблокирующих режимов. Я с таким сталкивался - при частоте кадров хотя бы в 100 кадров в секунду, слип уже начинает реально приводить к потерям, либо задержкам в приеме. Так что плохая идея, а без него - процессор нагружается жутко.
Просто я думал, что селект тоже вхолостую крутиться может.

Т.е. при отсутствии данных select не выкинет просто пустой список, а будет ждать? Это точно решит проблему, тем более что когда нет приема - поток у меня ничем не занят (для обратной ситуации я завел другой поток с другим портом).
0
Ygg
1581 / 372 / 146
Регистрация: 10.02.2018
Сообщений: 806
13.02.2018, 23:37 11
Цитата Сообщение от Leardjiny Посмотреть сообщение
Т.е. при отсутствии данных select не выкинет просто пустой список, а будет ждать?
Если таймаут ему не ставить последним аргументом, то будет ждать. Ну и код возврата проверять, естественно)
0
Leardjiny
2 / 2 / 0
Регистрация: 22.09.2013
Сообщений: 183
14.02.2018, 09:14  [ТС] 12
Цитата Сообщение от Ygg Посмотреть сообщение
Если таймаут ему не ставить последним аргументом, то будет ждать
Спасибо, попробую сделать так. Должно помочь по идее

Добавлено через 9 часов 13 минут
Цитата Сообщение от Ygg Посмотреть сообщение
Если таймаут ему не ставить последним аргументом, то будет ждать.
Разбираюсь с select, возник такой вопрос: есть способ прервать его?
Логично, что прерывание будет из другого потока, пока основной вариант, что я нашел в интернете - послать из другого потока какое-нибудь сообщение на сокет. Но такой вариант мне кажется странным.
Таймаут я делать в любом случае для реконнекта буду, но делать маленький - вернемся снова к холостому пробегу цикла, а большой таймаут тоже хотелось бы уметь прерывать.

По сути во время работы потока он мне почти всегда включенным нужен, поэтому
Что произойдет с select-ом, если я в итоге поток прерву таким образом:
C++
1
2
3
4
5
6
InterlockedExchange(&m_exitflag, 1);  // это в итоге не сработает, т.к. цикл будет залочен селектом
if (m_hThread != NULL)
    {
        if (WAIT_TIMEOUT == WaitForSingleObject(m_hThread, 1000))
            TerminateThread( m_hNETThread, 1 ); 
    }
0
Ygg
1581 / 372 / 146
Регистрация: 10.02.2018
Сообщений: 806
14.02.2018, 09:52 13
TerminateThread - это плохое решение.
Вы ждёте секунду прежде чем прервать поток, поставьте лучше в select таймаут на 10 мс - всё быстрее будет. Ну, будет периодически в холостую пробегать, но процессор грузиться не будет и задержек приёма тоже.
select ещё можно прервать закрытием из другого потока одного из ожидающих в нём приёма сокета.
Так как вы пишите под винду, то можно перейти на её асинхронные сокеты с ожиданием WaitForMultipleObjects по ивентам, куда можно добавить ещё один ивент для управления от второго потока. Лично я бы не стал так заморачиваться.
0
Leardjiny
2 / 2 / 0
Регистрация: 22.09.2013
Сообщений: 183
14.02.2018, 10:01  [ТС] 14
Цитата Сообщение от Ygg Посмотреть сообщение
TerminateThread - это плохое решение.
Потому и хочется прерывать нормально.

Цитата Сообщение от Ygg Посмотреть сообщение
select ещё можно прервать закрытием из другого потока одного из ожидающих в нём приёма сокета.
Т.е. сделать какую-то функцию типа public, которая будет делать либо close либо shutdown сокета, который висит в селекте? при ее вызове select прервется, и можно будет закрывать и второй сокет и поток?
0
Ygg
1581 / 372 / 146
Регистрация: 10.02.2018
Сообщений: 806
14.02.2018, 12:08 15
Leardjiny, shutdown вроде бы не влияет на select, а close должен приводить к прерыванию ожидания.
0
Leardjiny
2 / 2 / 0
Регистрация: 22.09.2013
Сообщений: 183
14.02.2018, 12:14  [ТС] 16
Ygg, спасибо. Тогда попробую проверить и так и так. В принципе таймаут в 100мс меня наверно устроит
0
14.02.2018, 12:14
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
14.02.2018, 12:14

Вопрос по портам IO
Подключаю фототранзистор к контроллеру (картинка в файле). Какая разница закрыт транзистор или...

Вопрос по портам
Всем привет, в сфере администрирования совсем недавно так что прошу не судить строго. Имеется сеть...

разделить 2 сайта по портам
арендовали сервер на котором выделен один ip по этому ip я могу увидеть только 1 сайт (порт 80)...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
16
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2019, vBulletin Solutions, Inc.
Рейтинг@Mail.ru