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

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Зависает при приёме, Winsock 
:(
    Опции темы
Alexey68
Дата 6.9.2015, 14:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Alexey
*


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

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



Здравствуйте All!
При выполнении нижеприведённой функции в цикле  (порт 80) программа может входить в вечный "ступор" дожидаясь ответа (которого может не быть):
Код

int GetResult(char ip[255], int port)
{
    WSADATA        ws;
    SOCKET        s;
    sockaddr_in    adr;
    hostent*    hn;
    char        buff [MAX_PACKET_SIZE];


//    if (WSAStartup (0x0101, &ws) != 0)
    if (WSAStartup (MAKEWORD(2,2), &ws) != 0)
    {
        // Error
        return -1;
    }

    if (INVALID_SOCKET == (s = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP) ) )
    {
        // Error
        return -1;
    }

    adr.sin_family                = AF_INET;
    adr.sin_addr.s_addr    = inet_addr(ip);
    adr.sin_port                = htons (port);

    int conn = connect (s, (sockaddr* )&adr,  sizeof (adr) );
    if (conn == SOCKET_ERROR)
    {
    return -1;
    }
    printf("Connect = %d\n", conn);
    char message[300];

    int message_size = sprintf
    (
        message,
        "GET / HTTP/1.1\r\n"
        "Host: %s:%i\r\n"
        "User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:39.0) Gecko/20100101 Firefox/39.0\r\n"
        "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"
        "Connection: keep-alive\r\n"
        "\r\n",
        ip,
        port
    );
    if (SOCKET_ERROR == send (s, message, sizeof (message_size), 0) )
    {
        // Error
        int res = WSAGetLastError ();
        return -1;
    }

    printf("ioctlsocket\n");
BOOL ctl = TRUE;
if (SOCKET_ERROR == ioctlsocket (s, FIONBIO, (unsigned long* ) &ctl))
{
// Error
int res = WSAGetLastError ();
return -1;
}
    int len, res;
    
    fd_set    read_s;
    timeval    time_out;
    time_out.tv_sec = 0;time_out.tv_usec = 500000;

    do
    {
        FD_ZERO (&read_s);
        FD_SET (s, &read_s);
        if (SOCKET_ERROR == (res = select (0, &read_s, NULL, NULL, &time_out) ) )
            return -1;
        if ((res!=0) && (FD_ISSET (s, &read_s)) )
        {
            if (SOCKET_ERROR == (len = recv (s, (char *) &buff, MAX_PACKET_SIZE, 0) ) )
            {
                int res = WSAGetLastError ();
                return -1;
            }
            for (int i = 0; i<len; i++)
                printf ("%c", buff [i]);
        }

    }
    while (len!=0);
    if (SOCKET_ERROR == closesocket (s) )
    {
        // Error
        return -1;
    }

WSACleanup();
    return 1;
    }

скорее всего при выполнении recv, каким образом можно исправить ситуацию?
С уважением, Алексей.

Это сообщение отредактировал(а) Alexey68 - 7.9.2015, 18:00
PM MAIL   Вверх
disputant
Дата 6.9.2015, 15:22 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


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

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



Как ни странно, но в принципе работает. Поэтому не стал пытаться обнаружить условия, при которых не работает :( Компилировал VC++ 2013.

Но по форме как минимум масса замечаний. 

sprintf у вас чудом не вылазит за пределы запрошенного размера. Все-таки это крайне небезопасная вещь...

message_size вы определяете, но отправляете запрос со всем мусором, указывая его размер не как message_size, а как sizeof(message).

Ну и - попробуйте делать выход не при len != 0, а при len < MAX_PACKET_SIZE - если вы считали неполный пакет, то читать вам уже больше нечего.

Хотя в сетях я и не профи, так что можете отнестись к последнему замечанию скептически...
PM MAIL   Вверх
Alexey68
Дата 6.9.2015, 15:58 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Alexey
*


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

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



Благодарю за замечания
Цитата(disputant @  6.9.2015,  15:22 Найти цитируемый пост)
Как ни странно, но в принципе работает. 

попробуйте получить данные с 50.198.70.209 или с 50.198.71.49, встречаются и другие такие же.
В данной ситуации (как я вижу) необходимо разрывать соединение если нет ответа например в течении 3 секунд. Как это сделать?

Это сообщение отредактировал(а) Alexey68 - 6.9.2015, 16:06
PM MAIL   Вверх
disputant
Дата 6.9.2015, 19:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


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

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



Цитата(Alexey68 @ 6.9.2015,  15:58)
Благодарю за замечания
Цитата(disputant @  6.9.2015,  15:22 Найти цитируемый пост)
Как ни странно, но в принципе работает. 

попробуйте получить данные с 50.198.70.209 или с 50.198.71.49, встречаются и другие такие же.
В данной ситуации (как я вижу) необходимо разрывать соединение если нет ответа например в течении 3 секунд. Как это сделать?

Вы пробовали заменить условие while(len!=0) на while(len == MAX_PACKET_SIZE)? Что после этого?
PM MAIL   Вверх
Alexey68
Дата 6.9.2015, 20:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Alexey
*


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

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



Цитата(disputant @  6.9.2015,  19:29 Найти цитируемый пост)
Вы пробовали заменить условие while(len!=0) на while(len == MAX_PACKET_SIZE)?

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


Опытный
**


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

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



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

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

Код как написан так и работает простите.... smile 

Это сообщение отредактировал(а) bass - 6.9.2015, 23:54
PM MAIL   Вверх
feodorv
Дата 7.9.2015, 04:36 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


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

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



Цитата(bass @  6.9.2015,  23:53 Найти цитируемый пост)
Код как написан так и работает простите....
С одной стороны, это верно. С другой стороны, напрасно Вы так, приведённый код написан с достаточной степенью компетентности. 


И хотя у меня самого возникла масса замечаний к коду, хотелось бы разобраться именно с возникшей ситуацией. Вот это не понятно:
Цитата(Alexey68 @  6.9.2015,  14:07 Найти цитируемый пост)
скорее всего при выполнении recv
Что значит "скорее всего"? Если лень или нет возможности воспользоваться отладчиком, то выдайте трассировочные сообщения (на консоль или в файл). Насколько сложно обрамить вызовы recv и select парой принтфов:
Код
printf( ">select\n" );
if (SOCKET_ERROR == (res = select (0, &read_s, NULL, NULL, &time_out) ) )
            return -1;
printf( "<select\n" );
и точно посмотреть, где происходит зависание? Чтобы исключить всякие "скорее всего".


Далее. Вы в подпрограмме делаете вызов WSAStartup, что само по себе не есть хороший подход, не говоря уже об отсутствии парного WSACleanup, но и аргумент вызова крайне странный:
Цитата(Alexey68 @  6.9.2015,  14:07 Найти цитируемый пост)
WSAStartup (0x0101, &ws)
Если Вы заказываете устаревшую версию библиотеки WinSocket, то она и будет подгружена к Вашему приложению. Смысла в этом мало, так как эта устаревшая версия имеет целый ряд серьёзных недостатков, в том числе в ней страдает реализация пары вызовов select/recv. Поэтому пользуйтесь новой версией библиотеки с номером релиза 0x202u (то есть MAKEWORD(2,2)).


Далее. Если используется select, смысла в блокируемом сокете в recv нет никакого. Используйте неблокируемый сокет через ioctlsocket(FIONBIO). Проверьте в этом случае наличие зависания Вашего приложения.


Если интересуют мои соображения насчет именно приведенного кода, то напишите об этом.



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


Alexey
*


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

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



to:feodorv
select возвращает 0, и соответственно происходит бесконечный цикл:
Код

do
    {
        FD_ZERO (&read_s);
        FD_SET (s, &read_s);

        if (SOCKET_ERROR == (res = select (0, &read_s, NULL, NULL, &time_out) ) )
            return -1;
//res = 0

        if ((res!=0) && (FD_ISSET (s, &read_s)) )//это условие не выполняется и...
        {

            if (SOCKET_ERROR == (len = recv (s, (char *) &buff, MAX_PACKET_SIZE, 0) ) )
            {
                int res = WSAGetLastError ();
                return -1;
            }
            for (int i = 0; i<len; i++)
                printf ("%c", buff [i]);
        }

    }
    while (len!=0);

проинициализировал версией 2 (MAKEWORD(2,2)), попробовал неблокируемый сокет через ioctlsocket(FIONBIO), вроде без изменений...


Это сообщение отредактировал(а) Alexey68 - 7.9.2015, 17:58
PM MAIL   Вверх
Alexey68
Дата 7.9.2015, 22:06 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Alexey
*


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

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



Цитата(bass @  6.9.2015,  23:53 Найти цитируемый пост)
В действительности приходит ответ или нет ????

после стандартного handshake (установка содинения) и отправки send (GET) приходит пакет с флагом аск и на этом от удалённого пк в ответ тишина...

Это сообщение отредактировал(а) Alexey68 - 7.9.2015, 23:30
PM MAIL   Вверх
feodorv
Дата 8.9.2015, 03:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


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

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



Цитата(Alexey68 @  7.9.2015,  17:57 Найти цитируемый пост)
вроде без изменений

Спасибо, понятно. 


Цитата(Alexey68 @  7.9.2015,  22:06 Найти цитируемый пост)
после стандартного handshake (установка содинения) и отправки send (GET) приходит пакет с флагом аск и на этом от удалённого пк в ответ тишина...

Так какой у Вас таймаут в итоге?


Цитата(Alexey68 @  7.9.2015,  17:57 Найти цитируемый пост)
select возвращает 0, и соответственно происходит бесконечный цикл

С len, конечно, нехороший цикл нарисовался. Так как len нужно инициализировать чем-нибудь, если до recv дело не доходит.


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


Alexey
*


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

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



Цитата(feodorv @  8.9.2015,  03:28 Найти цитируемый пост)
Так какой у Вас таймаут в итоге?

ждал 15 минут, потом бросил...

PM MAIL   Вверх
bass
Дата 8.9.2015, 23:34 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


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

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



https://msdn.microsoft.com/en-us/library/wi...v=vs.85%29.aspx
Может вот это немного поможет, прием вставите в отдельном потоке. 

http://www.pcausa.com/Utilities/pcattcp.htm
Вот исходник нормально написанного клиента VC.
http://www.pcausa.com/Utilities/ttcpdown1.htm



Это сообщение отредактировал(а) bass - 9.9.2015, 00:17
PM MAIL   Вверх
feodorv
Дата 9.9.2015, 03:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


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

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



Цитата(Alexey68 @  6.9.2015,  15:58 Найти цитируемый пост)
В данной ситуации (как я вижу) необходимо разрывать соединение если нет ответа например в течении 3 секунд. Как это сделать?

Тогда указывайте таймаут для select'а в 3 секунды. Если select вернёт 0, то выходите из цикла с отрицательным результатом.


Цитата(bass @  8.9.2015,  23:34 Найти цитируемый пост)
Вот исходник нормально написанного клиента VC.

Нормально в WinSockAPI всё же пользоваться WSAEventSelect() и Wait-функциями. И всё в одном потоке.


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


Эксперт
****


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

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



Цитата(Alexey68 @  6.9.2015,  14:07 Найти цитируемый пост)
программа может входить в вечный "ступор

в 86 строке
Цитата(Alexey68 @  6.9.2015,  14:07 Найти цитируемый пост)
 while (len!=0);

вечный цикл практически гарантирован

вероятно точка с зпт лишняя

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


 




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


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

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