| « Поставить закладку » « Сделать стартовой » | |||
|
|||
|
Cокеты и особенности их реализации в библиотеке wxWidgets
Сегодня я расскажу тебе сказку про сокеты и особенности их реализации в библиотеке wxWidgets. Ты, наверное, уже знаешь что такое сокеты и, уверен, знаешь о том, что с их помощью можно творить чудеса. Итак, сказка начинается… Жили были юзвери и была у них локаль домовая… только локаль была у них непродвинутая… без ICQшного или Jabber’овского сервера…. по сему общались юзвери исключительно net send’ом…. (чего уж тут поделаешь) Был у них еще не то админ, не то программер…. кто его разберет… за длинным хаером (волосами от англ. hair) и бородой все-равно ничего не видно. В общем надоел юзверям net send и начали они к своему админу приставать: - Мы тебя, - говорять, - пивом кормим? Кормим. Давай ставь нам софтину чтобы чатиться можно было, а то как-то не весело, когда посреди кина или в самый разар игры в сапёра или Mario выскакиваеть надоедливое окошко и говорить «NET SEND» Ну, админ почесал репу, подумал, потом раздал юзверям по подзатыльнику, по ходу назвав маздайными ламерами и сказал «Черт с вами, будеть вам чат» Пошел админ к себе в админку, закрылся и начал думать… как бы это так поставить чатилку чтобы поменьше системных ресурсов ела и чтобы еще под Линухом работала и чтобы писать поменьше да настраивать не надо было. А про Линух это я чего сказал… хоть юзвери и маздайные были, но у некоторых на машинках Линух стоял (к слову сказать. они в Линухе все-равно не шарили, но зато как игрушку или какую-нить программулину запустить знали) В общем думал наш админ-думал и решил… «А забабахаю я им чат на wxWidgets» (гы-ы-ы-ы) «Оно потом и настройки не будт требовать, а wx у меня все-равно уже установлен» Сбегал он в гамазин (энто магазин по-нашенски, все-равно в сказке всё не как у людей. по сему пусть так называется), затарился пивом и печеньком, закрылся у себя в админке, отключил телефон и начал програмить… Первым делом принялся наш герой за сервак… т.к. энто чудо на его же машинке и должно было стоять…. а себе любимому надо все в первую очередь делать… юзвери подождут.
CODE
#include <wx/wx.h> #include <wx/splitter.h> #include <wx/socket.h> #include <wx/list.h> #include <wx/valgen.h> #include <wx/listimpl.cpp> #include <math.h> /// Создаем новый тип списков – список указателей на сокеты WX_DECLARE_LIST(wxSocketBase, wxSocketList); WX_DEFINE_LIST(wxSocketList); class MyFrame : public wxFrame { // Здесь у нас будут отображаться сообщения wxTextCtrl * m_LOGTextCtrl; // Здесь будет список юзверей wxListBox * m_ClientListBox; // Переменная для отправки сообщения (временный буфер) wxString m_MessageStr; // Список клиентских сокетов wxSocketList m_Clients; // Серверный сокет wxSocketServer * m_SocketServer; public: MyFrame(wxWindow * parent); ~MyFrame(); // Этот метод выводит список клиентов void ShowClientList(); DECLARE_EVENT_TABLE() void OnExit(wxCommandEvent & event); void OnMessageInput(wxCommandEvent & event); void OnServerSocketEvent(wxSocketEvent & event); void OnClientSocketEvent(wxSocketEvent & event); }; enum { ID_LOG_TEXT_CTRL, ID_MESSAGE_TEXT_CTRL, ID_CLIENT_LIST_BOX, ID_SOCKET_SERVER, ID_SOCKET_CLIENT }; BEGIN_EVENT_TABLE(MyFrame, wxFrame) EVT_MENU(wxID_EXIT, MyFrame::OnExit) EVT_TEXT_ENTER(ID_MESSAGE_TEXT_CTRL, MyFrame::OnMessageInput) EVT_SOCKET(ID_SOCKET_SERVER, MyFrame::OnServerSocketEvent) EVT_SOCKET(ID_SOCKET_CLIENT, MyFrame::OnClientSocketEvent) END_EVENT_TABLE() MyFrame::MyFrame(wxWindow * parent) : wxFrame(parent, -1, _("Test Server"), wxDefaultPosition, wxSize(600, 450)) { SetExtraStyle(wxWS_EX_VALIDATE_RECURSIVELY); wxMenuBar * menuBar = new wxMenuBar(); SetMenuBar(menuBar); wxMenu * fileMenu = new wxMenu(); fileMenu->Append(wxID_EXIT, _("ExittAlt+F4")); menuBar->Append(fileMenu, _("File")); wxBoxSizer * sizer = new wxBoxSizer(wxVERTICAL); SetSizer(sizer); // Это у нас такой сплиттер. Позволяет изменять размеры поля сообщений и списка юзверей мышкой wxSplitterWindow * splitter_window = new wxSplitterWindow(this, wxID_ANY); sizer->Add(splitter_window, 1, wxEXPAND|wxALL, 5); m_LOGTextCtrl = new wxTextCtrl(splitter_window, ID_LOG_TEXT_CTRL, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_READONLY|wxTE_MULTILINE|wxNO_BORDER); m_LOGTextCtrl->SetBackgroundColour(*wxBLACK); m_LOGTextCtrl->SetForegroundColour(*wxGREEN); wxArrayString dummy; m_ClientListBox = new wxListBox(splitter_window, ID_CLIENT_LIST_BOX, wxDefaultPosition, wxSize(150, -1), dummy, wxNO_BORDER); splitter_window->SplitVertically(m_LOGTextCtrl, m_ClientListBox, splitter_window->GetSize().GetWidth()-150); splitter_window->SetMinimumPaneSize(50); splitter_window->SetSashGravity(1.0); wxTextCtrl * message_text_ctrl = new wxTextCtrl(this, ID_MESSAGE_TEXT_CTRL, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER); message_text_ctrl->SetValidator(wxGenericValidator(&m_MessageStr)); sizer->Add(message_text_ctrl, 0, wxGROW|wxLEFT|wxRIGHT|wxBOTTOM, 5); // Ага... здесь мы создаем серверный сокет // Сначала настраиваем порт 3000 wxIPV4address addr; addr.Service(3000); // Потом создаем новый объект сокета с привязкой к конкретному порту m_SocketServer = new wxSocketServer(addr); // Если создалось, то говорим что «всё ОК» if (!m_SocketServer->Ok()) { m_LOGTextCtrl->AppendText(_("Не удалось запустить сервер...rn")); return; } else { m_LOGTextCtrl->AppendText(_("Работаем...rn")); } // Указываем, что сообщения от сокета будут обрабатываться нашей формой m_SocketServer->SetEventHandler(*this, ID_SOCKET_SERVER); // Указываем что обрабатываться будет толкьо соединение нового клиента m_SocketServer->SetNotify(wxSOCKET_CONNECTION_FLAG); // Говорим что уже можно обрабатывать события m_SocketServer->Notify(TRUE); CreateStatusBar(); Centre(); } MyFrame::~MyFrame() { // При закрытии формы чистим список клиентов for(wxSocketList::Node * node = m_Clients.GetFirst(); node; node = node->GetNext()) { node->GetData()->Destroy(); } // И закрываем серверный сокет m_SocketServer->Destroy(); } void MyFrame::ShowClientList() { m_ClientListBox->Clear(); wxIPV4address addr; wxSocketBase * sock; // А здесь мы просто добавляем в список адреса всех наших клиентов for(wxSocketList::Node * node = m_Clients.GetFirst(); node; node = node->GetNext()) { sock = node->GetData(); sock->GetLocal(addr); m_ClientListBox->Append(addr.IPAddress()); } } void MyFrame::OnMessageInput(wxCommandEvent & event) { TransferDataFromWindow(); // Если нам есть что переслать if(m_MessageStr.IsEmpty()) return; wxSocketBase * sock; // Дописываем что это сообшение от сервера m_MessageStr = _("Server> ")+m_MessageStr; // Отсылаем сообщение всем клиентам из списка for(wxSocketList::Node * node = m_Clients.GetFirst(); node; node = node->GetNext()) { sock = node->GetData(); if(!sock) continue; // Не забываем что для UNICODE-сборки размер символа может быть 2 байта, поэтому учитываем размер wxChar sock->Write(m_MessageStr.GetData(), m_MessageStr.Length()*sizeof(wxChar)); } m_LOGTextCtrl->AppendText(m_MessageStr+wxString(wxT("rn"))); m_MessageStr = wxEmptyString; TransferDataToWindow(); } void MyFrame::OnServerSocketEvent(wxSocketEvent & event) { wxSocketBase *sock; wxIPV4address addr; switch(event.GetSocketEvent()) { case wxSOCKET_CONNECTION: // Зааццептить нового клиента sock = m_SocketServer->Accept(FALSE); if (!sock) return; // Указать что обрабатывать сообшение от этого сокета будет наша форма sock->SetEventHandler(*this, ID_SOCKET_CLIENT); // Указать что обрабатываться будет получение данных и дисконнект sock->SetNotify(wxSOCKET_INPUT_FLAG | wxSOCKET_LOST_FLAG); // Говорим что уже можно обрабатывать события sock->Notify(TRUE); // Выводим в ЛОГ что новый клиент добавлен sock->GetLocal(addr); m_Clients.Append(sock); m_LOGTextCtrl->AppendText(wxString::Format(_("Новый клиент: %srn"), addr.IPAddress())); // Выводим список клиентов ShowClientList(); break; } } void MyFrame::OnClientSocketEvent(wxSocketEvent & event) { // Пока здесь пусто… смотрим дальше } void MyFrame::OnExit(wxCommandEvent & event) { Close(); } class MyApp : public wxApp { public: virtual bool OnInit(); }; IMPLEMENT_APP(MyApp) bool MyApp::OnInit() { MyFrame * frame = new MyFrame(NULL); frame->Show(); SetTopWindow(frame); return true; } Жмакнул Build – скомпилилось… это хорошо… можно браться за клиента:
CODE
#include <wx/wx.h> #include <wx/socket.h> #include <wx/valgen.h> class MyFrame : public wxFrame { wxTextCtrl * m_LOGTextCtrl; wxString m_MessageStr; // Это наш клиентский сокет wxSocketClient * m_SocketClient; public: MyFrame(wxWindow * parent); ~MyFrame(); DECLARE_EVENT_TABLE() void OnExit(wxCommandEvent & event); // Обработчик нажатия ENTER в поле ввода сообщения void OnMessageInput(wxCommandEvent & event); // Обработчик событий от сокета void OnClientSocketEvent(wxSocketEvent & event); // Обработчики для установки параметров контролов в зависимости от состояния приложения void OnMessageTextCtrlUpdateUI(wxUpdateUIEvent & event); void OnConnectButtonUpdateUI(wxUpdateUIEvent & event); void OnDisconnectButtonUpdateUI(wxUpdateUIEvent & event); // Обработчик кнопки КОННЕКТ void OnConnect(wxCommandEvent & event); // Обработчик кнопки ДИСКОННЕКТ void OnDisconnect(wxCommandEvent & event); }; enum { ID_LOG_TEXT_CTRL, ID_MESSAGE_TEXT_CTRL, ID_CONNECT, ID_DISCONNECT, ID_SOCKET_CLIENT }; BEGIN_EVENT_TABLE(MyFrame, wxFrame) EVT_MENU(wxID_EXIT, MyFrame::OnExit) EVT_TEXT_ENTER(ID_MESSAGE_TEXT_CTRL, MyFrame::OnMessageInput) EVT_SOCKET(ID_SOCKET_CLIENT, MyFrame::OnClientSocketEvent) EVT_UPDATE_UI(ID_MESSAGE_TEXT_CTRL, MyFrame::OnMessageTextCtrlUpdateUI) EVT_UPDATE_UI(ID_CONNECT, MyFrame::OnConnectButtonUpdateUI) EVT_UPDATE_UI(ID_DISCONNECT, MyFrame::OnDisconnectButtonUpdateUI) EVT_BUTTON(ID_CONNECT, MyFrame::OnConnect) EVT_BUTTON(ID_DISCONNECT, MyFrame::OnDisconnect) END_EVENT_TABLE() MyFrame::MyFrame(wxWindow * parent) : wxFrame(parent, -1, _("Test Client"), wxDefaultPosition, wxSize(600, 450)), m_SocketClient(NULL) { SetExtraStyle(wxWS_EX_VALIDATE_RECURSIVELY); wxMenuBar * menuBar = new wxMenuBar(); SetMenuBar(menuBar); wxMenu * fileMenu = new wxMenu(); fileMenu->Append(wxID_EXIT, _("ExittAlt+F4")); menuBar->Append(fileMenu, _("File")); wxBoxSizer * sizer = new wxBoxSizer(wxVERTICAL); SetSizer(sizer); m_LOGTextCtrl = new wxTextCtrl(this, ID_LOG_TEXT_CTRL, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_READONLY|wxTE_MULTILINE); sizer->Add(m_LOGTextCtrl, 1, wxEXPAND|wxALL, 5); m_LOGTextCtrl->SetBackgroundColour(*wxBLACK); m_LOGTextCtrl->SetForegroundColour(*wxGREEN); wxTextCtrl * message_text_ctrl = new wxTextCtrl(this, ID_MESSAGE_TEXT_CTRL, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER); message_text_ctrl->SetValidator(wxGenericValidator(&m_MessageStr)); sizer->Add(message_text_ctrl, 0, wxGROW|wxLEFT|wxRIGHT|wxBOTTOM, 5); wxBoxSizer * buttonsizer = new wxBoxSizer(wxHORIZONTAL); wxButton * connect_btn = new wxButton(this, ID_CONNECT, _("Connect")); wxButton * disconnect_btn = new wxButton(this, ID_DISCONNECT, _("Disonnect")); buttonsizer->Add(connect_btn, 0, wxRIGHT, 5); buttonsizer->Add(disconnect_btn, 0); sizer->Add(buttonsizer, 0, wxALIGN_RIGHT|wxALL, 5); CreateStatusBar(); Centre(); } MyFrame::~MyFrame() { // При удалении формы, закрываем наш сокет if(m_SocketClient) { m_SocketClient->Destroy(); } } // Эти обработчики вызываются при любом обновлении ГУИ // С помощью wxUpdateUIEvent можно настраивать внешний вид и состояние контролов в зависимости от определенных параметров // Изменения вступают в силу для всех контролов с указанным идентификатором void MyFrame::OnMessageTextCtrlUpdateUI(wxUpdateUIEvent & event) { // Сделать поле ввода сообщения неактивным если нет соединения (смотрим в Event Table на идентификатор контрола для этого обработчика) event.Enable(m_SocketClient != NULL); } void MyFrame::OnDisconnectButtonUpdateUI(wxUpdateUIEvent & event) { // кнопка ДИСКОННЕКТ активна толкько когда есть соединение event.Enable(m_SocketClient != NULL); } void MyFrame::OnConnectButtonUpdateUI(wxUpdateUIEvent & event) { // кнопка КОННЕКТ активна толкько когда нет соединения event.Enable(m_SocketClient == NULL); } void MyFrame::OnMessageInput(wxCommandEvent & event) { TransferDataFromWindow(); if(m_MessageStr.IsEmpty()) return; if(!m_SocketClient) return; // Отослать сообщение m_SocketClient->Write(m_MessageStr.GetData(), m_MessageStr.Length()*sizeof(wxChar)); m_MessageStr = wxEmptyString; TransferDataToWindow(); } void MyFrame::OnConnect(wxCommandEvent & event) { // Если мы вдруг нажали на кнопку КОННЕКТ когда соединение уже установлено, то выход (вобще, таукого не должно случиться...) if(m_SocketClient) return; // Показываем диаложек для ввода адреса (или хоста) сервера wxString addr_str = wxGetTextFromUser(_("Введите адрес сервера:"), _("Соединение"), wxT("localhost")); // Настраиваем адрес для подключения wxIPV4address addr; addr.Service(3000); addr.Hostname(addr_str); // Создаем сокет m_SocketClient = new wxSocketClient; // Привязываем его к нашей форме m_SocketClient->SetEventHandler(*this, ID_SOCKET_CLIENT); m_SocketClient->SetNotify(wxSOCKET_CONNECTION_FLAG|wxSOCKET_INPUT_FLAG); m_SocketClient->Notify(TRUE); // Если все прошлоу дачно, то… if(m_SocketClient) { // Пытаемся сконнектиться с сервером m_SocketClient->Connect(addr, false); // Ожидание соединения 10 сек. m_SocketClient->WaitOnConnect(10); // Если соединение установлено... if(m_SocketClient->IsConnected()) { // Говорим что все ОК m_LOGTextCtrl->AppendText(_("Соединение выполнено...rn")); } else { m_LOGTextCtrl->AppendText(_("Не удалось выполнить соединение...rn")); } } } void MyFrame::OnDisconnect(wxCommandEvent & event) { // При нажатии на кнопку ДИСКОННЕКТ прерываем соединение и удаляем сокет if(m_SocketClient) { if(m_SocketClient->IsConnected()) m_SocketClient->Close(); m_SocketClient->Destroy(); } m_SocketClient = NULL; } void MyFrame::OnClientSocketEvent(wxSocketEvent & event) { // Пока здесь пусто… смотрим дальше } void MyFrame::OnExit(wxCommandEvent & event) { Close(); } class MyApp : public wxApp { public: virtual bool OnInit(); }; IMPLEMENT_APP(MyApp) bool MyApp::OnInit() { MyFrame * frame = new MyFrame(NULL); frame->Show(); SetTopWindow(frame); return true; } Запустил… работает… и даже коннектится - БУГАГА!!!, - Наверное скажешь ты, дорогой друг, и будешь совершенно прав. Стабильный коннект – залог здоровья и душевного равновесия любого АйТишника… но коннект – это еще не всё. Надо же было еще организовать передачу данных Для этого наш админ в проекте сервера дописал обработчик событий от клиентских сокетов
CODE
void MyFrame::OnClientSocketEvent(wxSocketEvent & event) { wxSocketBase *sock = event.GetSocket(); wxIPV4address addr; wxChar buffer[1024]; sock->GetLocal(addr); wxString message; switch(event.GetSocketEvent()) { // Если к нам пришло сообщение case wxSOCKET_INPUT: { // Прочитали его в буфер sock->Read(buffer, 1024*sizeof(wxChar)); // Если ошибка - поругались if(sock->Error()) { m_LOGTextCtrl->AppendText(_("Ошибка чтения данныхrn")); } else { // Дописали в конец нолик buffer[(size_t)ceil((double)(sock->LastCount()/sizeof(wxChar)))] = 0; // А в начало – от кого пришло message = wxString::Format(wxT("%s> %s"), addr.IPAddress(), buffer); // Добавили в ЛОГ m_LOGTextCtrl->AppendText(message+wxString(wxT("rn"))); // Раздали всем клиентам for(wxSocketList::Node * node = m_Clients.GetFirst(); node; node = node->GetNext()) { node->GetData()->Write(message.GetData(), message.Length()*sizeof(wxChar)); } } break; } case wxSOCKET_LOST: { // Если дисконнект – удалили из списка for(wxSocketList::Node * node = m_Clients.GetFirst(); node; node = node->GetNext()) { if(sock == node->GetData()) { m_Clients.DeleteNode(node); break; } } // Убили сокет sock->Destroy(); // Записали в ЛОГ m_LOGTextCtrl->AppendText(wxString::Format(_("Дисконнект: %srn"), addr.IPAddress())); break; } default:; } } Скомпилил – вроде работает… не ругается.. но чтобы проверить, надо еще добавить обработчик и в клиентское приложеньице… да… без этого никак…
CODE
void MyFrame::OnClientSocketEvent(wxSocketEvent & event) { wxSocketBase *sock = event.GetSocket(); wxIPV4address addr; wxChar buffer[1024]; switch(event.GetSocketEvent()) { // Если пришло сообщение case wxSOCKET_INPUT: { // Прочитали sock->Read(buffer, 1024*sizeof(wxChar)); // Поругались, если ошибка if(sock->Error()) { m_LOGTextCtrl->AppendText(_("Ошибка чтения данныхrn")); } else { // Дописали в конец нолик buffer[(size_t)ceil((double)(sock->LastCount()/sizeof(wxChar)))] = 0; // Отобразили m_LOGTextCtrl->AppendText(wxString::Format(wxT("%srn"), buffer)); } break; } case wxSOCKET_LOST: { // Если дисконнект sock->GetLocal(addr); // Обнулили наш сокет if(sock == m_SocketClient) { m_SocketClient = NULL; } // Удалили sock->Destroy(); // Сказали что произошел дисконнект m_LOGTextCtrl->AppendText(wxString::Format(_("Дисконнект: %srn"), addr.IPAddress())); break; } default:; } } Скомпилил – вроде работает… к тому же неплохо… Отнес юзверям, заинсталил, попрыгал для виду с бубном вокруг компутеров, забрал у одного ящик пива, у другого рыбки, у третьего печенько (а как же, за все хорошее надо платить… да и юзвери вроде не против были) и вернулся к себе в админку спать (завтра ему еще предстоял тяжелый день.. т.к. глупость человеческая не знает границ, а юзвери, ведь, они тоже люди Перепечатка или использование сего творения где-либо кроме www.realcoding.net без письменного согласия автора ЗАПРЕЩАЕТСЯ Copyright © Vladimir (T-Rex) Tryapichko Источник: "wxWidgets programming" by t_rex Рубрика: WxWidgets
Подгрузка через AJAX HTML-кода, содержащег....
При разработке CMS S.Builder наша команда активно использовала AJAX. Теперь вот решили поделиться накопленным опытом. Начнем с этого хабратопика. Не буду здесь затрагивать различные фреймворки и библиотеки. Свой код всегда роднее. Для работы с AJAX-ом в S.Builder написана библиотека sbAJAX. Можете качать и пользоваться :). В этом файле есть функция sbEvalJS. Для тех, кто не знает, объясню. При подгрузке через AJAX и вставке на страницу HTML-кода, содержащего JavaScript, JavaScript выполняться не будет или полезут баги. Эта функция как раз решает поставленную задачу.
Подробнее... |
Рубрика: AJAX
| Добавлено: 19.11.2008
Обзор нового релиза самой мощной Ajax библ....
Хотя наш обзор немного запоздал, оригинальный Dojo 1.2 вышел в релизной версии ещё 6-го октября, но сейчас мы наверстаем упущенное. И так, Dojo Toolkit — это самая мощная и гибкая ajax-библиотека из всех, что есть на рынке, она активно развивается и имеет большое комьюнити. Кстати, это самое комьюнити, совместно с компанией Sitepen, имеет ещё несколько проектов, среди которых и Cometd и некоторые другие, не менее интересные, о которых мы скоро вам расскажем. Сегодня же все внимание на флагманский продукт — Dojo 1.2.
Подробнее... |
Рубрика: AJAX
| Добавлено: 19.11.2008
Firebug 1.3 и 1.4 alpha — что нового и инт....
Если вы профессиональный веб-разработчик и постоянно имеете дело с разработкой и отладкой сложных AJAX приложений, то наверняка знаете и используете Firebug — плагин для браузера Firefox, предназначенный для отладки и исследования веб-приложений. Текущая его версия, 1.2х достаточно стабильная и функциональна, чтобы помочь в 99% проблем, которые могут возникнуть при разработке. Но и этот инструмент не лишён если не недостатков, то некоторых фич, которые могли бы облегчить работу. И даже идеальный инструмент можно сделать ещё более идеальным, как бы это не звучало.
Подробнее... |
Рубрика: Вебмастеру
| Добавлено: 19.11.2008
Остальные статьи: |
Цитата дня (все,добавить):
|
Realcoding.NET
© 2003-2008 |
Контакты |
Реклама на сайте
|