Windows套接字I/O模型(3) -- WSAAsyncSelect模型
来源:互联网 发布:淘宝卖特产没证可以吗 编辑:程序博客网 时间:2024/06/05 15:16
一、WSAAsyncSelect模型介绍
利用WSAAsyncSelect模型,结合windows窗口消息循环,应用程序可以在一个套接字上,接收以windows消息为基础的网络事件通知。要想使用WSAAsyncSelect模型,首先必须创建一个窗口,并为窗口提供一个窗口过程支持函数。
int WSAAsyncSelect( _In_ SOCKET s, _In_ HWND hWnd, _In_ unsigned int wMsg, _In_ long lEvent);
s
指定我们感兴趣的套接字。 hWnd
指定一个窗口句柄,它表示网络事件发生之后,收到消息通知的那个窗口。 wMsg
表示在网络事件发生时,窗口收到的消息。 lEvent
代表一个掩码,指定应用程序感兴趣的网络事件的组合。事件类型包括:FD_READ,FD_WRITE,FD_CLOSE,FD_CONNECT,FD_ACCEPT。对于FD_CONNECT,FD_ACCEPT,服务端一般使用FD_ACCEPT,客户端一般使用FD_CONNECT。若将lEvent
设置为0,则表示停止接收该套接字上的所有网络事件通知。
一旦调用WSAAsyncSelect在套接字上启用了事件通知,除非明确调用closesocket,或者再次调用WSAAsyncSelect重新设置网络事件类型,否则,事件通知总是有效的。
WSAAsyncSelect模型需要结合windows窗口来使用,不适用于没有界面的服务程序。建议使用WSAEventSelect模型来代替。
二、示例
因为重点不在界面编程上面,所以示例中使用TraceMsgA
打印信息到visual studio的输出窗口或debugview。 InitWindow
函数用于创建一个简单的、空白的窗口,将句柄存储在全局变量g_hwnd
中。 WndProc
为窗口的处理过程函数。
服务端在有新的客户端连接上时,给客户端发送“hello, I’m server.”消息,在服务端退出时,关闭所有客户端连接。
客户端连接上服务端之后,接收服务端的消息。
2.1 服务端
#include <winsock2.h>#include <vector>#include <strsafe.h>#pragma comment(lib, "Ws2_32.lib")const u_short kPort = 10001;const std::string kHelloServer = "hello, I'm server.";#define WUM_SOCKET (WM_USER+1)HWND g_hwnd;SOCKET socket_srv = INVALID_SOCKET;std::vector<SOCKET> clients;void TraceMsgA(const char *lpFormat, ...) { if (!lpFormat) return; char *pMsgBuffer = NULL; unsigned int iMsgBufCount = 0; va_list arglist; va_start(arglist, lpFormat); HRESULT hr = STRSAFE_E_INSUFFICIENT_BUFFER; while (hr == STRSAFE_E_INSUFFICIENT_BUFFER) { iMsgBufCount += 1024; if (pMsgBuffer) { free(pMsgBuffer); pMsgBuffer = NULL; } pMsgBuffer = (char*)malloc(iMsgBufCount * sizeof(char)); if (!pMsgBuffer) { break; } hr = StringCchVPrintfA(pMsgBuffer, iMsgBufCount, lpFormat, arglist); } va_end(arglist); if (hr == S_OK) { OutputDebugStringA(pMsgBuffer); } if (pMsgBuffer) { free(pMsgBuffer); pMsgBuffer = NULL; }}LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam){ int err; SOCKET s; char buf[100] = { 0 }; switch (message) { // (5) case WUM_SOCKET: if (WSAGETSELECTERROR(lParam)) { closesocket((SOCKET)wParam); break; } switch (WSAGETSELECTEVENT(lParam)) { case FD_ACCEPT: s = accept((SOCKET)wParam, NULL, NULL); clients.push_back(s); WSAAsyncSelect(s, g_hwnd, WUM_SOCKET, FD_READ | FD_WRITE | FD_CLOSE); TraceMsgA("new connection\n"); err = send(s, (const char*)kHelloServer.c_str(), kHelloServer.length(), 0); if (err == SOCKET_ERROR) { TraceMsgA("send failed, GLE: %d\n", WSAGetLastError()); break; } break; case FD_READ: err = recv((SOCKET)wParam, buf, 100, 0); if (err > 0) { TraceMsgA("recv: %s\n", buf); } else if (err == 0) { TraceMsgA("connection closed\n"); closesocket((SOCKET)wParam); } else { TraceMsgA("recv failed, GLE: %d\n", WSAGetLastError()); closesocket((SOCKET)wParam); } break; case FD_WRITE: break; case FD_CLOSE: closesocket((SOCKET)wParam); for (std::vector<SOCKET>::iterator it = clients.begin(); it != clients.end(); it++) { if (*it == (SOCKET)wParam) { clients.erase(it); break; } } break; } break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0;}void InitWindow(HINSTANCE hInstance) { WCHAR szWindowClass[100] = L"Test"; WNDCLASSEX wcex = { sizeof(WNDCLASSEX) }; wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.lpszClassName = szWindowClass; RegisterClassExW(&wcex); g_hwnd = CreateWindowW(szWindowClass, L"server", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr); if (g_hwnd) { ShowWindow(g_hwnd, SW_SHOW); UpdateWindow(g_hwnd); }}int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow){ UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); InitWindow(hInstance); WSADATA wsaData; WORD wVersionRequested = MAKEWORD(2, 2); WSAStartup(wVersionRequested, &wsaData); do { // (1) socket_srv = ::socket(AF_INET, SOCK_STREAM, 0); if (socket_srv == INVALID_SOCKET) { TraceMsgA("create socket failed, GLE: %d\n", WSAGetLastError()); break; } // (2) struct sockaddr_in addr = { 0 }; addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl(INADDR_ANY); addr.sin_port = htons(kPort); if (bind(socket_srv, reinterpret_cast<const sockaddr*>(&addr), sizeof(addr)) == SOCKET_ERROR) { TraceMsgA("bind failed, GLE: %d\n", WSAGetLastError()); break; } // (3) WSAAsyncSelect(socket_srv, g_hwnd, WUM_SOCKET, FD_ACCEPT | FD_READ | FD_WRITE | FD_CLOSE); // (4) if (listen(socket_srv, 5) == SOCKET_ERROR) { TraceMsgA("listen failed, GLE: %d\n", WSAGetLastError()); break; } TraceMsgA("listen on port: %d\n", kPort); } while (false); MSG msg; while (GetMessage(&msg, nullptr, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } for (std::vector<SOCKET>::iterator it = clients.begin(); it != clients.end(); it++) { closesocket(*it); } // (6) closesocket(socket_srv); WSACleanup(); return 0;}
2.2 客户端
#include <winsock2.h>#include <vector>#include <strsafe.h>#pragma comment(lib, "Ws2_32.lib")const std::string kIP = "127.0.0.1";const u_short kPort = 10001;#define WUM_SOCKET (WM_USER+1)HWND g_hwnd;void TraceMsgA(const char *lpFormat, ...) { if (!lpFormat) return; char *pMsgBuffer = NULL; unsigned int iMsgBufCount = 0; va_list arglist; va_start(arglist, lpFormat); HRESULT hr = STRSAFE_E_INSUFFICIENT_BUFFER; while (hr == STRSAFE_E_INSUFFICIENT_BUFFER) { iMsgBufCount += 1024; if (pMsgBuffer) { free(pMsgBuffer); pMsgBuffer = NULL; } pMsgBuffer = (char*)malloc(iMsgBufCount * sizeof(char)); if (!pMsgBuffer) { break; } hr = StringCchVPrintfA(pMsgBuffer, iMsgBufCount, lpFormat, arglist); } va_end(arglist); if (hr == S_OK) { OutputDebugStringA(pMsgBuffer); } if (pMsgBuffer) { free(pMsgBuffer); pMsgBuffer = NULL; }}LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam){ int err; SOCKET s; char buf[100] = { 0 }; switch (message) { // (4) case WUM_SOCKET: if (WSAGETSELECTERROR(lParam)) { closesocket((SOCKET)wParam); TraceMsgA("connect failed, %d\n", WSAGETSELECTERROR(lParam)); break; } switch (WSAGETSELECTEVENT(lParam)) { case FD_CONNECT: TraceMsgA("connection to server\n"); break; case FD_READ: err = recv((SOCKET)wParam, buf, 100, 0); if (err > 0) { TraceMsgA("recv: %s\n", buf); } else if (err == 0) { closesocket((SOCKET)wParam); TraceMsgA("connection closed\n"); } else { closesocket((SOCKET)wParam); TraceMsgA("recv failed, GLE: %d\n", WSAGetLastError()); } break; case FD_WRITE: break; case FD_CLOSE: closesocket((SOCKET)wParam); TraceMsgA("connection closed\n"); break; } break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0;}void InitWindow(HINSTANCE hInstance) { WCHAR szWindowClass[100] = L"Test"; WNDCLASSEX wcex = { sizeof(WNDCLASSEX) }; wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.lpszClassName = szWindowClass; RegisterClassExW(&wcex); g_hwnd = CreateWindowW(szWindowClass, L"client", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr); if (g_hwnd) { ShowWindow(g_hwnd, SW_SHOW); UpdateWindow(g_hwnd); }}int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow){ UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); InitWindow(hInstance); WSADATA wsaData; WORD wVersionRequested = MAKEWORD(2, 2); WSAStartup(wVersionRequested, &wsaData); SOCKET socket_ = INVALID_SOCKET; do { // (1) socket_ = ::socket(AF_INET, SOCK_STREAM, 0); if (socket_ == INVALID_SOCKET) { TraceMsgA("create socket failed, GLE: %d\n", WSAGetLastError()); break; } // (2) WSAAsyncSelect(socket_, g_hwnd, WUM_SOCKET, FD_CONNECT | FD_READ | FD_WRITE | FD_CLOSE); // (3) struct sockaddr_in addr = { 0 }; addr.sin_family = AF_INET; addr.sin_addr.s_addr = inet_addr(kIP.c_str()); addr.sin_port = htons(kPort); if (connect(socket_, reinterpret_cast<const sockaddr*>(&addr), sizeof(addr)) == SOCKET_ERROR) { int gle_err = WSAGetLastError(); if (gle_err != WSAEWOULDBLOCK) { TraceMsgA("connect failed, GLE: %d\n", WSAGetLastError()); break; } } } while (false); MSG msg; while (GetMessage(&msg, nullptr, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } // (5) closesocket(socket_); WSACleanup(); return 0;}
代码下载地址: http://download.csdn.net/download/china_jeffery/10155511
- Windows套接字I/O模型(3) -- WSAAsyncSelect模型
- 套接字I/O模型之 消息机制-WSAAsyncSelect
- Windows套接字I/O模型(1) -- 阻塞模型
- Windows套接字I/O模型(2) -- Select模型
- Windows套接字I/O模型(4) -- WSAEventSelect模型
- Windows套接字I/O模型
- Windows套接字I/O 模型
- Windows 套接字I/O 模型
- windows套接字I/O模型
- Windows 套接字I/O 模型
- Windows套接字I/O模型
- Windows套接字I/O模型之套接字模式
- WSAAsyncSelect 异步I/O模型
- 套接字i/o模型
- 套接字I/O模型
- 套接字I/O模型
- 套接字I/O模型
- Windows操作系统I/O模型—笔记2(异步选择(WSAAsyncSelect)模型)
- Effective Java之避免创建不必要的对象(五)
- CIFAR-10驱动的KNN分类器
- libux创建逻辑卷及扩展
- Jsoup通过URL获取文档,获取href属性内容
- caffe移植到mxnet
- Windows套接字I/O模型(3) -- WSAAsyncSelect模型
- 利用JS实现复选框一键全选/全不选
- 网络流(Dinic && ISAP)
- 谷歌浏览器js调试初体验
- classpath、path、JAVA_HOME的作用及JAVA环境变量配置
- Spring-boot 集成 kakfa 源码分析
- AI人工智慧成长迅速 恐在20年取代程序设计师?
- ent en et的区别
- Spring MVC +Spring + Mybatis 构建分库分表总结 SSM搭建以及分库分表的实现