HTTP Winsock
От: my_problem  
Дата: 22.11.11 18:39
Оценка:
Здравствуйте.

Мучаюсь над проэктом "полное перенаправление HTTP-запроса (клиент-сервер-клиент)" — фактически аналог прокси сервера (просьба не предлагать переделать через WinInet/сделать прокси) — задача должна быть решена конкретно через Winsock и именно так. Сокеты — блокируемые, мультипоточно.

Значит, тестирую так: есть любой сайт, к примеру тестировал на http://www.gotovim.ru/ Прописываю в конфиг его домен/айпи, биндю 80 порт на локалхосте и в файл hosts прописываю 127.0.0.1 www.gotovim.ru (соответственно все запросы, которые браузер посылает на домен www.gotovim.ru приходят на 80 порт loopback ко мне в приложение, и дальше я их и обрабатываю). Обработка — пока что : получение запроса от браузера -> пересылка без изменений на сервер -> получение ответа от сервера -> без изменения передача обратно в браузер (клиенту) — то есть вообще без изменений пересылка: браузер — приложение — сервер — приложение — браузер. На данный момент это все, что требуется.
Глобальные переменные, относящиеся к коду ниже:

// Variables
WSADATA wsaData; // Winsock object
#define LISTEN_PORT "80" // Listening port
#define DEFAULT_BUFLEN 10240 // Size of buffer
int nClients = 0;//Clients


Код инициализации Winsock, байнд порта, итп — стандартный (MSDN), заканчивается мультипоточным accept:
while(ClientSocket = accept(ListenSocket, (sockaddr *)&client_addr, &client_addr_size))
{
    if (ClientSocket == INVALID_SOCKET)
    {
#ifdef DEBUG
        sprintf_s(errormsg,"accept failed with error: %d\n", WSAGetLastError());
        Errorlog(errormsg);
#endif
        //TODO - report error
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }
    else
    { //Multithread clients accept
        DWORD thID;
        CreateThread(NULL, NULL, HandShake, &ClientSocket, NULL, &thID);
        nClients++;
        WaitForSingleObject(0, 50000);
    }
}


в функции HandShake...
// receiving the request
iResult = recv(ClientSocket, recvbuf, DEFAULT_BUFLEN, 0);
if (iResult > 0)
{ // Beginning dialog
    recvbuf[iResult] = '\0';
#ifdef DEBUG
    Errorlog(recvbuf);
    Errorlog("\n-r-\n");
#endif


... далее идёт куча ненужного кода, который пока вообще закомментирован и "непереводимая игра слов на местном диалекте" (с) ....

int isResult3 = 0;
isResult3 = send(fSocketOut, recvbuf, iResult, 0);
if(isResult3)
{
#ifdef DEBUG
    Errorlog(recvbuf);
    Errorlog("\n-s-\n");
#endif
    do
    {
        iResult = recv(fSocketOut, recvbuf, sizeof(recvbuf), 0);
#ifdef DEBUG
        Errorlog(recvbuf);
        Errorlog("\n-r-\n");
#endif
        int isResult2 = 0;
        unsigned int idx = 0;
        unsigned long left = iResult;
        while (left > 0)
        {
            isResult2 = send(ClientSocket, &recvbuf[idx], left, 0);
            left = left - isResult2;
            idx = idx + isResult2;
        }

        int errt;
        errt = WSAGetLastError();//для отладки
#ifdef DEBUG
        Errorlog(recvbuf);
        Errorlog("\n-s-\n");
#endif
    } while (iResult != 0 || iResult != -1 || strcmp(recvbuf, "") != 0);
}

ну и ниже обработка если что-то не то с сокетом (не соединиться и прочее)

else
{
#ifdef DEBUG
    Errorlog("Connect fSocketOut failed\n\n");
#endif
    nClients--;
    closesocket(ClientSocket);
    ExitThread(0);
}


Везде использую closesocket по завершению, Значит fSocketOut — сокет, через который коннекчусь к реальному домену, сокет ClientSocket — сокет, через который браузер коннектится ко мне посылая запрос (ну и соответсвенно получает от меня ответ).

Проблема. HTML страница (текст) грузится без проблем. Картинок почти нет (буквально пару баннеров). Когда пробую отдельно загрузить картинку — грузится.
Уже много кружек кофе и гугла прошло, а воз и ныне там.
Какие ошибки были замечены при отладке:

1. Бывает что объем полученных recv данных больше, чем объём данных, переданных send (вот только сегодня узнал, что функция send оказывается может отправить и не весь буффер). Как я понял из той же отладки, цикл
        while (left > 0)
        {
            isResult2 = send(ClientSocket, &recvbuf[idx], left, 0);
            left = left - isResult2;
            idx = idx + isResult2;
        }


это решил. Причем информацию про это нашёл http://www.rsdn.ru/article/files/progs/inetd.xml
Автор(ы): Александр Пристенский
Дата: 12.07.2005
Пример реализации inetd для windows представляет собой многопоточный сервер, запускающий дочерние процессы (консольные приложения) ввод-вывод которых перенаправляется на сокет.
вот тут, за что автору огромное спасибо.

2. Иногда в цикле
do{...} while (iResult != 0 || iResult != -1 || strcmp(recvbuf, "") != 0);
при iResult == 0 из цикла не выходит, а продолжает, равно как и при iResult == -1 и strcmp(recvbuf, "") == 0
Также после этого isResult2 часто возвращает -1.

3. Страница (именно текст) — грузится полностью, но в браузере "загрузка" не заканчивается — написано "ожидание ответа от www.gotovim.ru", а программа при этом уходит в recv который ничего не получает, а, recv, насколько мне известно — функция блокируемая. При этом в лог постоянно пишется пустой буффер recv + send.

4. Самая главная ) как сделать так, чтобы всё работало?

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

Код вероятно довольно кривой, но как умею... Рабочего решения не прошу, если не затруднит — подскажите, куда копать и где я ошибаюсь (не предлагайте бросить программирование )?

P.S. Когда открываю небольшую относительно страницу — все грузится и нормально работает.

23.11.11 00:41: Перенесено модератором из 'C/C++' — Кодт
http redirect winsock vc
Re: HTTP Winsock
От: Кодт Россия  
Дата: 22.11.11 19:19
Оценка:
Здравствуйте, my_problem, Вы писали:

_>2. Иногда в цикле
do{...} while (iResult != 0 || iResult != -1 || strcmp(recvbuf, "") != 0);
при iResult == 0 из цикла не выходит, а продолжает, равно как и при iResult == -1 и strcmp(recvbuf, "") == 0

_>Также после этого isResult2 часто возвращает -1.

Условие продолжения всегда истинно.
Очевидно, что iResult не может быть 0 и -1 одновременно, чтобы обе проверки дали false.
Наверное, имелось в виду && вместо ||

Может быть, именно этот зависающий цикл и мешает нормальной работе?
Перекуём баги на фичи!
Re[2]: HTTP Winsock
От: my_problem  
Дата: 22.11.11 20:19
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, my_problem, Вы писали:


_>>2. Иногда в цикле
do{...} while (iResult != 0 || iResult != -1 || strcmp(recvbuf, "") != 0);
при iResult == 0 из цикла не выходит, а продолжает, равно как и при iResult == -1 и strcmp(recvbuf, "") == 0

_>>Также после этого isResult2 часто возвращает -1.

К>Условие продолжения всегда истинно.

К>Очевидно, что iResult не может быть 0 и -1 одновременно, чтобы обе проверки дали false.
К>Наверное, имелось в виду && вместо ||

К>Может быть, именно этот зависающий цикл и мешает нормальной работе?


Вот я "косорукая свинья" (с) ну да, именно это я и хотел написать, спасибо за то, что заметили. исправил (пока что) на
} while (iResult != 0);

(потом добавлю -1, сейчас просто разобраться хочу), теперь стало в одном лучше, в другом хуже Значит факт такой, что картинки начали появляться (спасибо за помощь ещё раз). Появилась другая проблема. Зависает на 60+- секунд после загрузки первых потоков (есть огромное подозрение, что ровно на 65 — так как это конкретная цифра Time-Out на этом сайте), и после 65 секунд некоторые потоки завершаются, и начинается несколько новых (4-5), в каждом из которых запрашивается какая-то ещё не загруженная деталь вёб-страницы (картинка, итд итп). Но, это же сколько можно ждать загрузки сайта ва таком случае... Каждый тайм-аут — так и жизнь пройдет, пока загрузится. Изза этого после первого цикла вот такого (загрузка + 65 секунд ожидания + загрузка) — выходит не полностью html страница, и невооруженным глазом видно, что css ещё не подгружен (странно — он же в
<head>

прописан), контент выглядит криво. Прикладываю лог такого вот цикла загрузки с тайм-аутами (то, что пишет программа собственно) — http://files.rsdn.ru/99135/_debug.txt . Конечно бинарные данные там не разобрать, но хедеры все видны.

Ах, да. в логе "No request has been received" — это вот в этом месте:

    // receiving the request
    iResult = recv(ClientSocket, recvbuf, DEFAULT_BUFLEN, 0);
    if (iResult > 0)
    { // Beginning dialog
...
... //весь код, что выше
...
    }
    else if (iResult == 0)
    {
#ifdef DEBUG
            sprintf_s(errormsg,"No request has been received\n\n");
            Errorlog(errormsg);
#endif
            nClients--;
            closesocket(ClientSocket);
            ExitThread(0);
    }


то есть фактически завершение потоков.

Благодарю за помощь. Попробую подождать очень долго, и выяснить, загрузит он страницу хотя бы через час или нет.
Re: HTTP Winsock
От: okman Беларусь https://searchinform.ru/
Дата: 23.11.11 12:24
Оценка: 4 (1)
Здравствуйте, my_problem, Вы писали:

_>Мучаюсь над проэктом "полное перенаправление HTTP-запроса (клиент-сервер-клиент)" — фактически аналог прокси сервера (просьба не предлагать переделать через WinInet/сделать прокси) — задача должна быть решена конкретно через Winsock и именно так. Сокеты — блокируемые, мультипоточно.


_>Значит, тестирую так: есть любой сайт, к примеру тестировал на http://www.gotovim.ru/ Прописываю в конфиг его домен/айпи, биндю 80 порт на локалхосте и в файл hosts прописываю 127.0.0.1 www.gotovim.ru (соответственно все запросы, которые браузер посылает на домен www.gotovim.ru приходят на 80 порт loopback ко мне в приложение, и дальше я их и обрабатываю). Обработка — пока что : получение запроса от браузера -> пересылка без изменений на сервер -> получение ответа от сервера -> без изменения передача обратно в браузер (клиенту) — то есть вообще без изменений пересылка: браузер — приложение — сервер — приложение — браузер. На данный момент это все, что требуется.


_>Проблема. HTML страница (текст) грузится без проблем. Картинок почти нет (буквально пару баннеров). Когда пробую отдельно загрузить картинку — грузится.

_>Уже много кружек кофе и гугла прошло, а воз и ныне там.
_>Какие ошибки были замечены при отладке:

_>Начал качать исходники браузера, чтобы посмотреть как там реализован цикл "общения" клиент-сервер-клиент, однако исходники очень большие и качаться будут не быстро, судя по всему.


_>Код вероятно довольно кривой, но как умею... Рабочего решения не прошу, если не затруднит — подскажите, куда копать и где я ошибаюсь (не предлагайте бросить программирование )?


Первым делом рекомендую скачать и прочесть RFC 1945 (HTTP/1.0) и RFC 2616 (HTTP/1.1).
Материал может показаться скучным, но его не так много и читается и впрямь легко, особенно
когда есть практический интерес. Список неплохих книг по HTTP я приводил здесь
Автор: okman
Дата: 15.07.11
.
Начал с HTTP, поскольку это — основа, без ее хорошего знания у Вас все время что-то
будет падать или зависать в бесконечном цикле. Ну и без понимания работы TCP/IP тоже никуда.

По реализации.
HTTP — асинхронный протокол. Несмотря на роли "клиент" и "сервер", запросы и ответы не
сихнронизируются друг с другом, хотя порядок доставки сохраняется. Более того, браузеры обычно
создают два и более соединений с одним веб-сервером — по первому скачивают контент, по
второму получают какой-нибудь favicon. Причем доподлинно нельзя знать, какие данные
пойдут первыми по сети. Если делать прокси, нужно научить его работать полностью асинхронно,
чтобы одни потоки не стопорились другими. Тогда все будет работать.

Я делал точно такое же перенаправление трафика в одном продукте коммерческого уровня,
только вместо hosts использовал "ядерные" технологии перехвата — в результате исходящие
коннекты попадали на локальный веб-сервер, а от него уходили во внешний мир, отфильтрованными.
Входящий трафик, само собой, тоже успешно фильтровался.

Принцип работы следующий — в центре системы находится порт завершения ввода-вывода, сокет,
находящийся в режиме прослушки, а также пул рабочих потоков.
Как только приходит коннект (это браузер, к примеру, лезет на rsdn.ru), создается
соединение с удаленным узлом, и оба новых сокета (первый нам вернула функция accept,
второй — функция connect) помещаются в общую структуру, которая отдается на обработку
свободному потоку. Дальше все просто — на первом сокете вызывается WSARecv, полученные
данные отдаются второму сокету через WSASend. То же самое происходит в обратном направлении —
получается полноценный дуплексный обмен данными и никто друг другу не мешает.
Когда одна из сторон размыкает или разрывает соединение, второй отправляется shutdown.
То есть, логика завершения связи также полностью поддерживается.
Повторю — все происходит полностью асинхронно, WSASend вызывается в одном потоке, завершиться
может в другом, результаты ее работы обработаться в третьем и т.п. В этом смысл системы.

Будьте готовы к тому, что далеко не все реализации веб-серверов и браузеров следуют
рекомендациям HTTP и к тому, что придется делать всякие подпорки, не очень красивые.
Например, в HTTP/1.1 принято, что если в ответе сервера не указана длина (Content-Length),
то признаком конца сообщения будет размыкание соединения. Однако куча серверов не делают так —
они отдают HTML-контент, в заголовках HTTP прописывают даже "Connection: close", однако
соединение оставляют открытым. Для интернет-браузера это не является проблемой, так как,
получив, скажем, XML, он в состоянии определить его конец и визуально оповестить юзера,
что содержимое загрузилось. Для прокси-серверов все сложнее — потому что тоже приходится,
как и браузер, читать контент, а тут и вопросы сжатия (gzip/deflate), и chunked-encoding,
и еще много всего. Если самому рвать соединение, то это может нарушить совместимость с
нормальными веб-серверами, не говоря уже о проблеме с TIME_WAIT, когда в этом состоянии
окажется слишком много соединений и нельзя будет вообще никуда подключаться.

В общем, работа не простая предстоит, я бы посоветовал основательно подготовиться.
Re[2]: HTTP Winsock
От: my_problem  
Дата: 23.11.11 12:53
Оценка:
Здравствуйте, okman, Вы писали:

O>Здравствуйте, my_problem, Вы писали:


_>>Мучаюсь над проэктом "полное перенаправление HTTP-запроса (клиент-сервер-клиент)" — фактически аналог прокси сервера (просьба не предлагать переделать через WinInet/сделать прокси) — задача должна быть решена конкретно через Winsock и именно так. Сокеты — блокируемые, мультипоточно.


_>>Значит, тестирую так: есть любой сайт, к примеру тестировал на http://www.gotovim.ru/ Прописываю в конфиг его домен/айпи, биндю 80 порт на локалхосте и в файл hosts прописываю 127.0.0.1 www.gotovim.ru (соответственно все запросы, которые браузер посылает на домен www.gotovim.ru приходят на 80 порт loopback ко мне в приложение, и дальше я их и обрабатываю). Обработка — пока что : получение запроса от браузера -> пересылка без изменений на сервер -> получение ответа от сервера -> без изменения передача обратно в браузер (клиенту) — то есть вообще без изменений пересылка: браузер — приложение — сервер — приложение — браузер. На данный момент это все, что требуется.


_>>Проблема. HTML страница (текст) грузится без проблем. Картинок почти нет (буквально пару баннеров). Когда пробую отдельно загрузить картинку — грузится.

_>>Уже много кружек кофе и гугла прошло, а воз и ныне там.
_>>Какие ошибки были замечены при отладке:

_>>Начал качать исходники браузера, чтобы посмотреть как там реализован цикл "общения" клиент-сервер-клиент, однако исходники очень большие и качаться будут не быстро, судя по всему.


_>>Код вероятно довольно кривой, но как умею... Рабочего решения не прошу, если не затруднит — подскажите, куда копать и где я ошибаюсь (не предлагайте бросить программирование )?


O>Первым делом рекомендую скачать и прочесть RFC 1945 (HTTP/1.0) и RFC 2616 (HTTP/1.1).

O>Материал может показаться скучным, но его не так много и читается и впрямь легко, особенно
O>когда есть практический интерес. Список неплохих книг по HTTP я приводил здесь
Автор: okman
Дата: 15.07.11
.

O>Начал с HTTP, поскольку это — основа, без ее хорошего знания у Вас все время что-то
O>будет падать или зависать в бесконечном цикле. Ну и без понимания работы TCP/IP тоже никуда.

O>По реализации.

O>HTTP — асинхронный протокол. Несмотря на роли "клиент" и "сервер", запросы и ответы не
O>сихнронизируются друг с другом, хотя порядок доставки сохраняется. Более того, браузеры обычно
O>создают два и более соединений с одним веб-сервером — по первому скачивают контент, по
O>второму получают какой-нибудь favicon. Причем доподлинно нельзя знать, какие данные
O>пойдут первыми по сети. Если делать прокси, нужно научить его работать полностью асинхронно,
O>чтобы одни потоки не стопорились другими. Тогда все будет работать.

O>Я делал точно такое же перенаправление трафика в одном продукте коммерческого уровня,

O>только вместо hosts использовал "ядерные" технологии перехвата — в результате исходящие
O>коннекты попадали на локальный веб-сервер, а от него уходили во внешний мир, отфильтрованными.
O>Входящий трафик, само собой, тоже успешно фильтровался.

O>Принцип работы следующий — в центре системы находится порт завершения ввода-вывода, сокет,

O>находящийся в режиме прослушки, а также пул рабочих потоков.
O>Как только приходит коннект (это браузер, к примеру, лезет на rsdn.ru), создается
O>соединение с удаленным узлом, и оба новых сокета (первый нам вернула функция accept,
O>второй — функция connect) помещаются в общую структуру, которая отдается на обработку
O>свободному потоку. Дальше все просто — на первом сокете вызывается WSARecv, полученные
O>данные отдаются второму сокету через WSASend. То же самое происходит в обратном направлении -
O>получается полноценный дуплексный обмен данными и никто друг другу не мешает.
O>Когда одна из сторон размыкает или разрывает соединение, второй отправляется shutdown.
O>То есть, логика завершения связи также полностью поддерживается.
O>Повторю — все происходит полностью асинхронно, WSASend вызывается в одном потоке, завершиться
O>может в другом, результаты ее работы обработаться в третьем и т.п. В этом смысл системы.

O>Будьте готовы к тому, что далеко не все реализации веб-серверов и браузеров следуют

O>рекомендациям HTTP и к тому, что придется делать всякие подпорки, не очень красивые.
O>Например, в HTTP/1.1 принято, что если в ответе сервера не указана длина (Content-Length),
O>то признаком конца сообщения будет размыкание соединения. Однако куча серверов не делают так -
O>они отдают HTML-контент, в заголовках HTTP прописывают даже "Connection: close", однако
O>соединение оставляют открытым. Для интернет-браузера это не является проблемой, так как,
O>получив, скажем, XML, он в состоянии определить его конец и визуально оповестить юзера,
O>что содержимое загрузилось. Для прокси-серверов все сложнее — потому что тоже приходится,
O>как и браузер, читать контент, а тут и вопросы сжатия (gzip/deflate), и chunked-encoding,
O>и еще много всего. Если самому рвать соединение, то это может нарушить совместимость с
O>нормальными веб-серверами, не говоря уже о проблеме с TIME_WAIT, когда в этом состоянии
O>окажется слишком много соединений и нельзя будет вообще никуда подключаться.

O>В общем, работа не простая предстоит, я бы посоветовал основательно подготовиться.


Огромное спасибо за такой развернутый ответ и ваше потраченное время.

В принципе направление ясно, про WSASend/WSARecv как раз сидел и думал сейчас.

Однако не совсем ясно, если у меня сейчас мульти-поточный accept при каждом новом соединении создаёт поток, в который просто передает сокет, откуда к нему приконнектились в параметр, и, далее, созданный поток уже самостоятельно создает второй сокет для исходящего соединения, ну и соединяется с заданным узлом...

С начала, таких потоков одновременно обычно 5 штук (тестирую с Mozilla FF, предполагаю, что в зависимости от браузера данное поведение будет сильно меняться), про асинхронность не до конца понял... В чём плох подход создавать поток для каждого нового соединения забирая в параметр сокет, и исходящий сокет создавать в потоке? Неужели все "настолько" асинхронно, что в потоке №1 будет запрошено что-то либо
GET /images/1.jpg HTTP/1.1

, а ответ от сервера на этот запрос, уже с бинарными данными картинки, будет в потоке №3, например? Мне казалось, что обычно программы ожидают ответа на определенный запрос именно в том сокете, через который они этот запрос отправили (ну при наличии Keep-Alive подобного алгоритма)? И ещё, наверное торможу, но почему же потоки "висят" минуту + перед завершением?

Вот весь код Handshake...


DWORD WINAPI HandShake(LPVOID newthsocket)
{
    int iResult; // Return result
    int u=0;//array index
    BOOL gNeededDomainFound = FALSE;
    char futureIp[128];

    char recvbuf[DEFAULT_BUFLEN]; // Buffer to receive
    int recvbuflen = DEFAULT_BUFLEN; // Length of receive buffer
    SOCKET ClientSocket;
    ClientSocket=((SOCKET *) newthsocket)[0]; // Taking a socket from thread param

    SOCKET DestionationSocket = socket(AF_INET, SOCK_STREAM, 0);
    PSERVER_THREAD_INFO pServerThreadInfo = (PSERVER_THREAD_INFO)malloc(sizeof(SERVER_THREAD_INFO));
    pServerThreadInfo->SourceSock = ClientSocket;
    pServerThreadInfo->DestSock = DestionationSocket;

    // receiving the request
    iResult = recv(ClientSocket, recvbuf, DEFAULT_BUFLEN, 0);
    if (iResult > 0)
    { // Beginning dialog
        recvbuf[iResult] = '\0';
#ifdef DEBUG
        Errorlog(recvbuf);
        Errorlog("\n-r-\n");
#endif
        if(strcmp(recvbuf, cmO) == 0)
        {
            iResult = send(ClientSocket, cme1, 12, 0);
            closesocket(ClientSocket);
            ExitThread(0);
            return 0;
        }
        char *iHostLocation;
        char iHostCompare[DEFAULT_BUFLEN];
        char recvbufcopy[DEFAULT_BUFLEN];

        for(int t=0;t<=DEFAULT_BUFLEN;t++)
        {
            recvbufcopy[t] = recvbuf[t];
        }

        iHostLocation = strstr(recvbufcopy, cHost);
        if(iHostLocation)
        {
            iHostLocation[strcspn(iHostLocation, "\n")] = '\0';
            _strrev(iHostLocation);
            iHostLocation[strcspn(iHostLocation, " ")] = '\0';
            _strrev(iHostLocation);
            iHostLocation[strcspn(iHostLocation, " ")] = '\0';
            //COMM: -1 убрать из цикла в релизе
            size_t t=0;
            for(t=0;t<=strlen(iHostLocation)-1;t++)
            {
                iHostCompare[t] = iHostLocation[t];
            }
            iHostCompare[t-1] = '\0';
            iHostCompare[strcspn(iHostCompare, ":")] = '\0';
            //iHostCompare = host anyway, no matter is we had a port number or not.

            //Редирект
            SOCKET fSocketOut;
            SOCKADDR_IN fSocketOutSin;
            fSocketOut = socket(AF_INET, SOCK_STREAM, 0);
            if (fSocketOut == INVALID_SOCKET)
            {
#ifdef DEBUG
                sprintf_s(errormsg, "Failed to create fSocketOut: %ld\n\n", WSAGetLastError());
                Errorlog(errormsg);
                Errorlog("\n---\n");
#endif
                nClients--;
                closesocket(ClientSocket);
                ExitThread(0);
                return 0;
            }

            //Config comparation
            char *tmpbuff = "";
            char dToCompare[128];
            for(u=0; u <= domains_amount*2; u=u+2)
            {//
                tmpbuff = domains[u];
                for(size_t r=0; r<= strlen(domains[u]); r++)
                {
                    dToCompare[r] = tmpbuff[r];
                }
                if(strcmp(dToCompare, iHostCompare)==0)
                {
                    gNeededDomainFound = TRUE;
                    sprintf_s(futureIp, "%s", domains[u+1]);
                    fSocketOutSin.sin_addr.s_addr = inet_addr(futureIp);
                    break;
                }
                else
                {
                    dToCompare[0] = '\0';
                }
            }

            if(gNeededDomainFound)//working
            {
                fSocketOutSin.sin_family = AF_INET;
                fSocketOutSin.sin_port = htons(80); // port HTTP.

                int gConnect = connect(fSocketOut, (SOCKADDR *)&fSocketOutSin, sizeof(fSocketOutSin));
                if(gConnect != SOCKET_ERROR)
                {
                    int isResult3 = 0;
                    isResult3 = send(fSocketOut, recvbuf, iResult, 0);
                    if(isResult3 != iResult)
                    {
#ifdef DEBUG
                        char err_msg[10240];
                        sprintf(err_msg, "\n[Debug error: iResult = %d, isResult3 = %d, WSAGetLastError = %d, nClients = %d]\n\n", iResult, isResult3, WSAGetLastError(), nClients);
                        Errorlog(err_msg);
                        Errorlog("\n-e-\n");
#endif
                    }
                    if(isResult3)
                    {
#ifdef DEBUG
                        Errorlog(recvbuf);
                        Errorlog("\n-s-\n");
#endif
                        do
                        {
                            iResult = recv(fSocketOut, recvbuf, sizeof(recvbuf), 0);
                            recvbuf[iResult] = '\0';
#ifdef DEBUG
                            Errorlog(recvbuf);
                            Errorlog("\n-r-\n");
#endif
                            int isResult = 0;
                            int isResult2 = 0;
                            unsigned int idx = 0;
                            unsigned long left = iResult;

                            while (left > 0)
                            {
                                isResult2 = send(ClientSocket, &recvbuf[idx], left, 0);
                                if(iResult != isResult2)
                                {
                                    if(isResult2 == -1)
                                    {
#ifdef DEBUG
                                        char err_msg[10240];
                                        sprintf(err_msg, "\n[Debug error: iResult = %d, isResult2 = %d, WSAGetLastError = %d, nClients = %d]\n\n", iResult, isResult2, WSAGetLastError(), nClients);
                                        Errorlog(err_msg);
                                        Errorlog("\n-e-\n");
#endif
                                        break;
                                    }
                                }
                                left = left - isResult2;
                                idx = idx + isResult2;
                            }
                            //strcpy(recvbuf, "");
                            int errt;
                            errt = WSAGetLastError();
#ifdef DEBUG
                            Errorlog(recvbuf);
                            Errorlog("\n-s-\n");
#endif//
                        } while (iResult != 0);//-1 проверяется внутри цикла
                    }
                }
                else
                {
#ifdef DEBUG
                    Errorlog("Connect fSocketOut failed\n\n");
#endif
                    nClients--;
                    closesocket(ClientSocket);
                    ExitThread(0);
                    return 0;
                }
            }
            else
            {//no needed domain ip configured - show 404
#ifdef DEBUG
                Errorlog("404");
                Errorlog("\n-e-\n");
#endif
                iResult = send(ClientSocket, cHTTPNotFound, 18, 0);
                nClients--;
                closesocket(ClientSocket);
                ExitThread(0);
                return 0;
            }
        }
        else
        {//!iHostLocation;// No host or no HTTP_REQUEST
#ifdef DEBUG
            Errorlog("No host or no HTTP_REQUEST\n\n");
#endif
            nClients--;
            closesocket(ClientSocket);
            ExitThread(0);
            return 0;
        }
    }//End Beginning dialog
    else if (iResult == 0)
    {
#ifdef DEBUG
            sprintf_s(errormsg, "No request has been received\n\n");
            Errorlog(errormsg);
#endif
            nClients--;
            closesocket(ClientSocket);
            ExitThread(0);
            return 0;
    }
    else
    {
#ifdef DEBUG
        sprintf_s(errormsg,"Handshake recv failed with error: %d\n\n", WSAGetLastError());
        Errorlog(errormsg);
#endif
        nClients--;
        closesocket(ClientSocket);
        ExitThread(0);
        return 0;
    }
    nClients--;
    closesocket(ClientSocket);
    ExitThread(0);
    return 0;
}
Re[3]: HTTP Winsock
От: my_problem  
Дата: 23.11.11 12:56
Оценка:
    SOCKET DestionationSocket = socket(AF_INET, SOCK_STREAM, 0);
    PSERVER_THREAD_INFO pServerThreadInfo = (PSERVER_THREAD_INFO)malloc(sizeof(SERVER_THREAD_INFO));
    pServerThreadInfo->SourceSock = ClientSocket;
    pServerThreadInfo->DestSock = DestionationSocket;


Прошу прощения, это лишнее, попало случайно с другого проэкта не нашел, как редактировать.
Re[3]: HTTP Winsock
От: okman Беларусь https://searchinform.ru/
Дата: 23.11.11 17:26
Оценка:
Здравствуйте, my_problem, Вы писали:

_>Однако не совсем ясно, если у меня сейчас мульти-поточный accept при каждом новом соединении создаёт поток, в который просто передает сокет, откуда к нему приконнектились в параметр, и, далее, созданный поток уже самостоятельно создает второй сокет для исходящего соединения, ну и соединяется с заданным узлом...


_>С начала, таких потоков одновременно обычно 5 штук (тестирую с Mozilla FF, предполагаю, что в зависимости от браузера данное поведение будет сильно меняться), про асинхронность не до конца понял... В чём плох подход создавать поток для каждого нового соединения забирая в параметр сокет, и исходящий сокет создавать в потоке?


Так тоже можно делать.
Но моя статистика показывает, что браузеры очень активно создают и завершают соединения, и даже
когда открыто всего несколько вкладок, этих соединений легко может оказаться под сотню.
Создавать поток на каждое соединение — слишком дорого, и после определенного порога чем больше
потоков, тем хуже производительность.

Неужели все "настолько" асинхронно, что в потоке №1 будет запрошено что-то либо
_>
_>GET /images/1.jpg HTTP/1.1
_>

_>, а ответ от сервера на этот запрос, уже с бинарными данными картинки, будет в потоке №3, например? Мне казалось, что обычно программы ожидают ответа на определенный запрос именно в том сокете, через который они этот запрос отправили (ну при наличии Keep-Alive подобного алгоритма)? И ещё, наверное торможу, но почему же потоки "висят" минуту + перед завершением?

Как-то путанно излагаете. Или я неправильно понимаю.
В любом случае, Ваша задача — разгрузить главный поток приложения, в котором вызывается accept,
от лишней работы и ожиданий, потому что сервер тогда станет недоступным и некоторые типичные
операции сломаются. Приняли коннект — сразу отдаем на обработку свободному потоку и быстро
возвращаемся к приему следующих соединений.

Рабочие потоки должны быть спроектированы так, чтобы они могли передавать данные от клиента (браузера)
серверу и в обратном направлении, причем одновременно. Используя синхронные сокеты, этого
достичь не получится, потому что поток будет "спать" либо на send, либо на recv, и встречные
данные "застопорятся". На первый взгляд кажется, что для HTTP это вроде как и не нужно,
так как запросы и ответы чередуются, следуя в строгом порядке друг за другом, но это только на
первый взгляд.

Именно поэтому я советую использовать асинхронные сокеты и WSASend/WSARecv, ну
а координировать их работу не обязательно портом завершения ввода/вывода, есть и другие механизмы.
Тогда у Вас получится схема, когда данные смогут беспрепятственно идти в обоих направлениях,
не мешая друг другу, а без этого некоторые сайты будут "зависать" на долгое время.

Когда я начал заниматься этими вопросами, то первое, что попытался сделать — написать
простейший веб-сервер, который бы принимал GET-запросы и отдавал ответы.
А потом стал бомбить его множеством одновременных запросов из разных браузеров и под
разными версиями Windows — оказалось, что мой сервер не работает так, как надо,
иногда повисает по непонятным причинам, иногда не может толком отдать контент, и т.д.
Потом, разбирая "по косточкам", открыл для себя много нового.
Re[4]: HTTP Winsock
От: my_problem  
Дата: 23.11.11 17:40
Оценка:
Здравствуйте, okman, Вы писали:

O>Здравствуйте, my_problem, Вы писали:


_>>Однако не совсем ясно, если у меня сейчас мульти-поточный accept при каждом новом соединении создаёт поток, в который просто передает сокет, откуда к нему приконнектились в параметр, и, далее, созданный поток уже самостоятельно создает второй сокет для исходящего соединения, ну и соединяется с заданным узлом...


_>>С начала, таких потоков одновременно обычно 5 штук (тестирую с Mozilla FF, предполагаю, что в зависимости от браузера данное поведение будет сильно меняться), про асинхронность не до конца понял... В чём плох подход создавать поток для каждого нового соединения забирая в параметр сокет, и исходящий сокет создавать в потоке?


O>Так тоже можно делать.

O>Но моя статистика показывает, что браузеры очень активно создают и завершают соединения, и даже
O>когда открыто всего несколько вкладок, этих соединений легко может оказаться под сотню.
O>Создавать поток на каждое соединение — слишком дорого, и после определенного порога чем больше
O>потоков, тем хуже производительность.

O>Неужели все "настолько" асинхронно, что в потоке №1 будет запрошено что-то либо

_>>
_>>GET /images/1.jpg HTTP/1.1
_>>

_>>, а ответ от сервера на этот запрос, уже с бинарными данными картинки, будет в потоке №3, например? Мне казалось, что обычно программы ожидают ответа на определенный запрос именно в том сокете, через который они этот запрос отправили (ну при наличии Keep-Alive подобного алгоритма)? И ещё, наверное торможу, но почему же потоки "висят" минуту + перед завершением?

O>Как-то путанно излагаете. Или я неправильно понимаю.

O>В любом случае, Ваша задача — разгрузить главный поток приложения, в котором вызывается accept,
O>от лишней работы и ожиданий, потому что сервер тогда станет недоступным и некоторые типичные
O>операции сломаются. Приняли коннект — сразу отдаем на обработку свободному потоку и быстро
O>возвращаемся к приему следующих соединений.

O>Рабочие потоки должны быть спроектированы так, чтобы они могли передавать данные от клиента (браузера)

O>серверу и в обратном направлении, причем одновременно. Используя синхронные сокеты, этого
O>достичь не получится, потому что поток будет "спать" либо на send, либо на recv, и встречные
O>данные "застопорятся". На первый взгляд кажется, что для HTTP это вроде как и не нужно,
O>так как запросы и ответы чередуются, следуя в строгом порядке друг за другом, но это только на
O>первый взгляд.

O>Именно поэтому я советую использовать асинхронные сокеты и WSASend/WSARecv, ну

O>а координировать их работу не обязательно портом завершения ввода/вывода, есть и другие механизмы.
O>Тогда у Вас получится схема, когда данные смогут беспрепятственно идти в обоих направлениях,
O>не мешая друг другу, а без этого некоторые сайты будут "зависать" на долгое время.

O>Когда я начал заниматься этими вопросами, то первое, что попытался сделать — написать

O>простейший веб-сервер, который бы принимал GET-запросы и отдавал ответы.
O>А потом стал бомбить его множеством одновременных запросов из разных браузеров и под
O>разными версиями Windows — оказалось, что мой сервер не работает так, как надо,
O>иногда повисает по непонятным причинам, иногда не может толком отдать контент, и т.д.
O>Потом, разбирая "по косточкам", открыл для себя много нового.

Спасибо большое ещё раз. Ну чтож, раз проще никак, буду мучать WSASend/WSARecv.

На данный момент бесконечный цикл accept'a выглядит так:


    while(ClientSocket = accept(ListenSocket, (sockaddr *)&client_addr, &client_addr_size))
    {
        if (ClientSocket == INVALID_SOCKET)
        {
#ifdef DEBUG
            sprintf_s(errormsg,"accept failed with error: %d\n", WSAGetLastError());
            Errorlog(errormsg);
#endif
            //TODO - report error
            closesocket(ListenSocket);
            WSACleanup();
            return 1;
        }
        else
        { //Multithread clients accept
            DWORD thID;
            CreateThread(NULL, NULL, HandShake, &ClientSocket, NULL, &thID);
            nClients++;
#ifdef DEBUG
            sprintf(errormsg, "\n[nClients = %d]", nClients);
            Errorlog(errormsg);
#endif
            WaitForSingleObject(0, 50000);
        }
    }


Как я понимаю, это полностью удовлетворяет условия задачи. Асинхронно надо в моём случае переделать HandShake... Этим и займусь.

Ну а функция "рабочего потока" выше постом. Хотя она конечно изменится. Спасибо за помощь.

Если я правильно понимаю, я делаю их не блокируемыми и в цикле проверяю, в каком из них на данный момент времени есть данные (клиент или сервер), и читая с этого сокета — отправляю в "противоположный".
Re[5]: HTTP Winsock
От: okman Беларусь https://searchinform.ru/
Дата: 23.11.11 18:02
Оценка:
Здравствуйте, my_problem, Вы писали:

Вот эта строчка для чего ?
WaitForSingleObject(0, 50000);


_>Если я правильно понимаю, я делаю их не блокируемыми и в цикле проверяю, в каком из них на данный момент времени есть данные (клиент или сервер), и читая с этого сокета — отправляю в "противоположный".


Цикл — это тоже не хорошо (ну что за собеседник, скажете, — и это ему не хорошо, и то)...
Представьте себе 50 потоков, в каждом из которых крутится цикл, причем только для того,
чтобы проверить — пришли ли данные. Суть асинхронных операций в Windows в том, чтобы
отдать данные и забыть о них на время. Например, поспать [часок] в wait-функции или заняться
другой полезной работой. Когда инфраструктура завершит операцию, она даст сигнал и
результаты можно будет забрать. Для Windows это наиболее эффективная схема.
Есть такая книга — "Программирование в сетях Windows" от Э.Джонса и Д.Оланда.
В ней довольно подробно рассматриваются основные модели работы с сокетами — select,
перекрытый ввод-вывод, порты завершения, и другие. Рекомендую найти и прочесть.
Re[6]: HTTP Winsock
От: my_problem  
Дата: 25.11.11 12:47
Оценка:
Здравствуйте, okman, Вы писали:

O>Здравствуйте, my_problem, Вы писали:


O>Вот эта строчка для чего ?

O>
O>WaitForSingleObject(0, 50000);
O>


_>>Если я правильно понимаю, я делаю их не блокируемыми и в цикле проверяю, в каком из них на данный момент времени есть данные (клиент или сервер), и читая с этого сокета — отправляю в "противоположный".


O>Цикл — это тоже не хорошо (ну что за собеседник, скажете, — и это ему не хорошо, и то)...

O>Представьте себе 50 потоков, в каждом из которых крутится цикл, причем только для того,
O>чтобы проверить — пришли ли данные. Суть асинхронных операций в Windows в том, чтобы
O>отдать данные и забыть о них на время. Например, поспать [часок] в wait-функции или заняться
O>другой полезной работой. Когда инфраструктура завершит операцию, она даст сигнал и
O>результаты можно будет забрать. Для Windows это наиболее эффективная схема.
O>Есть такая книга — "Программирование в сетях Windows" от Э.Джонса и Д.Оланда.
O>В ней довольно подробно рассматриваются основные модели работы с сокетами — select,
O>перекрытый ввод-вывод, порты завершения, и другие. Рекомендую найти и прочесть.


Спасибо Большое, разобрался, книжку скачал, просмотрел, прочел (не всю пока). С WSASend/WSARecv не подружился, сделал через select + FD_ISSET переводом сокетов в неблокируемый режим. Код пока ужасный, показывать стыдно, но работает безотказно (как ни странно, тьфу три раза). Продолжаю тесты гонять + переделываю остальные части программы, ибо слишком сильно изменился код.
Re: HTTP Winsock
От: std.denis Россия  
Дата: 23.12.11 10:54
Оценка:
_>Начал качать исходники браузера, чтобы посмотреть как там реализован цикл "общения" клиент-сервер-клиент, однако исходники очень большие и качаться будут не быстро, судя по всему.
исходники браузера смотреть это радикально конечно ㋡ есть легковесный прокси — 3proxy, опенсорцный. в его составе есть tcppm — выполняет тот самый функционал по тупой перекачке tcp-трафика на удаленный сервер. хоть автор и утверждает, что сорцы сложны для понимания и местами запутаны, но IMO вполне читаемы, и уж всяко понятнее исходников браузера. может будет полезным.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.