Модераторы: xvr
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> SO_REUSEADDR или его замена 
:(
    Опции темы
konshyn
Дата 28.9.2014, 20:42 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 295
Регистрация: 19.9.2013

Репутация: нет
Всего: нет



Открываю 1000-2000 udp сокетов
Использую опцию SO_REUSEADDR. Потом делаю bind() на такой адрес:
Код

binaddr.sin_family = AF_INET;
bindaddr.sin_addr.s_addr = htonl(INADDR_ANY); //несколько ip-адресов
bindaddr.sin_port = htons(port[i]);
bind(...)


после рестарта программы, при bind() ошибки не выдает, но на эти сокеты в течение 5-10 минут (примерно) ничего не приходит. 
как с этим справиться?


--------------------
«Потому что ценность акта действия в этой стране возрастает в несколько раз».
PM MAIL Skype   Вверх
feodorv
Дата 28.9.2014, 22:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2214
Регистрация: 30.7.2011

Репутация: 1
Всего: 45



А какой здесь смысл в SO_REUSEADDR для udp-сокетов?

Цитата(konshyn @  28.9.2014,  21:42 Найти цитируемый пост)
после рестарта программы, при bind() ошибки не выдает, но на эти сокеты в течение 5-10 минут (примерно) ничего не приходит. 

Поясните, пожалуйста, идут ли вообще на эти порты данные в течении этих 5-10 минут, и как Вы работаете с таким немалым набором сокетов.


--------------------
Напильник, велосипед, грабли и костыли - основные инструменты программиста...
PM MAIL   Вверх
konshyn
Дата 28.9.2014, 22:43 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 295
Регистрация: 19.9.2013

Репутация: нет
Всего: нет



Работаю с помощью epoll. Я не знаю, идут ли данные, но счетчик показывает, что не идут. Я до этого открывал 1024 сокета, но netstat не показывал около 300 открытых портов, потом я уменьшил до 512 кол-во сокетов. netstat показывал все. 
Когда уменьшил кол-во сокетов, проблем стало меньше при рестарте. Потом оставил всего один сокет,  и проблемы ушли. 
Напрашивается вывод, что кол-во сокетов влияет на мою проблему, а может и нет.

Суть задачи в том, что данные должны отправляться на >1000 различных портов, а на другой стороне принимаются. Как можно решить эту задачу с помощью ресурсов ОС Linux?


--------------------
«Потому что ценность акта действия в этой стране возрастает в несколько раз».
PM MAIL Skype   Вверх
feodorv
Дата 28.9.2014, 23:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2214
Регистрация: 30.7.2011

Репутация: 1
Всего: 45



Как вызван epoll_create?

Цитата(konshyn @  28.9.2014,  23:43 Найти цитируемый пост)
Суть задачи в том, что данные должны отправляться на >1000 различных портов, а на другой стороне принимаются.

Не понял. 
Через sendto и recvfrom можно работать с одним udp-сокетом.

SO_REUSEADDR здесь не нужен.


--------------------
Напильник, велосипед, грабли и костыли - основные инструменты программиста...
PM MAIL   Вверх
konshyn
Дата 29.9.2014, 11:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 295
Регистрация: 19.9.2013

Репутация: нет
Всего: нет



Цитата(feodorv @  28.9.2014,  23:01 Найти цитируемый пост)
Как вызван epoll_create?

Код


const int count_ports = 1024;

struct 
{
    int sd;
    int port;
    int iface;
}sp[1024];

int send_socket;

pcap_t *internal_handle = NULL;

int main(int argc, char *argv[])
{
    int i, start_port = 31567;
    struct sockaddr_in servaddr;
    int on_off = 1, result;
    
    for (i = 0; i < count_ports; i++)
    {
        sp[i].sd = socket(PF_INET, SOCK_DGRAM, 0);
        if (sp[i].sd < 0)
        {
            perror("socket()");
            return -1;
        }
        
        result = setsockopt(sp[i].sd, SOL_SOCKET, SO_REUSEADDR, (char *)&on_off, sizeof(on_off));
        if (result < 0)
        {
            perror("setsockopt()");
            return -1;
        }
        
        
        sp[i].port = i + start_port;
        servaddr.sin_family = AF_INET;
        servaddr.sin_port = htons(sp[i].port);
        servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
        if (bind(sp[i].sd, (struct sockaddr *)&servaddr, sizeof(struct sockaddr_in)) < 0)
        {
            perror("bind()");
            return -1;
        }
    }
    send_socket = socket(PF_INET, SOCK_DGRAM, 0);
    if (send_socket < 0)
    {
        perror("socket()");
        return -1;
    }
    result = setsockopt(send_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&on_off, sizeof(on_off));
    if (result < 0)
    {
        perror("setsockopt()");
        return -1;
    }

    int int_sd = 0; // Дескриптор внутреннего интерфейса

    int_sd = pcap_get_selectable_fd(internal_handle);
    const int MAX_EPOLL_EVENTS = count_ports + 1;
    const int BACK_LOG = MAX_EPOLL_EVENTS;
    int nfds = 0, epoll_fd = 0;
    struct epoll_event events[MAX_EPOLL_EVENTS], listevents[MAX_EPOLL_EVENTS];

    memset((void *)&listevents, 0, MAX_EPOLL_EVENTS*sizeof(struct epoll_event));

    epoll_fd = epoll_create(MAX_EPOLL_EVENTS);
    if (epoll_fd < 0)
    {
        demise_journal("epoll_create() failed: %s", strerror(errno));
        return;
    }
    
    listevents[count_ports].events = EPOLLIN;
    listevents[count_ports].data.fd = int_sd;
    if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, int_sd, &listevents[count_ports]) < 0)
    {
        perror("epoll_ctl()");
        return -1;
    }
    for (i = 0; i < count_ports; i++)
    {
        listevents[i].events = EPOLLIN;
        listevents[i].data.fd = sp[i].sd;
        if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, sp[i].sd, &listevents[i]) < 0)
        {
            perror("epoll_ctl()");
            return -1;
        }
    }

    struct pcap_pkthdr *header;
    const u_char *pkt_data;
    while (!get_signal_exit)
    {

        nfds = epoll_wait(epoll_fd, events, MAX_EPOLL_EVENTS, -1);
        
        if (nfds < 0)
        {
            if (errno == EINTR)
                continue;

            perror("epoll_wait()");
            return -1;
        }
        
        for (i = 0; i < nfds; i++)
        {
            if (events[i].data.fd == int_sd)
            {
                ....
            }
            else
            {
                receive_udp(events[i].data.fd);
            }
        }
    }
    ....
    return 0;
}


void demise_receive_udp(int sd)
{
    int cnt_recv = 0;
    static char recv_buff[2048];
    struct sockaddr_in clientaddr;

    memset((void*)&clientaddr, 0, sizeof(struct sockaddr_in));
    int length_sock = sizeof(struct sockaddr_in);

    cnt_recv = recvfrom(sd, recv_buff, 2048, 0, (struct sockaddr *)&clientaddr, (void*)&length_sock);
    if (cnt_recv < 0)
    {
        perror("recvfrom()");
        return;
    }
    if (cnt_recv == 0)
        return;
    ...
}


Переменная send_socket нужна для отправки данных на различные порты, т.к. если я в функцию бросаю сокет, к которому применил bind, он говорит, что INVALID ARGUMENT, скорее всего потому что bind() был на INADDR_ANY.


--------------------
«Потому что ценность акта действия в этой стране возрастает в несколько раз».
PM MAIL Skype   Вверх
feodorv
Дата 2.10.2014, 07:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2214
Регистрация: 30.7.2011

Репутация: 1
Всего: 45



Честно говоря, довольно трудно судить о том, что у Вас происходит в программе. Ну, например:
Цитата(konshyn @  29.9.2014,  12:16 Найти цитируемый пост)
    struct epoll_event events[MAX_EPOLL_EVENTS], listevents[MAX_EPOLL_EVENTS];

Код инициализации массива listevents приведен, а массива events - нет, посему так и остаешься в приятном недоумении, используете ли Вы далее неинициализированный массив events (кстати, зачем Вам два массива?).


В операторе
Цитата(konshyn @  29.9.2014,  12:16 Найти цитируемый пост)
    memset((void *)&listevents, 0, MAX_EPOLL_EVENTS*sizeof(struct epoll_event));

я бы червячка убрал, не нужен он.


Так же ещё раз хочу обратить Ваше внимание на то, что опция SO_REUSEADDR не нужна при рестарте udp-сокетов.


Для сокета send_socket я бы еще задал событие EPOLLOUT и его обработку (на случай, если внутренний буфер сокета на передачу вдруг переполнился).


Цитата(konshyn @  29.9.2014,  12:16 Найти цитируемый пост)
Переменная send_socket нужна для отправки данных на различные порты, т.к. если я в функцию бросаю сокет, к которому применил bind, он говорит, что INVALID ARGUMENT, скорее всего потому что bind() был на INADDR_ANY.

В какую функцию? sendto? Маловероятно, т.к. bind в данном случае лишь принудительно задает номер исходящего порта.


--------------------
Напильник, велосипед, грабли и костыли - основные инструменты программиста...
PM MAIL   Вверх
konshyn
Дата 2.10.2014, 12:23 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 295
Регистрация: 19.9.2013

Репутация: нет
Всего: нет



Цитата(feodorv @  2.10.2014,  07:39 Найти цитируемый пост)

я бы червячка убрал, не нужен он.

Не знаю, как он туда попал. В коде его нет:)



Цитата(feodorv @  2.10.2014,  07:39 Найти цитируемый пост)
используете ли Вы далее неинициализированный массив events (кстати, зачем Вам два массива?).

использую. просмотрите цикл while, в котором используется epoll_wait().


Цитата(feodorv @  2.10.2014,  07:39 Найти цитируемый пост)
В какую функцию? sendto? Маловероятно, т.к. bind в данном случае лишь принудительно задает номер исходящего порта. 

send_socket инициализирован специально, потому что если я использую сокеты, которые за'bind'ил и которые потом включил в список epoll, для отправки сообщений через sendto, то выдает ошибку invalid argument. 
send_socket не надо включать в список, и если буфер отправки будет переполнен, то мне надо ждать, чтобы отправить следующий пакет. send_socket - блокирующий сокет.


Цитата(feodorv @  2.10.2014,  07:39 Найти цитируемый пост)

Так же ещё раз хочу обратить Ваше внимание на то, что опция SO_REUSEADDR не нужна при рестарте udp-сокетов.

это я понял, потому что с одним сокетом проблем нет. а когда их 500 - есть. 


--------------------
«Потому что ценность акта действия в этой стране возрастает в несколько раз».
PM MAIL Skype   Вверх
feodorv
Дата 3.10.2014, 21:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2214
Регистрация: 30.7.2011

Репутация: 1
Всего: 45



Цитата(konshyn @  2.10.2014,  13:23 Найти цитируемый пост)
send_socket не надо включать в список, и если буфер отправки будет переполнен, то мне надо ждать, чтобы отправить следующий пакет. send_socket - блокирующий сокет.

Понял. А Вы все это (epoll_wait и sendto) в одном потоке делаете?


Цитата(konshyn @  2.10.2014,  13:23 Найти цитируемый пост)
если я использую сокеты, которые за'bind'ил и которые потом включил в список epoll, для отправки сообщений через sendto, то выдает ошибку invalid argument

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


Цитата(konshyn @  2.10.2014,  13:23 Найти цитируемый пост)
это я понял

Что именно? Зачем нужна или не нужна опция SO_REUSEADDR при рестарте tcp-сервера или udp-сервера?


Цитата(konshyn @  2.10.2014,  13:23 Найти цитируемый пост)
а когда их 500 - есть

Вот тут надо бы плотно заняться тем, что у Вас происходит в самом сервере (я сильно намекаю на выдачу отладочных сообщений в какой-нибудь уникальный лог-файл).


--------------------
Напильник, велосипед, грабли и костыли - основные инструменты программиста...
PM MAIL   Вверх
konshyn
Дата 4.10.2014, 20:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 295
Регистрация: 19.9.2013

Репутация: нет
Всего: нет



Цитата(feodorv @  3.10.2014,  21:05 Найти цитируемый пост)
А Вы все это (epoll_wait и sendto) в одном потоке делаете?

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

epoll был лучшим решением и оно работает, но есть проблема - она в этой теме:)


Цитата(feodorv @  3.10.2014,  21:05 Найти цитируемый пост)

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

интересная мысль, спасибо за наводку. попробую и потом напишу.


Цитата(feodorv @  3.10.2014,  21:05 Найти цитируемый пост)
Что именно? Зачем нужна или не нужна опция SO_REUSEADDR при рестарте tcp-сервера или udp-сервера?

что для UDP сокетов не нужен SO_REUSEADDR.

Добавлено через 14 минут и 2 секунды
Цитата(feodorv @  3.10.2014,  21:05 Найти цитируемый пост)

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

Хотя подумал - мало вероятно, т.к. когда делаю 1-2 сокета с epoll'ом все работает. Но покопать все-таки в эту сторону стоит.


--------------------
«Потому что ценность акта действия в этой стране возрастает в несколько раз».
PM MAIL Skype   Вверх
feodorv
Дата 5.10.2014, 04:31 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2214
Регистрация: 30.7.2011

Репутация: 1
Всего: 45



Цитата(konshyn @  4.10.2014,  21:13 Найти цитируемый пост)
epoll был лучшим решением и оно работает

Так я ни слова не сказал против epoll, наоборот, я двумя руками за. Что мне кажется крайне сомнительным, так это существование двух блокирующих вызовов в одном потоке. Кстати, а данные, которые Вы посылаете через sendto( send_socket, ...), неужели они предназначены той тысяче сокетов, которые в epoll_wait?


Цитата(konshyn @  4.10.2014,  21:13 Найти цитируемый пост)
но есть проблема - она в этой теме

Я, честно говоря, так и не понял сути проблемы. Вы пишете:
Цитата(konshyn @  28.9.2014,  21:42 Найти цитируемый пост)
на эти сокеты в течение 5-10 минут (примерно) ничего не приходит

Цитата(konshyn @  28.9.2014,  23:43 Найти цитируемый пост)
Я до этого открывал 1024 сокета, но netstat не показывал около 300 открытых портов, потом я уменьшил до 512 кол-во сокетов. netstat показывал все. 

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


Я еще раз настойчиво советую Вам выдачу отладочных сообщений (с как минимум посекундным текущим временем), например:
Цитата(konshyn @  29.9.2014,  12:16 Найти цитируемый пост)

    slog_open();
    ....
    slog( "Starting socket list, count_ports = %d", count_ports);
    for (i = 0; i < count_ports; i++)
    {
        sp[i].sd = socket(PF_INET, SOCK_DGRAM, 0);
        if (sp[i].sd < 0)
        {
            perror("socket()");
            return -1;
        }
        slog( "    Creating socket #%d: %d", i, sp[i].sd);

        sp[i].port = i + start_port;
        servaddr.sin_family = AF_INET;
        servaddr.sin_port = htons(sp[i].port);
        servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
        if (bind(sp[i].sd, (struct sockaddr *)&servaddr, sizeof(struct sockaddr_in)) < 0)
        {
            perror("bind()");
            return -1;
        }
        slog( "    Binding socket #%d to port %d", i, sp[i].port);
    }
   slog( "Finishing socket list" );

Цитата(konshyn @  29.9.2014,  12:16 Найти цитируемый пост)
    cnt_recv = recvfrom(sd, recv_buff, 2048, 0, (struct sockaddr *)&clientaddr, (void*)&length_sock);
    slog( "Reading %d bytes from socket %d", cnt_recv, sd);

И так далее, и тому подобное. Времени это займет не много, а вот понимания происходящего в программе добавит.

Это сообщение отредактировал(а) feodorv - 5.10.2014, 04:36


--------------------
Напильник, велосипед, грабли и костыли - основные инструменты программиста...
PM MAIL   Вверх
konshyn
Дата 5.10.2014, 15:46 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 295
Регистрация: 19.9.2013

Репутация: нет
Всего: нет



Цитата(feodorv @  5.10.2014,  04:31 Найти цитируемый пост)
Что мне кажется крайне сомнительным, так это существование двух блокирующих вызовов в одном потоке.

Их там больше, но максимальная последовательность - 3 блокирующих вызова.  epoll_wait(), recvfrom(), sendto().

А почему это сомнительно? Пакет приходит на карточку, процесс выходит из epoll_wait(), ядро копирует с карточки в буфер процесса, пакет обрабатывается и если надо, отправляется.
Насколько я понимаю, пакеты приходят по одному, а не несколько штук параллельно, и нет смысла ядру копировать в буфер процесса пакет, который пришел позже. А т.к. мне все нужно делать попорядку, то последовательные действия с ожиданием их завершения мне показалось лучшим решением. разве что на отправку можно снять блокировку, но пока что нагрузка небольшая, поэтому проблем не возникало с блокировкой. 

Напишите, пожалуйста, свое мнение на этот счет. Может я что-то упустил.


Цитата(feodorv @  5.10.2014,  04:31 Найти цитируемый пост)
Кстати, а данные, которые Вы посылаете через sendto( send_socket, ...), неужели они предназначены той тысяче сокетов, которые в epoll_wait?

Да.

Цитата(feodorv @  5.10.2014,  04:31 Найти цитируемый пост)
И так далее, и тому подобное. Времени это займет не много, а вот понимания происходящего в программе добавит.

Сделаю. Как будет готово, напишу.

Это сообщение отредактировал(а) konshyn - 5.10.2014, 15:48


--------------------
«Потому что ценность акта действия в этой стране возрастает в несколько раз».
PM MAIL Skype   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С/С++: Программирование под Unix/Linux"
xvr
  • Проставьте несколько ключевых слов темы, чтобы её можно было легче найти.
  • Не забывайте пользоваться кнопкой "Код".
  • Вопросы мобильной разработки тут
  • Телепатов на форуме нет! Задавайте чёткий, конкретный и полный вопрос. Указывайте полностью ошибки компилятора и компоновщика.
  • Новое сообщение должно иметь прямое отношение к разделу форума. Флуд, флейм, оффтопик запрещены.
  • Категорически запрещается обсуждение вареза, "кряков", взлома программ и т.д.

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, xvr.

 
 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Программирование под Unix/Linux | Следующая тема »


 




[ Время генерации скрипта: 0.0586 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.