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

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Неблокирующий сокет Си проблема с закрытием, проблема с закрытием сокетов в цикле 
:(
    Опции темы
feodorv
Дата 23.11.2012, 16:03 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


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

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



Цитата(MnxVol @  23.11.2012,  13:49 Найти цитируемый пост)
уже лучше.

То есть не всё гладко?)))

Цитата(MnxVol @  23.11.2012,  13:49 Найти цитируемый пост)
оператор case пришлось все же заменить на if

Странно, может ntohs(cd->id) виновато?


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


Новичок



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

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



Не понимаю, что было виной, но сначала он не хотел работать с константой, определенной как unsigned short const REQ_REQUEST = 0x0200; пытался определить ее по-другому - та ошибка пропала, но желаемого результата я не получил. Не уверен что ntohs(cd->id) виновато, потому что в операторе if оно отрабатывает. Да, не все гладко. При долгосрочном тесте почему-то программа виснет. Сейчас проверяю.

вот то, что у меня получилось: 
Код

struct cisdata
{
   unsigned short id;
   unsigned int number;
   unsigned int ipcli;
   unsigned int ipserv;
   unsigned short lenurl;
   unsigned short lenname;
   unsigned char data[2];
} __attribute__((packed));


int read_socket(int fd, void *buf, int size)
{
    int have_read;
    char c;
    int i = 0;

    while(size > 0)
    {  
  if((have_read = recv(fd, &c, 1, 0)) < 0)
  {
    if(errno == EWOULDBLOCK)
    continue;
    perror("read_socket: ");
    return 0;
  }
  ((char *)buf)[i++] = c;
  size--;
    }

    return 1;
}

int process_socket( int s, struct cisdata *cd)
{
    unsigned short const REQ_REQUEST = 0x0200;
    unsigned short const REQ_ALIVE = 0x0203;
    short const RES_ACCEPT = 0x0002;
    short const RES_ALIVE = 0x0302;    
    short const RES_DENIED = 0x0102;

if((ntohs((*cd).id)) == REQ_ALIVE)
    {
    (*cd).id = RES_ALIVE;
    (*cd).ipcli = 0;
    if( (send( s, &cd, 10, 0)) < 0 ) 
  {
  perror ("Write");
  return 1;
  }
    printf("Send to CISCO RES_ALIVE\n");
    }
  else
  {    
    if((ntohs((*cd).id)) == REQ_REQUEST)
    {
    char *data, *url, *name, return_url[1024];
    int return_lenurl;
    unsigned int number;
    unsigned short lenurl, lenname;
    lenurl = ntohs((*cd).lenurl), lenname = ntohs((*cd).lenname);
     if( lenurl == 0 || lenname == 0 )
     {
      printf( "data error\n" );
      return 0;
     }
    data = malloc(lenurl + lenname);
    data[0] = (*cd).data[0]; // 2 байта уже прочли
    data[1] = (*cd).data[1];
    // дочитываем (читаем ровно столько, сколько запрашиваем, не больше, не меньше)
     if( (lenurl + lenname) > 2 && read_socket( s, &data[2], lenurl + lenname - 2) < 0 )
     {
      free( data );
      return 0;
     }
    url = &data[0];
    name = &data[lenurl];
    // проверим, что на концах есть символ '\0'
     if( url[lenurl-1] != '\0' || name[lenname-1] != '\0' )
     {
      printf( "data error\n" );
      free( data );
      return 0;
     }
    (*cd).id = htons(RES_ACCEPT);
        (*cd).ipcli = 0;
     if( (send( s, cd, 10, 0)) < 0 ) 
      perror("Write");
    printf("Send to cisco RES_ACCEPT\n");
    //printf("Url: %s\n", (*cd).data);
    printf("Url: %s\n", url);
    const time_t timer = time(NULL);
    printf("%s\n", ctime(&timer));
    free( data );
    }
  }
 return 0;
}

int main()
{    
struct cisdata cd;

char buf[PKTSIZE]; /* used for incoming dir name, and outgoing data */    
char *redirect = "http://therules.ru/";

char* rstring;
    int len = htonl(strlen(redirect) + 1);
    int sd, nsd, x, sd_current, addrlen, t, b, r, n, rd, i = sizeof(int);
    unsigned short id;
    unsigned int number;
    struct sockaddr_in sa;
    bzero(&cd, sizeof (cd));
    
    
    if ((sd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
    {
  perror("socket");
  return 1;
    }
    printf ("SD %d\n", sd);
    
    bzero(&sa, sizeof (sa));
    sa.sin_family = AF_INET;
    sa.sin_addr.s_addr = htonl(INADDR_ANY);
    sa.sin_port = htons(PORT);
    
    if ((b = bind(sd, (struct sockaddr *) &sa, sizeof(sa))) < 0)
    {
  perror("bind");
  return 2;
    }
    printf("bind= %d\n", b);
    
    if ((t = listen(sd, BACKLOG)) == -1)
    printf("listen= %d\n",t);
    
while( 1 )
    {
   struct cisdata cd;
 
   if ((nsd = accept(sd, (struct sockaddr *) &sa, &addrlen)) < 0) 
   {
       printf( "General error on accent()\n" );
       break;
   }
   printf("Nsd=%d\n", nsd);
   while( (read_socket( nsd, &cd, 20)) > 0 )
       if( !process_socket( nsd, &cd) ) break;
   close( nsd );
    }
}


Это сообщение отредактировал(а) feodorv - 30.12.2012, 12:30
PM MAIL   Вверх
feodorv
Дата 27.11.2012, 12:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


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

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



Цитата(MnxVol @  27.11.2012,  11:17 Найти цитируемый пост)
Хотя по логике вроде должен после каждого url закрывать сокет, и печатать Nsd=4 (открывать новый).

Так в том-то и дело, что не должен закрываться сокет после каждого url. Соединение действует постоянно.


Цитата(MnxVol @  27.11.2012,  11:17 Найти цитируемый пост)
При долгосрочном тесте почему-то программа виснет.

Судя по всему, программа виснет, когда циска закрывает соединение (по каким причинам - тоже выяснить бы нужно, возможно, прочла из сокета некорректный ответ).
В этом случае recv возвращает 0. Проверки на этот случай в программе нет, а она нужна:
Цитата(MnxVol @  27.11.2012,  11:17 Найти цитируемый пост)
  if((have_read = recv(fd, &c, 1, 0)) < 0)
  {
    if(errno == EWOULDBLOCK)
    continue;
    perror("read_socket: ");
    return 0;
  }
  else if( have_read == 0 )
  {
     printf( "connection reset\n" );
     return 0;
  }





Кроме того, в случае возврата циске положительного решения подпрограмма process_socket явным образом не возвращает никакого значения (а, следовательно, возвращает случайный мусор, хотя обязана вернуть 1 или 0):
Цитата(MnxVol @  27.11.2012,  11:17 Найти цитируемый пост)
   printf("Send to cisco RES_ACCEPT\n");
    //printf("Url: %s\n", (*cd).data);
    printf("Url: %s\n", url);
    const time_t timer = time(NULL);
    printf("%s\n", ctime(&timer));
    free( data );
    return 1;
    }
  }

  printf( "Unknown request id %x\n", cd->id);
  return 0;

}



И довольно странный подход к указателю cd. Почему (*cd).id, а не cd->id?

Добавлено через 3 минуты и 54 секунды
Соответственно
Цитата(MnxVol @  27.11.2012,  11:17 Найти цитируемый пост)
    if( (lenurl + lenname) > 2 && read_socket( s, &data[2], lenurl + lenname - 2) <= 0 )



Это сообщение отредактировал(а) feodorv - 27.11.2012, 12:08


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


Новичок



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

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



Изменил то, на что вы указали - работает, спасибо! За 16 часов не вылетела ни разу, постоянно обрабатывает запросы. Подскажите еще, с помощью чего лучше реализовать регулярные выражения на С, чтобы разобрать url и сравнить с файлом, содержащим маски запрещенных ресурсов, и еще, есть ли смысл какие-то функции перенести на 64-битную ось. Я например не нашел 64 битного аналого функции recv, зато есть 64 битная функция open, что в данном случае будет лучше?
PM MAIL   Вверх
feodorv
Дата 29.11.2012, 03:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


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

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



Цитата(MnxVol @  28.11.2012,  09:44 Найти цитируемый пост)
работает

Очень хорошо!

Цитата(MnxVol @  28.11.2012,  09:44 Найти цитируемый пост)
Я например не нашел 64 битного аналого функции recv

А его и нету))) Если Вы задумались над 64-битной версией, то работа с сетью останется прежней, но в Вашем коде прежде всего нужно обратить внимание на структуру cisdata и pragma pack. Вообще структура этой структуры мне не нравится, ужасное выравнивание, в 64-битном окружении стоит переписать работу с ней.

Цитата(MnxVol @  28.11.2012,  09:44 Найти цитируемый пост)
Подскажите еще, с помощью чего лучше реализовать регулярные выражения на С

Ну, для этого вопроса лучше всего открыть новую тему smile 

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


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


Новичок



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

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



Еще маленький вопрос, все конечно работает, но сегодня заметил - не поступало запросов в течении часа - и программа не захотела закрывать сокет, весь час ждала запросов на этом сокете. Может как-то реализовать выход по таймауту? Я сегодня буду пытаться прикрутить многозадачность, а в таком случае это будет лишь бесконечное множество процессов))

вот так примерно пока у меня в голове многозадачность выглядит, пока правда не проверял, сегодня займусь.

Цитата

int pid;
    if ( (pid = fork()) == 0 )
    { // это потомок
    while( (read_socket( nsd, &cd, 20 )) >0 )
     if( !process_socket( nsd, &cd ) ) break;
    close(nsd);
    //exit(status);\
    exit(0);
    }
    else if (pid > 0)
    { // это предок
    //int retval;
    //wait(&retval);
    close(nsd);
    break;
    }
    else
    {
    perror("Tried to fork() a process");
    close(nsd);
    break;
    }
    while(waitpid(-1, NULL, WNOHANG) > 0);

PM MAIL   Вверх
feodorv
Дата 30.11.2012, 19:41 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


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

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



Цитата(MnxVol @  30.11.2012,  09:00 Найти цитируемый пост)
не поступало запросов в течении часа - и программа не захотела закрывать сокет, весь час ждала запросов на этом сокете.

И? Дальше-то работала?

Это нормальное поведение сервера. Не вижу причин для закрытия сокета. Зачем? 


Цитата(MnxVol @  30.11.2012,  09:00 Найти цитируемый пост)
вот так примерно пока у меня в голове многозадачность выглядит

Где-то так, только 
Цитата(MnxVol @  30.11.2012,  09:00 Найти цитируемый пост)
   while(waitpid(-1, NULL, WNOHANG) > 0);

это самоубийство)))

Не нужен waitpid: Программирование сокетов в Linux smile 


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


Новичок



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

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



Дальше работала, но у меня может быть до 1000, а может и больше, одновременных подключений, получается каждое подключение будет висеть незакрытым, рано или поздно закончатся файловые дескрипторы в системе. 
PM MAIL   Вверх
feodorv
Дата 3.12.2012, 10:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


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

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



Цитата(MnxVol @  3.12.2012,  11:24 Найти цитируемый пост)
Дальше работала, но у меня может быть до 1000, а может и больше, одновременных подключений, получается каждое подключение будет висеть незакрытым, рано или поздно закончатся файловые дескрипторы в системе.  

Честно говоря, моё терпение начинает подходить к концу))))
Цитата(feodorv @  6.11.2012,  18:39 Найти цитируемый пост)
Вот маловероятно, что циска устанавливает новое соединение на новый вопрос, скорее всего, она пользуется старым соединением.

Цитата(feodorv @  7.11.2012,  10:35 Найти цитируемый пост)
Более того, напишите грамотную программу с участием блокирующего сокета, которая бы постоянно читала бы из сокета nsd (и постоянно бы отвечала туда же). А не требовала бы от циски каждый раз после очередного таймаута переустанавливать соединение с сервером проверки.

Цитата(feodorv @  27.11.2012,  13:07 Найти цитируемый пост)
Так в том-то и дело, что не должен закрываться сокет после каждого url. Соединение действует постоянно.

Почему Вы зависли на идее, что для каждого запроса нужно открывать новый сокет? Если так действовать, то, да, рано или поздно закончатся файловые дескрипторы. Посмотрите как следует код, в каких случаях происходит переоткрытие сокета, в каких случаях используется (многократно) уже созданный и уже присоединённый сокет nsd.



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


Новичок



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

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



Извиняюсь, не понял Вас изначально до конца) А тогда если делать fork, то как я понимаю, процессы будут распараллеливаться, там тоже будет все нормально? Я как понял, по идее, в дочерней копии процесса происходит обработка, и если обработка завершена, процесс закроется, и управление вернется родительскому процессу, так?
PM MAIL   Вверх
feodorv
Дата 4.12.2012, 10:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


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

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



Цитата(MnxVol @  4.12.2012,  09:37 Найти цитируемый пост)
там тоже будет все нормально?

Будет))) Только не нужно делать так:
Цитата(MnxVol @  4.12.2012,  09:37 Найти цитируемый пост)
управление вернется родительскому процессу

Родительский процесс должен работать вне зависимости от дочернего smile 


--------------------
Напильник, велосипед, грабли и костыли - основные инструменты программиста...
PM MAIL   Вверх
Ответ в темуСоздание новой темы Создание опроса
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Сети | Следующая тема »


 




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


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

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