Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: проблема с SO_REUSEADDR в Linux
Форум разработчиков электроники ELECTRONIX.ru > Cистемный уровень проектирования > Операционные системы
romez777
Приветствую.
Установил бит SO_REUSEADDR на сокет, но что-то не всегда работает должным образом, периодически ругается (при запуске приложения), что мол сокет порт занят:

...
int sd;
int yes = 1;

if ( (sd = socket(AF_INET, SOCK_STREAM, 0)) < 0 ) {
perror("socket() error!");
exit(1);
}

if ( setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1 ) {
{
perror("setsockopt() error");
exit(1);
}
...

if ( bind(sd, (struct sockaddr *)&serv_addr, sizeof serv_addr) == -1 ) {
perror("bind() error");
exit(1);
}
...

что еще нужно подкрутить?
Спасибо!
Olej
Цитата(romez777 @ Oct 11 2005, 15:35)
Приветствую.
Установил бит SO_REUSEADDR на сокет, но что-то не всегда работает должным образом, периодически ругается (при запуске приложения), что мол сокет порт занят:
...
if ( bind(sd, (struct sockaddr *)&serv_addr, sizeof serv_addr) == -1 ) {
  perror("bind() error");
  exit(1);
}
...
что еще нужно подкрутить?
Спасибо!
*


Всё, похоже, - так, только непонятно что там в &serv_addr ? все ли поля заполнены? Вот под рукой оказался фрагмент: это для серверного сокета (SO_REUSEADDR, как правило, применяют для серверных-прослушивающих сокетов, ... я другого не видел), это работало давно и безукоризненно:

Код
  struct sockaddr_in addr;
  int rc = 1, ls;
  if( ( ls = socket( AF_INET, SOCK_STREAM, 0 ) ) = -1 )
     errx( "create stream socket failed" );
  if( setsockopt( ls, SOL_SOCKET, SO_REUSEADDR, &rc, sizeof( rc ) )
      != 0 ) errx( "set socket option failed" );
  memset( &addr, 0, sizeof( addr ) );
  addr.sin_len = sizeof( addr );
  addr.sin_family = AF_INET;
  addr.sin_port = htons( p );
  addr.sin_addr.s_addr = htonl( INADDR_ANY );
  if( bind( ls, (struct sockaddr*)&addr, sizeof( sockaddr ) ) != 0 )
     errx( "bind socket address failed" );
  if( listen( ls, 25 ) != 0 )
     errx( "put socket in listen state failed" );


- посмотрите заполнение addr полей (это код QNX, но значения не имеет - стек TCP у них ... одинаково работает, я это использовал и в Linux, но нет файлов под рукой).

Если совсем уж плохо smile.gif - посмотрите вот здесь, может что подскажет:
http://qnxclub.net/files/articles/EchSrv/echsrv-107.html
http://qnxclub.net/files/articles/tcpip-qnx/tcpip-qnx.pdf

А "ругается", да ещё "периодически" smile.gif ... А вы случаем 2 экземпляра приложения не пытаетесь пустить, или 2-й (поток, например) запускать - пока 1-й ещё окончательно не завершился?
romez777
Цитата(Olej @ Oct 12 2005, 13:26)
Всё, похоже, - так, только непонятно что там в &serv_addr ? все ли поля заполнены? Вот под рукой оказался фрагмент: это для серверного сокета (SO_REUSEADDR, как правило, применяют для серверных-прослушивающих сокетов, ... я другого не видел), это работало давно и безукоризненно:

Код
  struct sockaddr_in addr;
  int rc = 1, ls;
  if( ( ls = socket( AF_INET, SOCK_STREAM, 0 ) ) = -1 )
     errx( "create stream socket failed" );
  if( setsockopt( ls, SOL_SOCKET, SO_REUSEADDR, &rc, sizeof( rc ) )
      != 0 ) errx( "set socket option failed" );
  memset( &addr, 0, sizeof( addr ) );
  addr.sin_len = sizeof( addr );
  addr.sin_family = AF_INET;
  addr.sin_port = htons( p );
  addr.sin_addr.s_addr = htonl( INADDR_ANY );
  if( bind( ls, (struct sockaddr*)&addr, sizeof( sockaddr ) ) != 0 )
     errx( "bind socket address failed" );
  if( listen( ls, 25 ) != 0 )
     errx( "put socket in listen state failed" );


- посмотрите заполнение addr полей (это код QNX, но значения не имеет - стек TCP у них ... одинаково работает, я это использовал и в Linux, но нет файлов под рукой).

Приветствую.
Вот как выглядит заполнение структуры:
Код
const int port = 5000;
...
/* zero out structure */
memset(&serv_addr, 0, sizeof serv_addr);

/* fill in structure */
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(port);

if ( bind(sd, (struct sockaddr *)&serv_addr, sizeof serv_addr) == -1 ) {
 perror("bind() error");
 exit(1);
}


"Ругается периодически" в том смысле, что я не заметил пока какой-то закономерности smile.gif Всегда запускается только одна копия, сервер построен на основе fork()'ов, треды не применяются. Мне также рекомендовали посмотреть на опцию SO_REUSEPORT, но в моем линуксе ее не нашел, наверное платформенно-зависимый параметр.
Olej
Цитата(romez777 @ Oct 12 2005, 15:02)
Код
const int port = 5000;
...
/* fill in structure */
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(port);

*


1. Я вам не зря показывал заполнение - заполните поле длины: в некоторых реализациях без неё проходило (в Linux, но это зависит от верс. стека-ядра - оно вам надо?) ... но строго по POSIX (или по У.Стивенсу smile.gif ) - она должна быть заполнена:
Код
/* fill in structure */
serv_addr.sin_len = sizeof( serv_addr );


2. И порт какой-то странненький (маленький, из well-known) - вы уверены, что на нём не сидит какая-то из служб, работающих в вашей конфигурации OS?

Цитата(romez777 @ Oct 12 2005, 15:02)
"Ругается периодически" в том смысле, что я не заметил пока какой-то закономерности smile.gif Всегда запускается только одна копия, сервер построен на основе fork()'ов, треды не применяются. Мне также рекомендовали посмотреть на опцию SO_REUSEPORT, но в моем линуксе ее не нашел, наверное платформенно-зависимый параметр.
*


3. То, что fork() или pthread_create() - это значения не имеет, "что в лоб, что по-лбу" - одно и тоже... но если вы в разных ветках fork() хотите открыть сокеты с одинаковыми параметрами (внешними) - этот номер не проходит, здесь и SO_REUSEADDR вам не поможет... да и зачем: я, как понял по INADDR_ANY - это у вас сервер? сервер по bind() создаёт единый экземпляр прослушивающего сокета, а каждая ветка fork() получает по accept() свою копию связанного сокета, имеющие те-же внешние параметны (адрес, порт) - с ними и работаете...
Или я чего-то не так понял blink.gif

4. Посмотрте те URL, что я назвал ... там рядом есть готовые проекты 7-ми серверов, принципиально разной структуры, со сравнением результатов:
http://qnxclub.net/files/articles/EchSrv/echsrv.tgz
romez777
Приветствую.
Цитата(Olej @ Oct 12 2005, 15:44)
1. Я вам не зря показывал заполнение - заполните поле длины: в некоторых реализациях без неё проходило (в Linux, но это зависит от верс. стека-ядра - оно вам надо?) ... но строго по POSIX (или по У.Стивенсу smile.gif ) - она должна быть заполнена:
Код
/* fill in structure */
serv_addr.sin_len = sizeof( serv_addr );


Поле длины отсутствует (и соответственно компиляция обламывается: structure has no member named `sin_len').

Цитата
2. И порт какой-то странненький (маленький, из well-known) - вы уверены, что на нём не сидит какая-то из служб, работающих в вашей конфигурации OS?

В системе ни одна из служб не занимает порт 5000, проверено. Стивенс пишет, что диапозон портов 1024-5000 используются системой для авто-назначения, то есть все >5000 вполне легально импользовать для своих нужд, правильно я понимаю?

Цитата
3. То, что fork() или pthread_create() - это значения не имеет, "что в лоб, что по-лбу" - одно и тоже... но если вы в разных ветках fork() хотите открыть сокеты с одинаковыми параметрами (внешними) - этот номер не проходит, здесь и SO_REUSEADDR вам не поможет... да и зачем: я, как понял по INADDR_ANY - это у вас сервер? сервер по bind() создаёт единый экземпляр прослушивающего сокета, а каждая ветка fork() получает по accept() свою копию связанного сокета, имеющие те-же внешние параметны (адрес, порт) - с ними и работаете...
Или я чего-то не так понял  blink.gif

Да, именно так и работает мой сервер: каждый новый форк получает после accept'a свой socket handler и работает с ним.

Еще поизучаю примеры с вашей ссылки, спасибо.
Dizel
Цитата
В системе ни одна из служб не занимает порт 5000, проверено. Стивенс пишет, что диапозон портов 1024-5000 используются системой для авто-назначения, то есть все >5000 вполне легально импользовать для своих нужд, правильно я понимаю?


Иксы к примеру висят на 6000 порте. А вообще cat /etc/services - там довольно много портов > 5000.
Состояние портов можно глянуть с помощью netstat -a
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.