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

Поиск:

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


Новичок



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

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



Здравствуйте! Подскажите пожалуйста, в чем может быть проблема. Есть задание написать серверное приложение на Си, которое будет выступать в качестве сервера N2H2 для маршрутизатора Cisco. Смысл данного сервера - ограничение доступа пользователей к ресурсам, указанным в файле banners.txt. Есть рабочее приложение на perl (переработанное из неработающего приложения вот тут: http://www.opennet.ru/base/cisco/cisco_banner.txt.html). Нужно было написать  то же самое, но на Си. В итоге программа была написана, и программа работает, но до определенного момента, пока не заканчиваются файловые дескрипторы в моем debian (так как для завершения сокета в цикле используется функция shutdown, вместо close - при использовании close cisco не дает открыть никакую страницу, интернет попросту висит). Извиняюсь за много букв, но я постарался как можно кратко объяснить суть проблемы. спасибо за посильную помощь. Внизу привожу код на Си.

Код

#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>
#include <errno.h>
#include <malloc.h>
#define PORT 4005
#define MAXSEGSIZE 65464
#define PKTSIZE (MAXSEGSIZE + 4)
#define BACKLOG 4

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

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

    return 1;
}

int main()
{    
    short const REQ_REQUEST = 0x0200;
    short const REQ_ALIVE = 0x0203;
    short const RES_ACCEPT = 0x0002;
    short const RES_ALIVE = 0x0302;    
    short const RES_DENIED = 0x0102;
    
    struct cisdata {
        short id;
        int number;
        int ipcli;
        int ipserv;
        short lenurl;
        short lenname;
        char *url;        
    } __attribute__((packed));
    
    char *redirect = "http://therules.ru/";
    char buf[PKTSIZE]; /* used for incoming dir name, and outgoing data */    
    char sfile[20][20];
    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;
    struct timeval to;
    struct cisdata cd;
    fd_set readf, writef;
    bzero(&cd, sizeof (cd));
        
    FILE * fo;
    if ((fo = fopen("banners.txt", "rt")) == 0)
    {    
        printf("Coudn't open file 'banners.txt'\n");
    }
    i=0;
    while(!feof(fo))
    {    
        fgets(sfile[i], 20, fo);
        //printf("%s", sfile[i]);
        i++;
    }
    printf("%s", sfile[1]);
    printf("Nstring=%d\n", i);
    fclose(fo);

    /* get an internet domain socket */
        if ((sd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
    {
        perror("socket");
        return 1;
    }
    printf ("SD %d\n", sd);

    if ((x = fcntl(sd, F_GETFL, 0)) == -1 ||        
        fcntl(sd, F_SETFL, x | O_NONBLOCK) == -1)
        perror("fcntl");
        
bzero(&sa, sizeof (sa));
    sa.sin_family = AF_INET;
    sa.sin_addr.s_addr = htonl(INADDR_ANY);
    sa.sin_port = htons(PORT);
    //sa.sin_len = sizeof(sa);        

    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)
    {
        perror("listen");
        return 4;            
    }
    printf("listen= %d\n",t);
        
    while (1)
{        
        FD_ZERO(&readf);
        FD_ZERO(&writef);
        FD_SET(sd, &readf);
        FD_SET(sd, &writef);
        to.tv_sec = 1;
        to.tv_usec = 0;
            
        if ((rd = select(sd+1, &readf, &writef, (fd_set*)0, &to)) == -1)
        {
            perror("select");
            return 3;
        }

        if ((nsd = accept(sd, (struct sockaddr *) &sa, &addrlen)) < 0)
            {
                perror("Accept");
                continue;
            }
        
        printf("Nsd= %d\n", nsd);    
        
        if((read_socket(nsd, &cd.id, 6)) < 0)
        {
            perror("Read Nsd:");
            return 6;
        }                    
        printf("Id of request: 0x%02x\n", ntohs(cd.id));
        printf("Number of request: 0x%04x\n", ntohl(cd.number));        
        
        if((ntohs(cd.id)) == REQ_ALIVE) 
        {    
            cd.id = RES_ALIVE;
            cd.ipcli = 0;
            if ((write(nsd, &cd, 10)) < 0)
            {
                perror("Write");
                return 7;
            }
            printf("Send to cisco RES_ALIVE\n");
            close(nsd);
        }    
            else
            {    
                if ((ntohs(cd.id)) == REQ_REQUEST)
                {
                    if ((read_socket(nsd, &cd.ipcli, 12)) < 0)
                    {
                        perror("Read Nsd:");
                        return 6;
                    }
                    cd.url = malloc(ntohs(cd.lenurl));
                    if ((read_socket(nsd, cd.url, ntohs(cd.lenurl))) < 0)
                    {
                        perror("Read Nsd:");
                        return 6;
                    }
                    //printf("Ip client: 0x%04x\n", ntohl(cd.ipcli));
                    //printf("Ip serv: 0x%04x\n", ntohl(cd.ipserv));
                    //printf("Length of url: 0x%02x\n", ntohs(cd.lenurl));
                    //printf("Lenght of name 0x%02x\n", ntohs(cd.lenname));                    
                    printf("Url: %s\n", cd.url);
                    //printf("Sfile=: %s\n", sfile[1]);
                    
                    if ((strcmp(cd.url, sfile[1])) == 0)                        
                    {
                        cd.id = htons(RES_DENIED);
                        printf("Len= %x\n", len);
                        write(nsd, &cd.id, 6);
                        write(nsd, &len, 4);
                        write(nsd, redirect, ntohl(len));
                        printf("Send to cisco RES_DENIED\n");
                    }            
                        else
                        {
                            cd.id = htons(RES_ACCEPT);
                            cd.ipcli = 0;
                            if ((write(nsd, &cd, 10)) < 0)
                            {
                                perror("Write");
                                return 7;
                            }
                            printf("Send to cisco RES_ACCEPT\n");                            
                        }
                    free(cd.url);    
                    shutdown(nsd, SHUT_RDWR);                                    
                }                                                        
                
            }
            
        //close(nsd);        
                                    
    }
close(sd);
close(nsd);        
}


В файле - код на perl.

Это сообщение отредактировал(а) Олег2005 - 22.10.2012, 14:23
PM MAIL   Вверх
boostcoder
Дата 22.10.2012, 15:54 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


pattern`щик
****


Профиль
Группа: Завсегдатай
Сообщений: 5458
Регистрация: 1.4.2010

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



Цитата(MnxVol @  22.10.2012,  13:27 Найти цитируемый пост)
для завершения сокета в цикле используется функция shutdown, вместо close - при использовании close cisco не дает открыть никакую страницу

попробуй после shutdown вызывать close.

PM WWW   Вверх
feodorv
Дата 22.10.2012, 18:49 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


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

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



А я так понял, что сокет вообще не нужно закрывать, ни с помощью shutdown, ни с помощью close.
Просто нужно висеть на этом сокете и ждать прихода пакета. На каждый пришедший пакет слать ответ:
Код

while( recv(...) > 0 )
  send(...);



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


Новичок



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

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



Цитата

попробуй после shutdown вызывать close.

пробовал.

Цитата

А я так понял, что сокет вообще не нужно закрывать, ни с помощью shutdown, ни с помощью close.
Просто нужно висеть на этом сокете и ждать прихода пакета. На каждый пришедший пакет слать ответ:


если не использую ни shutdown ни close - сначала работает, но медленно, потом виснет все. Но Nsd все равно постоянно растет, и если потом превысит предельно допустимое для системы - все упадет - too many open files. Это предположение. не проверял. Nsd при проверке доросло только до 6, дальше на запросы перестал отвечать сервер. правда, я не менял условия цикла, я только убрал shutdown.
PM MAIL   Вверх
MnxVol
Дата 6.11.2012, 11:54 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



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

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



Кстати, в последнее время замечаю, что если я вместо shutdown вызываю close, то работает, но нестабильно, во-первых очень медленно, во-вторых, недолго))) а если вызываю close после shutdown, происходит то же самое, работает медленно и нестабильно. Но значение Nsd меняется и в том и в другом случае
PM MAIL   Вверх
feodorv
Дата 6.11.2012, 11:57 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


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

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



Только что обратил внимание, что код на перле использует UDP.


Цитата(MnxVol @  6.11.2012,  12:26 Найти цитируемый пост)
если не использую ни shutdown ни close - сначала работает, но медленно, потом виснет все.

Ну правильно. Схема-то какая должна быть:
Код

while( 1 )
{
        if ((nsd = accept(sd, (struct sockaddr *) &sa, &addrlen)) < 0) ...

        while( recv( nsd...) > 0 )
           send( nsd...);

        close( nsd );
}

для одного клиента.

Для нескольких нужно изыскивать более изощрённую схему.


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


pattern`щик
****


Профиль
Группа: Завсегдатай
Сообщений: 5458
Регистрация: 1.4.2010

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



именно по этому я и не приемлю использование системного API, а предпочитаю концентрироваться на решении задачи.

PM WWW   Вверх
MnxVol
Дата 6.11.2012, 16:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



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

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



Цитата

Ну правильно. Схема-то какая должна быть:
Без подсветки
1:
2:
3:
4:
5:
6:
7:
8:
9:
while( 1 )
{
        if ((nsd = accept(sd, (struct sockaddr *) &sa, &addrlen)) < 0) ...
        while( recv( nsd...) > 0 )
           send( nsd...);
        close( nsd );
}

для одного клиента.



а разве суть неблокирующих сокетов не в одновременном ожидании больше чем одного подключения? код на perl использует udp, вы правы, пришлось поменять код на tcp для достижения цели, видимо, cisco поменяли протокол. При использовании udp не работает, tcp - все прекрасно. 
В том то и дело, что дана установка использовать системные API. Не могу отойти от нее. Подскажите тогда в какую сторону сейчас смотреть.
PM MAIL   Вверх
feodorv
Дата 6.11.2012, 17:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


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

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



Цитата(MnxVol @  6.11.2012,  17:16 Найти цитируемый пост)
При использовании udp не работает, tcp - все прекрасно. 

Это всё прописывается на циске. Смотрите соответствующий мануал.

Цитата(MnxVol @  6.11.2012,  17:16 Найти цитируемый пост)
а разве суть неблокирующих сокетов не в одновременном ожидании больше чем одного подключения? 

Простите, но в Вашем коде никакой сути неблокирующих сокетов не просматривается. ЗаводИте список клиентов, список сокетов и т.д. Какие проблемы?

Однако от этого схема не изменится. Вот маловероятно, что циска устанавливает новое соединение на новый вопрос, скорее всего, она пользуется старым соединением. И именно это уже установленное соединение Вы бросаете на произвол судьбы - ни close, ни чтения/записи (ну, кроме одного единственного). Вы пробовали на этом же соединении читать следующие записи? Прочли запись, посмотрели в базу данных, послали ответ. И всё по новой. Соединение закрылось (например, при ребуте циски), возвращаемся к accept.

PS Для данной задачи UDP значительно проще. Поищете команды циски, которые прописывают сервер проверки и протокол обмена. Настоятельно советую)))


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


Опытный
**


Профиль
Группа: Завсегдатай
Сообщений: 421
Регистрация: 26.5.2005
Где: Рига Латвия

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



Цитата(MnxVol @  6.11.2012,  15:16 Найти цитируемый пост)
а разве суть неблокирующих сокетов не в одновременном ожидании больше чем одного подключения?

Попробую изложить свое представление о "неблокирующих" и других сокетах.
Блокирующих, неблокирующих или асинхронных сокетов в природе не существует.
Сокет - это сложный конгломерат-абстракция.
В простоте - это как временный рабочий файл - так он и трактуется в *никсах.
Блокируют ход программы - операции ввода/вывода, выполняющиеся с этим "псевдорабочим временным файлом".
Потому операция connect() - это типичная блокирующая операция.
Типично блокирующей операцией я вляется и accept().
Что означает тогда к примеру неблокирующий connect()?
Это означает, что типично блокирующую функцию запускают в несвойственном ей режиме, который не является для нее естественным. Потому connect() и отдает сразу управление на следующую команду программы,  возвращая SOS - EWOULDBLOCK.
И теперь задача дальнейшего кода - все проверить, как же все-таки реально через некоторое время завершится connect().
Вот типичное поведение разных функций, для которых проставлен режим неблокирования:
В этом случае работа этого сокета для разных сокетных операций осуществляется следующим образом:
·    accept() завершает работу сразу же с ошибкой EWOULDBLOCK;
·    connect() завершает работу сразу же с ошибкой EINPROGRESS; вызывающий процесс продолжит свое выполнение в любом случае, даже если на установку соединения потребуется несколько десятков секунд;
·    recv(), read() или recvfrom() возвратят -1 (с применением флагов FIONBIO или FNDELAY функции ioctl()) или 0 (O_NDELAY- функция fcntl()) при отсутствии считываемых данных; выставляется ошибка EWOULDBLOCK или EAGAIN. 
Потому ваш вопрос так сказать повисает в воздухе.
Подключения ждет модуль TCP, и в ходящие подключения буферизируются в очереди прослушивающего сокета.
accept() висит (блокирует) до тех пор, пока в очереди полностью установленных соединений прослушивающего сокета не появится очередной готовый запрос на обслуживание.
Как только запрос появился - accept() cразу создает новый присоединенный сокет и возвращает управление на следующую команду.
Насчет асинхронности.
Есть функции, которые по жизни сделаны неблокирующими. Не нужны ни ioctl или что еще.
Этот режим для них родной......



Это сообщение отредактировал(а) Олег2005 - 6.11.2012, 18:14
PM MAIL WWW MSN   Вверх
feodorv
Дата 6.11.2012, 18:58 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


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

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



Цитата(feodorv @  6.11.2012,  18:39 Найти цитируемый пост)
Поищете команды циски, которые прописывают сервер проверки и протокол обмена.

Вроде бы это оно smile 


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


Новичок



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

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



Цитата

Поищете команды циски, которые прописывают сервер проверки и протокол обмена.

Вроде бы это оно   


не совсем, у меня команда ip urlfilter server vendor n2h2 10.10.10.10 - и из допустимых параметров дальше 
outside  Specify if the server is on the outside network
  port     Specify URL filter server port number
  retrans  Specify retransmission count
  timeout  Specify retransmission timeout
  vrf      Specify VRF
  <cr>
так что я предположил что по крайней мере в моем случае буду использовать tcp.

Олег2005, спасибо за подробное объяснение. 
feodorv, если я вас правильно понял, мне лучше поменять концепцию с неблокирующих на другие?) попробую. Спасибо, чуть позже отчитаюсь о новом варианте.
PM MAIL   Вверх
feodorv
Дата 7.11.2012, 09:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


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

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



Цитата(MnxVol @  7.11.2012,  08:50 Найти цитируемый пост)
feodorv, если я вас правильно понял, мне лучше поменять концепцию с неблокирующих на другие?) 

Я хоть слово сказал против неблокирующих сокетов? Я двумя руками за. Было бы у меня 10 рук, был бы десятью за.
Но ведь надо понимать, что это за зверь - неблокирующий сокет. Вот у Вас в коде применяется recv на неблокирующем сокете. Правильно применяется? Нет.

Потому что при работе с неблокирующими сокетами нужно ожидать наступления некоторого события, для чего очень важен вызов select. С помощью этого вызова можно ожидать наступления сразу массы событий от массы же сокетов (и файловых дескрипторов). Но наступления события нужно ожидать (вызовы с блокирующим сокетом просто не вернут управление в программу, пока не наступит ожидаемое событие). У Вас в коде где ожидание события "готовы данные для чтения на таком-то сокете"??? Его нет. Я не против блокирующих сокетов, я против неправильного их использования)))

Неблокирующие сокеты позволяют работать сразу с несколькими клиентами, позволяя при этом ожидать подключения новых, в одном единственном потоке. Если у Вас такой клиент один (та самая циска), то смысла в использовании неблокирующих сокетов нет. Да, в этой ситуации достаточно одного блокирующего (nsd). Более того, напишите грамотную программу с участием блокирующего сокета, которая бы постоянно читала бы из сокета nsd (и постоянно бы отвечала туда же). А не требовала бы от циски каждый раз после очередного таймаута переустанавливать соединение с сервером проверки.


Цитата(MnxVol @  7.11.2012,  08:50 Найти цитируемый пост)
не совсем, у меня команда ip urlfilter server vendor n2h2 10.10.10.10 - и из допустимых параметров дальше 

Очень жаль. 


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


Эксперт
****


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

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



Ну, я вижу код приблизительно таким:

Код

// #pragma pack ??? sizeof(struct client_data) == 20

struct client_data
{
   unsigned short id;
   unsigned int num;
   unsigned int ip_or_len;
   unsigned int server_ip;
   unsigned short url_len;
   unsigned short name_len;
   unsigned char data[2];
};

while( 1 )
{
   struct client_data cd;  
 
   if ((nsd = accept(sd, (struct sockaddr *) &sa, &addrlen)) < 0) 
   {
       printf( "General error on accent()\n" );
       break;
   }

   while( read_socket( nsd, &cd, 20) > 0 )
       if( !process_socket( nsd, &cd) ) break;

   close( nsd );
}


А уже process_socket анализирует пришедшие 20 байт, если нужно, дочитывает из сокета оставшееся и посылает ответ:

Код

int process_socket( int s, struct client_data *cd)
{
   switch(  ntohs( cd->id ) )
   {
      case REQ_ALIVE:
         cd->id = htons(RES_ALIVE);
         cd->ip_or_len = 0;
         if( write_socket( s, cd, 10) < 0 ) perror("Write");
         return 1;

      case REQ_REQUEST:
      {
         char *data, *url, *name, return_url[1024];
         int return_url_len;
         unsigned int num;
         unsigned short url_len = ntohs(cd->url_len), name_len = ntohs(cd->name_len);

         if( url_len == 0 || name_len == 0 )
         {
            printf( "data error\n" );
            return 0;
         }
         if( (data = malloc( url_len + name_len )) == NULL ) ...;
         data[0] = cd->data[0]; // 2 байта уже прочли
         data[1] = cd->data[1];

         // дочитываем (читаем ровно столько, сколько запрашиваем, не больше, не меньше)
         if( (url_len + name_len) > 2 && read_socket( s, &data[2], url_len + name_len - 2) < 0 )
         {
             free( data );
             return 0;
         }
         url = &data[0];
         name = &data[url_len];

         // проверим, что на концах есть символ '\0'
         if( url[url_len-1] != '\0' || name[name_len-1] != '\0' )
         {
             printf( "data error\n" );
             free( data );
             return 0;
         }
         if( check_url( url, name, &return_url[10], sizeof(return_url)-10) ) // разрешаем?
         {
             cd->id = htons(RES_ACCEPT);
             cd->ip_or_length = 0;
             if( write_socket( s, cd, 10) < 0 ) perror("Write");
             free( data );
             return 1;
         }

         // составим отрицательный ответ
         num = cd->num;
         cd = (struct client_data *) return_url;  // будем писать в сокет сразу одной пачкой байт
         cd->id = htons(RES_DENIED);
         cd->num = num;
         return_url_len = strlen( &return_url[10] ) + 1;
         cd->ip_or_len = htonl( return_url_len );
         if( write_socket( s, cd, 10+return_url_len) < 0 ) perror("Write");
         free( data );
         return 1;
      }

      default:
          printf( "Unknown query id %.4x\n", ntohs( cd->id ));
          return 0;
   }
}


при очень важном условии: читается из сокета ровно столько, сколько запрошено, и пишется туда ровно столько, сколько запрошено (т.е. если прочитано меньше, надо дочитывать, если записано меньше, нужно дозаписывать остаток).

Предварительно нужно убедиться, что размер структуры client_data ровно 20 байт, если нет - воспользоваться #pragma pack

Это сообщение отредактировал(а) feodorv - 16.11.2012, 16:46


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


Новичок



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

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



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


 




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


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

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