
Опытный
 
Профиль
Группа: Участник
Сообщений: 427
Регистрация: 13.6.2007
Где: Молдова, Кишинев
Репутация: нет Всего: 4
|
WinSock_v2.0 - WSA*Accept/Read/Send & Events Использование WinSock с событиями; [hr][/hr] Введение Общие вопросы Событийная модель в WinSock Часть 1: Подключение WinSock_v2.0; Часть 2: Инициализация начальных структуру/функций/событий; Подготовка к прослушиванию подключений; Часть 3: Прослушивание подключений в отдельном потоке; Часть 4: Установка соединения; Часть 5: Обработка сообщений от клиента; Часть 6: Дополнительные функции; Заключение Список использованной литературы и Интернет ресурсов;
------ # Введение:Доброе время суток. Достаточно долгое время я пытался перейти на WSA* функции, но постоянно сталкивался с разнообразными проблемами, ответы на которые в интернете не находил. Так же, мягко говоря, расстраивал тот факт, что статей именно по WinSock_v2.0 (WSA*) нет, не одной нормально статьи, что собственно и побудило меня к написанию данной статьи. # Общие вопросы:# Вопрос: Под какую среду программрования написана статья? # Ответ: C++ Builder, но переделать под любую другую среду программирования проблем не составит. # Вопрос: Под какую операционную систему написан код? # Ответ: Семейство Windows; # Вопрос: Чем отличаются обычные функции от WSA*? # Ответ:- Обычные функции - это виндовская реализация интерфейса Беркли-сокетов. - WSA*() - Это родные функции операционной системы Windows; (Windows Socket API) # Вопрос: Что будет рассматриваться в данной статье? # Ответ: В данной статье будет рассматриваться модель Windows Socket (WinSock) v2.0; - WSA* функции. - Использование событий. # Вопрос: Какие проблемы поможет понять/решить данная статья? # Ответ: Данная статья поможет вам решить следующие проблемы: - Проблемы с подключением к серверу через некоторое время после последнего подключения.
- Accept/WSAAccept когда в режиме ожидания долгое время, то после возникают существенные проблемы с подключением. - Проблемы с принятием данных WSARecv() при помощи событий.
- Проблема с возвратом WSARecv() значения -1; # Событийная модель в WinSock:Цитата(Gregory Liokumovich) | Сразу оговорюсь, что поскольку я занимаюсь программированием под Windows, то и говорить буду про реализацию сокетов под эту платформу. WinSock позволяет работать с сокетами в рамках трех подходов, против двух классических. А именно:
- Обычная блокирующая передача данных;
- Ннеблокирующая передача данных;
- Событийно ориентированная передача данных.
Лично я предпочитаю третий метод, и ниже попытаюсь изложить основные причины.
# Немного вводной информации:
Основные функции:
- WSACreateEvent() --- создать новое событие;
- WSACloseEvent() --- удалить событие;
- WSAEventSelect() --- связать событие с сокетом и его внутренним событием (пришли новые данные, готов к передаче и т.д.);
- WSAWaitForMultipleEvents() --- ожидание событий;
Таким образом, алгоритм примерно такой: создаем сокет, создаем для него событие, связываем, далее ожидаем от него событий и реагируем на них. После того, как известно, что сокет готов к приему (передаче) данных, я предпочитаю использовать блокирующие операции чтения (отправки) данных. При этом, если например не все данные будут прочитаны из буфера, соответствующее событие будет автоматически активировано снова.
Что интересно тип WSAEVENT является просто переопределением типа HANDLE со всеми вытекающими отсюда возможностями.
# Причина первая, эстетическая:
Общеизвестный факт, что все ПО развивается в сторону унификации интерфейса, это позволяет значительно упростить и ускорить его использование для пользователя. Вполне логично, что унификация программного API приводит к более эффективному и грамотному его использованию программистами, которые выступают в роли потребителей API. Так вот, с этой точки зрения событийный подход с известной всем windows программистам функцией WaitForMultipleObject (пусть даже с каким-то префиксом) явно упрощает использование сокетов.
Кроме того, лично мне следующий фрагмент кода:
Код | res = WSAWaitForMultipleObject(5, Events, FALSE, WSA_INFINITE, FALSE); if (res == WSA_WAIT_FAILED) { //... } index = res; WSAEnumNetworkEvents(sockets[index], Events[index], &info); if (info.lNetworkEvents & FD_READ) { // принимаем новые данные; }
|
кажется намного более понятным и приятным чем этот:
Код | int res = select(0, &read, &write, &error, NULL); if (res == SOCKET_ERROR) { //... } for(int i = 0; i < socket.size(); i++) { if (FD_ISSET(socket[i], &read)) // принимаем данные }
|
Как видно и вышенаписанного в событийном подходе мы исходим от события, мы знаем для какого сокета есть новости. Во втором же случае, мы не знаем, что произошло и вынуждены перебирать все варианты. В первом случае мы оперируем массивами и событиями, во втором же случае неочевидными структурами и макросами.
# Причина вторая, элегантная: Используя select или WSAWaitForMultipleEvents мы в любом случае не можем удалять или добавлять соединения во время работы данных функций, так как это приведет к сбою. А это наверняка придется делать.
Если в программе, только один поток то проблемы нет, но не надо забывать что обе эти функции поддерживают не более 64 сокетов. А если надо больше? писать select за select'ом и ставить выход по таймауту --- это очень некрасиво, но (что намного более важно) мы тут же теряем скорости реакции или скатываемся до элементарного опроса готов/не готов. А если надо поддерживать 500 соединений? Время реакции будет ужасным. В связи с этим хочу также отметить, что, по-моему, WSAWaitForMultipleEvents работает быстрее, чем select в режиме опроса, но это субъективное мнение --- цифрами подтвердить не могу.
Так вот, единственный разумный выход из описанной ситуации который мне видится --- это многопоточность. Каждый поток работает со своими соединениями и все замечательно. Но тут возникает (как это всегда бывает с потоками) проблемы взаимной синхронизации.
Предположим поток 1 хочет добавить сокет в обработчик потока 2. В это время поток 2, выполняет select (или WSAWaitForMultipleEvents). Что прикажете делать? Что делать в случае select я, честно говоря, даже не знаю. При использовании событий я нашел, по-моему, неплохой и достаточно красивый выход. Создается одно пустое событие, которое не привязано к конкретному сокету. Это событие добавляется к остальным, которых дожидается WSAWaitForMultipleEvents (при этом число сокетов на поток уменьшается до 63). И вот в тот самый момент, когда мне надо вывести Поток два из "комотозного" состояния, я просто активирую это событие и все. То есть я использую события от сокета вместе и наравне с другими событиями. И отсюда вытекает еще одна более принципиальная причина.
# Причина третья, принципиальная: Если задуматься на тему что такое поступление новых данных в сокет или, например, что такое закрытие сокета с другой стороны. Можно придти к простому выводу --- это события. Такие же события, как наступления времени Х или любое другое событие в программе. Более того, абсолютнозаконна постановка вопроса: "ждем пока произойдет какое-нибудь событие или пока придут данные от сокета". Поэтому это правильно и логично, что и работа с сокетами должна строится на событиях.
|
# Часть 1: Подключение WinSock_v2.0; Код | // Заметка: Данные строчки необходимо написать после всех подключений модулей; #pragma comment(lib, "ws2_32.lib") #include <winsock2.h>
|
# Часть 2: Инициализация начальных структуру/функций/событий; Подготовка к прослушиванию подключений; Код | // Шаг 1: Инициализация дополнительных, нужных переменных; int iRes = 0; int iErrorCode = 0; WSADATA wsaData; SOCKET m_sListen; // Слушающий сокет; WSAEVENT m_hEvent; // Событие FD_ACCEPT; HANDLE m_hKillEvent; bool m_bShouldStopListenThread = false; HANDLE hThread; // Хэндел слушающего потока; UINT threadID; // Шаг 2: Инициализация WinSock_v2.0; iRes = WSAStartup(MAKEWORD(2, 2), &wsaData); if(iRes != 0) { iErrorCode = WSAGetLastError(); WLog("WSAStartup() failed with error: %d", iErrorCode); return Result; } // Шаг 3: Инициализация сокета; m_sListen = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED); if(m_sListen == INVALID_SOCKET) { iErrorCode = WSAGetLastError(); WLog("WSASocket() failed with error: %d", iErrorCode); return Result; } // Шаг 4: Создаем пустое событие; m_hEvent = WSA_INVALID_EVENT; m_hEvent = WSACreateEvent(); if (m_hEvent == WSA_INVALID_EVENT) { iErrorCode = WSAGetLastError(); closesocket(m_sListen); WLog("WSACreateEvent() failed with error: %d", iErrorCode); return Result; } // Шаг 5: Задаемое событие/я, которое/ые нужно будет обрабатывать; iRes = WSAEventSelect(m_sListen, m_hEvent, FD_ACCEPT); if (iRes == SOCKET_ERROR) { iErrorCode = WSAGetLastError(); closesocket(m_sListen); WLog("WSAEventSelect() failed with error: %d", iErrorCode); return Result; } // Шаг 6: Заполянем спец.структуру; IP-Адрес; Порт; sockaddr_in a; a.sin_family = AF_INET; a.sin_port = htons(cPort); // Задаём прослушивающий порт; a.sin_addr.S_un.S_addr = htonl(INADDR_ANY); // Для всех сетевых интерфейсов; // Шаг 7: Занимаем порт в системе; iRes = bind(m_sListen, (sockaddr*)&a, sizeof(sockaddr)); if(iRes == SOCKET_ERROR) { iErrorCode = WSAGetLastError(); closesocket(m_sListen); WLog("bind() to the port '%d' failed with error: %d", cPort, iErrorCode); return Result; } // Шаг 8: Устанавливаем количество одновременных прослушиваний для сокета; iRes = listen(m_sListen, 5); if(iRes == SOCKET_ERROR) { iErrorCode = WSAGetLastError(); closesocket(m_sListen); WLog("listen() failed with error: %d", iErrorCode); return Result; } // Шаг 9: Создаём спец.поток для прослушивания подключений; hThread = (HANDLE)_beginthreadex(NULL, // Защита 0, // Размер стэка - по умолчанию; ListenThreadProc, // Имя функции; (void*) this, // Передача указателя на данный класс; 0, // Инициализация флага; &threadID); // Адрес потока;
|
# Часть 3: Прослушивание подключений в отдельном потоке; Код | // Прототип функции; unsigned __stdcall ListenThreadProc(LPVOID lParam); ... WSANETWORKEVENTS m_events; ... unsigned __stdcall ListenThreadProc(LPVOID lParam) { // Шаг 1: Приводим аргумент к классу, с которого было создание данного потока; TSocket* pThis = reinterpret_cast<TSocket*>(lParam); // Шаг 2: Инициализация дополнительных, нужных переменных; DWORD dwRes = 0; int iRes = 0; int iErrorCode = 0; bool bShouldStop = false; // Шаг 3: Создаём цикл для прослушивания сокета; while(!pThis->m_bShouldStopListenThread) { // Шаг 4: Делаем задержку на прослушивание события о выходе из цикла; // Если событие сработало, то мы выходим; if (WaitForSingleObject(pThis->m_hKillEvent, 100) == WAIT_OBJECT_0) { pThis->m_bShouldStopListenThread = true; break; } // ---- // Шаг 4: Ожидаем какого-либо события; dwRes = WSAWaitForMultipleEvents(1, // Кол-во прослушивающих событий; &pThis->m_hEvent, // Указатель на событие; FALSE, // Ожидать пока случиться все события; 100, // Кол-во мсек для прослушивания; (WSA_INFINITE - бесконечное ожидание) FALSE); // Используется для перекрытого ввода-ввыода; // ---- // Шаг 5: Проверка возвращаемого значения; if (dwRes == WSA_WAIT_TIMEOUT) continue; // Шаг 6: Получения списка событий, которые произошли; iRes = WSAEnumNetworkEvents(pThis->m_sListen, pThis->m_hEvent, &pThis->m_events); if (iRes == SOCKET_ERROR) { iErrorCode = WSAGetLastError(); WLog("[%s] %s: %d", "ListenThreadProc", "WSAEnumNetworkEvents() failed with error", iErrorCode); pThis->m_bShouldStopListenThread = true; break; } // ---- pThis->ProcessMessage(); } // ---- return 0; // Нормальный выход из потока; } //---------------------------------------------------------------------------
void __fastcall TSocket::ProcessMessage() { // Шаг 7: Осуществляем выбор события; // Заметка: Не советую использовать switch(), так как если у вас несколько событий, то произойдёт обработка только одного события; if(m_events.lNetworkEvents & FD_ACCEPT) { if (m_events.iErrorCode[FD_ACCEPT_BIT] == 0) { OnAccept(); } else { m_iErrorCode = WSAGetLastError(); WLog("[%s->%s] %s: %d", "TSocket", "ProcessMessage", "FD_ACCEPT failed with error", m_iErrorCode); m_bShouldStop = true; } } } //---------------------------------------------------------------------------
|
# Часть 4: Установка соединения; Код | class TClientContext: { friend class TSocket; friend DWORD WINAPI ServerWorkerThread(LPVOID lParam); // ---- private: TSocket *m_pSocket; // ---- DWORD m_dwWaitForSingleObjectValue; DWORD m_dwWaitForMultipleEvents; DWORD m_dwMaxSizeOfReadBuf; DWORD m_dwWaitEventsCount; // Input Elements for Winsock WSABUF m_wsaInBuf; BYTE *m_pInBuf; DWORD m_dwRecvBytes; DWORD m_dwRecvFlags; int m_iRecvRes; WSAOVERLAPPED m_WSARecvOverlapped; // ---- HANDLE m_ThreadHandle; DWORD m_ThreadId; // ---- int m_iRes; DWORD m_dwRes; int m_iErrorCode; // --- SOCKADDR_IN m_SockAddr; IN_ADDR m_SockInAddr; AnsiString m_IPAddress; // ---- WSAEVENT m_EventArray[2]; WSAEVENT m_hEvent; WSANETWORKEVENTS m_NetworkEvents; HANDLE m_hKillEvent; // ---- void __fastcall ProcessMessage(); // ---- void __fastcall OnRead(); void __fastcall OnDisconnect(); // ---- void __fastcall PacketCore(LPBYTE Packet, int len); void __fastcall DisconnectUser(); public: TClientContext() {}; ~TClientContext() {}; // ---- void __fastcall InitializingClass(); void __fastcall PrepareClass(); bool __fastcall PrepareSocket(); void __fastcall PrepareSocketToDelete(); void __fastcall PrepareClassToDelete(); void __fastcall StopListen() { m_bShouldStop = true; } bool __fastcall GetListenStatus() { return !m_bShouldStop; }; // ---- DWORD __fastcall GetWaitEventsCount() { return m_dwWaitEventsCount; }; DWORD __fastcall GetWaitForSingleObjectValue() { return m_dwWaitForSingleObjectValue; } DWORD __fastcall GetWaitForMultipleEvents() { return m_dwWaitForMultipleEvents; } DWORD __fastcall GetMaxSizeOfReadBuf() { return m_dwMaxSizeOfReadBuf; }; } //---------------------------------------------------------------------------
void __fastcall TSocket::OnAccept() { // Шаг 1: Проверка глобальный переменных; if (g_bTimeToKill || g_bDisconnectAll) return; // Шаг 2: Инициализация дополнительных, нужных переменных; int nLen = sizeof(SOCKADDR_IN); // Шаг 3: Установка соединения; // Заметка: Советую использовать именно функцию Accept(); // WSAAccept() возвращает управления потоку сразу, без проверки успошности; // Accept() возвращает настоящую проверку успешности; ClientSocket = accept(g_sListen_TCP, (LPSOCKADDR)&SockAddr, &nLen); if (ClientSocket == SOCKET_ERROR) { m_iErrorCode = WSAGetLastError(); if (m_iErrorCode != WSAEWOULDBLOCK) { WLog("[%s->%s] %s: %d", "TSocket", "OnAccept", "accept() failed with error", m_iErrorCode); return; } } // ---- AddNewUser(ClientSocket, SockAddr); } //---------------------------------------------------------------------------
TClientContext* __fastcall TSocket::AddNewUser(SOCKET sSocket, SOCKADDR_IN SockAddr) { TClientContext* pContext = new TClientContext(); // ---- pContext->PrepareClass(); // ---- pContext->m_sListen = sSocket; pContext->m_SockAddr = SockAddr; pContext->m_iProtocolMode = m_iProtocolMode; // ---- memcpy(&pContext->m_SockInAddr, &pContext->m_SockAddr.sin_addr, sizeof(IN_ADDR)); pContext->m_IPAddress = inet_ntoa(pContext->m_SockInAddr); // ---- bResult = pContext->PrepareSocket(); // ---- if(bResult) { pContext->m_ThreadHandle = CreateThread(NULL, 0, ClientWorkerThread, (void*)pContext, 0, &pContext->m_ThreadId); } else { pContext->PrepareSocketToDelete(); pContext->PrepareClassToDelete(); delete pContext; pContext = NULL; } // ---- return pContext; } //---------------------------------------------------------------------------
|
# Часть 5: Обработка сообщений от клиента; Код | // Прототип функции; DWORD WINAPI ServerWorkerThread(LPVOID lParam); ... DWORD WINAPI ServerWorkerThread(LPVOID lParam) { // Шаг 1: Получение указателя на класс от клиента; TClientContext *pThis = reinterpret_cast<TClientContext*>(lParam); // Шаг 2: Инициализация дополнительных, нужных переменных; int iRes = 0; DWORD dwRet = 0; int iErrorCode = 0; // Шаг 3: Создаём цикл для прослушивания сокета; while(pThis->GetListenStatus()) { if (WaitForSingleObject(pThis->m_hKillEvent, pThis->GetWaitForSingleObjectValue()) == WAIT_OBJECT_0) { pThis->StopListen(); break; } // ---- dwRes = WSAWaitForMultipleEvents(pThis->GetWaitEventsCount(), pThis->m_EventArray, FALSE, pThis->GetWaitForMultipleEvents(), FALSE); // ---- switch(dwRes) { case WSA_WAIT_TIMEOUT: { continue; } break; case WSA_WAIT_FAILED: { iErrorCode = WSAGetLastError(); // ---- pThis->WLog(1, "[%s->%s] %s: %d; IP-Address: %s", "TClientContext", "ClientThreadProc", "WSAWaitForMultipleEvents() failed with error", iErrorCode, pThis->m_IPAddress.c_str()); // ---- pThis->StopListen(); } break; } // ---- if(!pThis->GetListenStatus()) break; // ---- dwEventIndex = dwRes - WSA_WAIT_EVENT_0; // ---- iRes = WSAEnumNetworkEvents(pThis->m_sListen, pThis->m_EventArray[dwEventIndex], &pThis->m_NetworkEvents); // ---- if (iRes == SOCKET_ERROR) { iErrorCode = WSAGetLastError(); // ---- pThis->WLog(1, "[%s->%s] %s: %d; IP-Address: %s", "TClientContext", "ClientThreadProc", "WSAEnumNetworkEvents() failed with error", iErrorCode, pThis->m_IPAddress.c_str()); // ---- pThis->StopListen(); // ---- break; } // ---- pThis->ProcessMessage(); } // ---- pThis->PrepareSocketToDelete(); // ---- // Шаг 9: Удаление записи из списка; pThis->m_pSocket->DisconnectUser(pThis->m_dwListIndex); } //---------------------------------------------------------------------------
void __fastcall TClientContext::ProcessMessage() { // Шаг 11: Осуществляем выбор события; // Заметка: Не советую использовать switch(), так как если у вас несколько событий, то произойдёт обработка только первого события; if(m_NetworkEvents.lNetworkEvents & FD_READ) { if (m_NetworkEvents.iErrorCode[FD_READ_BIT] == 0) { OnRead(); } else { WLog("[%s->%s] %s: %d; IP-Address: %s", "TClientContext", "ProcessMessage", "FD_READ failes with error", m_NetworkEvents.iErrorCode[FD_READ_BIT], m_IPAddress); } } // ---- if(m_NetworkEvents.lNetworkEvents & FD_CLOSE) { if (m_NetworkEvents.iErrorCode[FD_CLOSE_BIT] == 0) { OnDisconnect(); } else { WLog("[%s->%s] %s: %d; IP-Address: %s", "TClientContext", "ProcessMessage", "FD_CLOSE failes with error", m_NetworkEvents.iErrorCode[FD_CLOSE_BIT], m_IPAddress); m_bShouldStop = true; } } } //---------------------------------------------------------------------------
void __fastcall TClientContext::OnRead() { ZeroMemory(&m_WSARecvOverlapped, sizeof(WSAOVERLAPPED)); // ----- m_wsaInBuf.len = GetMaxSizeOfReadBuf(); m_wsaInBuf.buf = m_pInBuf; // ---- m_iRecvRes = WSARecv(m_sListen, &m_wsaInBuf, 1, &m_dwRecvBytes, &m_dwRecvFlags, &m_WSARecvOverlapped, NULL); // ---- if(m_iRecvRes == SOCKET_ERROR) { m_iErrorCode = WSAGetLastError(); if(m_iErrorCode != WSA_IO_PENDING) { DisconnectUser(); } } // ---- if (m_dwRecvBytes > 0 && m_dwRecvBytes < GetMaxSizeOfReadBuf()) { PacketCore(m_sListen, m_pInBuf, m_dwRecvBytes); } } //---------------------------------------------------------------------------
void __fastcall TClientContext::InitializingClass() { m_dwMaxSizeOfReadBuf = 1024; // ---- m_pInBuf = new BYTE[GetMaxSizeOfReadBuf()]; // ---- m_wsaInBuf.buf = m_pInBuf; m_wsaInBuf.len = GetMaxSizeOfReadBuf(); // ---- m_dwWaitForSingleObjectValue = 100; m_dwWaitForMultipleEvents = 100; // ---- m_dwWaitEventsCount = 0; memset(m_EventArray, 0, sizeof(WSAEVENT) * 2); // ---- m_hKillEvent = CreateEvent(NULL, TRUE, FALSE, NULL); // ---- PrepareClass(); } //---------------------------------------------------------------------------
void __fastcall TClientContext::PrepareClass() { m_sListen = INVALID_SOCKET; // ---- ZeroMemory(m_pInBuf, GetMaxSizeOfReadBuf()); // ---- m_iErrorCode = 0; m_dwRecvBytes = 0; m_iRecvRes = 0; // ---- memset(&m_SockAddr, 0, sizeof(SOCKADDR_IN)); memset(&m_SockInAddr, 0, sizeof(IN_ADDR)); m_IPAddress = ""; // ---- m_hEvent = WSA_INVALID_EVENT; // ---- m_bManualStop = false; m_bShouldStop = false; } //---------------------------------------------------------------------------
bool __fastcall TClientContext::PrepareSocket() { m_hEvent = WSACreateEvent(); if (m_hEvent == WSA_INVALID_EVENT) { m_iErrorCode = WSAGetLastError(); WLog("[%s] %s: %d; IP-Address: %s", "TClientContext", "WSACreateEvent() failed with error", m_iErrorCode, m_IPAddress.c_str()); m_bShouldStop = true; } // ---- m_iRes = WSAEventSelect(m_sListen, m_hEvent, FD_READ | FD_CLOSE); if (m_iRes == SOCKET_ERROR) { m_iErrorCode = WSAGetLastError(); WLog("[%s] %s: %d; IP-Address: %s", "TClientContext", "WSAEventSelect() failed with error", m_iErrorCode, m_IPAddress.c_str()); m_bShouldStop = true; } // ---- m_dwWaitEventsCount = 0; m_EventArray[m_dwWaitEventsCount] = m_hEvent; m_dwWaitEventsCount = 1; // ---- return !m_bShouldStop; } //---------------------------------------------------------------------------
void __fastcall TClientContext::PrepareSocketToDelete() { if(m_sListen != NULL && m_sListen != INVALID_SOCKET) TCloseSocket(m_sListen); m_sListen = INVALID_SOCKET; // ---- if(m_hEvent != NULL && m_hEvent != WSA_INVALID_EVENT) WSACloseEvent(m_hEvent); m_hEvent = WSA_INVALID_EVENT; } //---------------------------------------------------------------------------
void __fastcall TClientContext::PrepareClassToDelete() { m_bShouldStop = true; // ---- if(m_sListen != NULL && m_sListen != INVALID_SOCKET) TCloseSocket(m_sListen); m_sListen = INVALID_SOCKET; // ---- if(m_hEvent != NULL && m_hEvent != WSA_INVALID_EVENT) WSACloseEvent(m_hEvent); m_hEvent = WSA_INVALID_EVENT; // ---- if(m_hKillEvent != NULL && m_hKillEvent != INVALID_HANDLE_VALUE) CloseHandle(m_hKillEvent); m_hKillEvent = INVALID_HANDLE_VALUE; // ---- if(m_pInBuf != NULL) { delete[] m_pInBuf; m_pInBuf = NULL; } } //---------------------------------------------------------------------------
|
# Часть 6: Дополнительные функции; Код | // Функция для закрытия сокета; void TCloseSocket(SOCKET Socket) { try { shutdown(Socket, SD_BOTH); closesocket(Socket); } catch(...) {} }
// Функция отправки данных; void SendBufWSA(SOCKET Socket, LPBYTE Packet, int len) { try { WSABUF DataBuf; // ---- DataBuf.len = len; DataBuf.buf = Packet; // ---- DWORD SendBytes; WSAOVERLAPPED SendOverlapped; memset(&SendOverlapped, 0, sizeof(WSAOVERLAPPED)); // ---- WSASend(Socket, &DataBuf, 1, &SendBytes, 0, &SendOverlapped, NULL); } catch(...) {} }
|
# Заключение:Цитата | В выше изложенном материале изложены основные, не все, принципы написания серверного приложения используемого WinSock_v2.0 с использованием событий. Мы рассмотрели некоторые важные моменты, сделали акцент на некоторых нюансах. Подытоживая, можно сказать, что мы научились использовать асинхронные функции WinSock и работать с событиями.
|
# Список использованной литературы и Интернет ресурсов:Цитата |
- MSDN;
- Google;
- Wiki;
- CodeProject;
- Событийная модель в WinSock;
- Э. Джонс, Д. Оланд - Программирование в сетях Microsoft® Windows; (МАСТЕР-КЛАСС); (2002 год)
- Джеффри Рихтер, Джейсон Кларк - Программирование серверных приложений для Windows 2000; (МАСТЕР-КЛАСС); (2001 год)
- Джонсон М.Харт - Системное программирование в среде Windows; (Третье издание); (2005 год)
|
P.S. -> Статья будет по мере возможности дописываться. Автор статьи Дамьян Александр, 21.08.2009. -- Отредактировано 13.10.2009 Это сообщение отредактировал(а) MuForum - 13.10.2009, 10:27
--------------------
"Чтобы правильно задать вопрос, нужно знать большую часть ответа!" (Р. Шекли)
|