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

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Web интерфейс программы 
:(
    Опции темы
TheDestroyer
Дата 29.6.2009, 00:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


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

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



Сначала начал делать редирект сам, потом вспомнил, что видел уже это в коде, в итоге оставил как есть в HTTP_SendAns::send_redir, похоже это самый лучший вариант. Проблема была не в редиректе...
А вот проблему с IE и Ajax пока не решил. FF нормально работает, а IE постоянно выдает ошибку "Ошибка c00ce56e."
Похоже нашел решение: http://forum.vingrad.ru/forum/topic-237305...t-explorer.html с 4го поста снизу.
Идея в том, чтобы выдавать на аякс запрос в кодировке UTF-8. Как перекодировать отправляемый браузеру текст в эту кодировку? С помощью setlocale( LC_ALL, ... )?


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


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

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



Цитата(TheDestroyer @ 29.6.2009,  00:32)
Идея в том, чтобы выдавать на аякс запрос в кодировке UTF-8. Как перекодировать отправляемый браузеру текст в эту кодировку? 

Сервер отправляет содержимое строки 'как есть', никаких перекодировок не производится. Что бы это было UTF-8, исходная строка должна быть в UTF-8. Саму строку можно перекодировать чем угодно (например libiconv). Еще надо изменить кодировку в заголовке ответа (об этом уже писал)

Цитата

С помощью setlocale( LC_ALL, ... )?
Это не поможет. Он максимум может повлиять на cout, но никак не на внутренние самописные сервера  smile 
PM MAIL   Вверх
TheDestroyer
Дата 30.6.2009, 13:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


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

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



Спасибо за наводку на libiconv. Сделал чтобы сервер выдавал текст в UTF-8.
Возникла следующая проблема: если сервер обрабатывает запрос одного пользователя, то второй уже не может зайти на сайт (на данном сервере). Есть режим работы когда пользователю на страницу долго грузится результат работы некоторых приложений, в этом случае на сайт удается зайти только после того, как все операции будут закончены. Т.е. получается что вся работа с сокетом выполняется в одном потоке.
Код

#define RUN_MAIN(nm) _beginthread(&nm##_proxy,0,this)
...
RUN_MAIN(run);

run  - функция прослушивания сокета.
Как сделать так, чтобы каждому пользователю выделялся свой поток чтобы они могли работать одновременно?
Вот функция обработки сокета:
Код

void TCP_Server::run()
{// функция ожидания запросов от клиентов
 HANDLE h[]={sock_event,term_event};
 WSAEventSelect(sock,sock_event,FD_ACCEPT|FD_CLOSE);
 for(;;)
  {
   DWORD v=WaitForMultipleObjectsEx(2,h,FALSE,INFINITE,TRUE);
   switch(v)
    {
     case WAIT_OBJECT_0:
      {
       WSANETWORKEVENTS ne;
       WSAEnumNetworkEvents(sock,sock_event,&ne);
       if (ne.lNetworkEvents&FD_CLOSE) return;
       SOCKET s=accept(sock,NULL,NULL); // При появлении запроса от клиента accept успешно завершается, возвращая в переменной s дескриптор socket'а для обмена информацией с клиентом
       if (s==INVALID_SOCKET) continue;
       if (process_accept(s)) return; 
       break;
      }
     case WAIT_OBJECT_0+1: return;
     case WAIT_IO_COMPLETION: continue;
     default: error(2,"%s::run: Unexpected return value '%d' from WaitForMultipleObjectsEx",class_name,v);
    }
  }
}

Видимо, надо запускать поток для обработки process_accept(s) для каждого пользователя (а, наверное, правильнее говорить сеанса). Но тут проблема - в одном сеансе множество запросов (текст, картинки и т.д.). Как тут правильно поступить?

Также возник вопрос как запустить сервер на определенном адресе, если, например, у компьютера несколько сетевых карт?
PM MAIL   Вверх
xvr
Дата 30.6.2009, 15:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

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



Этот сервер намеренно был сделан single thread - для упрощения жизни (не надо связываться с синхронизацией обращений к серверным скриптам и данным для WEB страниц). Кроме того, специфика приложения - это был WEB интерфейс к программе (вместо GUI).
Если нужно многопоточное приложение, то поток надо заводить в методе WEB_Server::process_accept - все его содержимое запускать в потоке. Количество одновременно запущенных потоков можно (и даже нужно) ограничить - например сделать пул нитей, и отдавать запрос в нить из пула. По окончании обработки нить возвращать в пул.
Всю синхронизацию придется добавлять (например через критические секции)

Цитата

Также возник вопрос как запустить сервер на определенном адресе, если, например, у компьютера несколько сетевых карт? 
Никогда не пробовал. Видимо при bind'е исходного сокета нужно указать реальный адрес (сейчас используется константа INADDR_ANY - что означает - первый попавшийся)

Строка 13 в tcp_server.cpp:
Код

bool TCP_Server::do_init(int port) // return true on abort
{
 ...
 sockaddr_in sin;
 sin.sin_family = AF_INET;
 sin.sin_addr.s_addr = INADDR_ANY;  // <<<< Place real IP address here
 sin.sin_port = htons(port);
 if (bind(sock,(sockaddr*)&sin,sizeof(sin)))
 ...
}

PM MAIL   Вверх
TheDestroyer
Дата 30.6.2009, 20:14 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


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

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



Чтобы добавить многопоточность делаю так:
фаил tcp_server.cpp:
Код

void TCP_Server::run()
{// функция ожидания запросов от клиентов
 HANDLE h[]={sock_event,term_event};
 WSAEventSelect(sock,sock_event,FD_ACCEPT|FD_CLOSE);
 for(;;)
  {
   DWORD v=WaitForMultipleObjectsEx(2,h,FALSE,INFINITE,TRUE);
   switch(v)
    {
     case WAIT_OBJECT_0:
      {
       WSANETWORKEVENTS ne;
       WSAEnumNetworkEvents(sock,sock_event,&ne);
       if (ne.lNetworkEvents&FD_CLOSE) return;
       SOCKET s=accept(sock,NULL,NULL); // При появлении запроса от клиента accept успешно завершается, возвращая в переменной s дескриптор socket'а для обмена информацией с клиентом
       if (s==INVALID_SOCKET) continue;
       if (process_accept(s)) return; 
      //RUN_MAIN(process_accept);// <<<
     _beginthread(WEB_Server::process_accept_m, 0, (void*) s); // <<<
       break;
      }
     case WAIT_OBJECT_0+1: return;
     case WAIT_IO_COMPLETION: continue;
     default: error(2,"%s::run: Unexpected return value '%d' from WaitForMultipleObjectsEx",class_name,v);
    }
  }
}

Пытался запустить поток аналогично потоку с обработкой сокета (через RUN_MAIN), но вылезают ошибки о неправильных объявлениях... Тогда решил работать с  _beginthread напрямую. 
К сожалению просто так в функцию, передаваемую в  _beginthread параметры не передашь. Да еще эта функция должна быть типа void и иметь параметр типа (void *param) .
Примеры смотрел здесь:
http://msdn.microsoft.com/en-us/library/kdzttdcb(VS.71).aspx
http://www.rsdn.ru/forum/cpp/4612.flat.aspx
http://www.gamedev.net/community/forums/to...topic_id=448462
В борланде было проще - указал на запуск функции в отдельном потоке и все функции которые вызываются из нее тоже запускаются в том потоке, и все без преобразования типов передаваемых параметров и т.д. Может и тут так можно?

Пришлось сдеделать static функции:
фаил web_server.h:
Код

public:
...
 static void process_accept_m(void *sv);
 static void process_file_m(HTTP_SendAns&, string fname);
 static void process_root_m(HTTP_SendAns &a) {process_file_m(a,"index.html");}
 static void process_cmd_m(HTTP_SendAns&, string cmd);

фаил web_server.cpp - добавил функции с _m на конце (multithread) аналогичные их оригиналам. Только, наверное, прийдется им передавать параметры также как и в static void process_accept_m(void *sv). Пока до этого не дошел.
Код

void WEB_Server::process_cmd_m(HTTP_SendAns &A, string cmd)
{
}

void WEB_Server::process_file_m(HTTP_SendAns &A, string fname)
{
Web_pages web_page;
string page= ""; // содержимое отсылаемой страницы
if ((fname == "process.php") || (fname == "index.html"))
{// если фаил динамических страниц
web_process(A, fname);
}
else
{// все остальное просто подгружаем из файлов
A.send_file(fname);
}

void WEB_Server::process_accept_m(void *sv)
{
 SOCKET s = SOCKET(sv);
 HTTP_ReadReq req(s); // объявляем переменную req типа HTTP_ReadReq, заполняем структуру req (расшифруем запрос)
 if (req.load_req()) return;
 HTTP_SendAns ans(&req); // заполняем структуру ans
 string url=req.get_url(); // получим запрашиваемый клиентом URL
 if (url.empty() || url=="/") process_root_m(ans); else  // если запрос корневой директории, то выдадим index.html
  {
   if (url[0]=='/') url.erase(0,1);
   if (url[0]=='$') {url.erase(0,1); process_cmd_m(ans,url);}
   else process_file_m(ans,url); // если не команда и не корневая директория, то выдадим запрашиваемый фаил
  }
}

При компиляции ругается на то, что 
Код

web_server.cpp(39) : error C2352: WEB_Server::web_process: недопустимый вызов нестатической функции-члена

Здесь это строка 11.
Неужели прийдется делать все вызываемые далее функции статическими? Может у меня неправильный подход?
Далее, критическими секциями, видимо, прийдется защитить, переменные которые используются в программе глобально?

Это сообщение отредактировал(а) TheDestroyer - 30.6.2009, 20:17
PM MAIL   Вверх
xvr
Дата 30.6.2009, 21:46 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

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



Подход в корне не верный. Обработку надо делать в коде WEB_Server::process_accept

Например так

Код


class WEB_Server : public InitOrder , public TCP_Server {
 // Add some members and types
 struct RunInfo {
   SOCKET s;
   WEB_Server* self;
   RunInfo(SOCKET s1, WEB_Server* sl) :s(s1), self(sl) {}
 };
 
 void process_accept_imp(SOCKET s);
 static void __cdecl process_accept_imp_proxy(void *p) 
  {
   RunInfo* ri = (RunInfo*)p;
   ri->self->process_accept_imp(ri->s);
   delete ri;
  }

};

bool WEB_Server::process_accept(SOCKET s)
{
 _beginthread(&process_accept_imp_proxy,0,new RunInfo(s,this));
 return false;
}

// Rename old WEB_Server::process_accept into WEB_Server::process_accept_imp


Далее - все обращения из бывшего WEB_Server::process_accept к общим ресурсам (в первую очередь - поиск и загрузка файлов) должны быть защищены критическими секциями

Далее - эта реализация будет создавать по отдельному thread'у на каждый запрос. При большом числе запросов сервер может помереть  smile 
PM MAIL   Вверх
TheDestroyer
Дата 1.7.2009, 00:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


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

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



Класс, я замучался с этими потоками. Спасибо огромное.
Если несложно пояснить:
здесь добавлена структура RunInfo, в ней есть RunInfo(SOCKET s1, WEB_Server* sl) :s(s1), self(sl) {}. Это инициализация структуры? Похоже на конструктор.

По поводу защиты критическими секциями. Видимо при запросе от клиента все сводится к функции void WEB_Server::process_file(HTTP_SendAns &A, string fname). А там либо генерация кода страницы, либо подгрузка файлов из ресурсов.
Достаточно ли защитить подгрузку файлов следующим образом:
фаил web_server.cpp:
Код

...
CRITICAL_SECTION cs;
bool WEB_Server::process_accept(SOCKET s)
{
InitializeCriticalSection( &cs ); 
_beginthread(&process_accept_imp_proxy,0,new RunInfo(s,this));
 return false;
}

фаил zipfile.cpp:
Код

string find_resource(string name)
{
string res;
EnterCriticalSection( &cs );
res = get_rs().find_resource(name);
LeaveCriticalSection( &cs );
 return res;
}

Пример с критическими секциями смотрел здесь: http://www.cyberguru.ru/programming/cpp/mu...ntro-page2.html
В find_resource будут защищены все переменные что во вложенном в нее вызове функции или только одна переменная res?
Где лучше объявить CRITICAL_SECTION cs чтобы использовать в файлах zipfile.cpp и web_server.cpp?
PM MAIL   Вверх
xvr
Дата 1.7.2009, 11:54 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

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



Цитата(TheDestroyer @ 1.7.2009,  00:05)
Класс, я замучался с этими потоками. Спасибо огромное.
Если несложно пояснить:
здесь добавлена структура RunInfo, в ней есть RunInfo(SOCKET s1, WEB_Server* sl) :s(s1), self(sl) {}. Это инициализация структуры? Похоже на конструктор.

В С++ структуры и классы - это одно и то же (отличается только спицификатор доступа по умолчанию - для структуры public, для класса - private). Так что это конструктор и есть

Цитата

Достаточно ли защитить подгрузку файлов следующим образом:

InitializeCriticalSection( &cs ); нужно вынести из WEB_Server::process_accept и поместить куда то на самый верхний уровень - ее нужно вызвать ТОЛЬКО 1 раз до первого использования самой критической секции

Цитата

В find_resource будут защищены все переменные что во вложенном в нее вызове функции или только одна переменная res?
Все.

Цитата

Где лучше объявить CRITICAL_SECTION cs чтобы использовать в файлах zipfile.cpp и web_server.cpp?
Где нибудь в zipfile.cpp. В web_server.cpp ее вообще заносить не надо. Можно сделать так:
(фаил zipfile.cpp):
Код

static CRITICAL_SECTION* get_cs()
{
 static CRITICAL_SECTION cs;
 InitializeCriticalSection(&cs);
 return &cs;
}

string find_resource(string name)
{
 static CRITICAL_SECTION* cs=get_cs(); // <<< 'static' here is a 'must' word! Do not remove it!

 string res;
 EnterCriticalSection( cs );
 res = get_rs().find_resource(name);
 LeaveCriticalSection( cs );
 return res;
}

PM MAIL   Вверх
TheDestroyer
Дата 5.7.2009, 21:27 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


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

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



С многопоточностью вроде разобрался, спасибо.
Обнаружилась проблема: если я передаю GET методом на веб сервер строку типа "qwe%20-qwe%20+qwe", то пропадает знак "+". Стал разбираться, обнаружил:
Код

static string unescape_url(string url, bool supp_plus)
{
 string dst;
 const char* p=url.c_str();
 int len=url.size();
 dst.reserve(len);
 for(;len--;++p)
  switch(*(unsigned char*)p)
   {
    case '%': if (len<2) return dst;
              {char c=x2c(p+1); dst.append(&c,1);}
              p+=2; len-=2; break;
    case '+': if (supp_plus) {dst+=" "; break;}
    default:  dst.append(p,1); break;
   }
 return dst;
}

Чем грозит переключение supp_plus в false? Как правильно обработать этот момент?
PM MAIL   Вверх
xvr
Дата 5.7.2009, 21:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

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



При кодировании URL пробелы заменяются на знак '+' (HTTP требует). Если нужен пробел лучше передавать его как %31


PM MAIL   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С++:Общие вопросы"
Earnest Daevaorn

Добро пожаловать!

  • Черновик стандарта C++ (за октябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика(4.4мб).
  • Черновик стандарта C (за сентябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика (3.4мб).
  • Прежде чем задать вопрос, прочтите это и/или это!
  • Здесь хранится весь мировой запас ссылок на документы, связанные с C++ :)
  • Не брезгуйте пользоваться тегами [code=cpp][/code].
  • Пожалуйста, не просите написать за вас программы в этом разделе - для этого существует "Центр Помощи".
  • C++ FAQ

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn

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


 




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


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

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