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

原创粉丝点击