异步选择WSAAsyncSelect
2016-04-23 10:28
429 查看
大家用这个模型,可以让应用程序在一个套接字上接收以windows消息为基础的网络事件通知。我们想要用这个操作,具体的做法就是我们建立一个套接字,然后调用WSAAsyncSelect函数, 也就是说,这个模型的核心就是我们的这个函数;
来看一下函数原型:
[cpp] view
plain copy
int WSAAsyncSelect(
_In_ SOCKET s,//我们感兴趣的套接字
_In_ HWND hWnd,//窗口的句柄,对于网络事件繁盛后,想要接收到的通知的那个窗口
_In_ unsigned int wMsg,//指定在发生网络事件时,打算接收的消息。
_In_ long lEvent//指定一个位掩码,对应于一系列网络事件的组合。
);
对于最后一个参数,在这里要说一下,他包括的网络事件模型:FD_READ、FD_WRITE、FD_ACCEPT、FD_CONNECT、FD_CLOSE。
到底使用FD_ACCEPT,还是使用FD_CONNECT类型,要取决于应用程序的身份是客户端,还是服务器。我们可以用或运算来一起使用;
FD_READ 应用程序想要接收有关是否可读的通知,以便读入数据
FD_WRITE 应用程序想要接收有关是否可写的通知,以便写入数据
FD_ACCEPT 应用程序想接收与进入连接有关的通知
FD_CONNECT 应用程序想接收与一次连接完成的通知
FD_CLOSE 应用程序想接收与套接字关闭的通知
实例:
[cpp] view
plain copy
WSAAsyncSeltct(s, hwnd, WM_SOCKET, FD_CONNECT | FD_READ | FD_WRITE | FD_CLOSE);
应用程序可以在套接字s上,接收到有关连接,发送,接收以及套接字关闭的一系列通知了。
要注意的是:通过上面的函数原型我们可以看出来,要想使用我们的这个模型,必须要先调用CreateWindow函数来创建一个窗口,然后再为窗口提供一个窗口例程函数(WinProc);
这里要说的注意是:
当你有多个事件的时候,你一定要在套接字上一次注册,一旦你在这个套接字上允许了事件通知,那么以后除非你明确的调用closesocket命令或者由应用程序针对的那个套接字调用了WSAAsyncSelect,那么你就更改了注册的网络事件的类型了,否则的话事件通知就会永远有效。 上面函数你最后一个参数设置为0,效果相当于你停止在套接字上进行的所有网络事件通知。
还有就是你的应用程序针对一个套接字调用了WSAAsyncSelect,那么套接字会从“锁定”模式变成“非锁定”模式。 这样以后,会导致错误。 为了防止错误的产生,应用程序依赖于由WSAAsyncSelect的第三个参数指定的用户自定义窗口消息,来判断网络事件类型何时在套接字上发生,不能盲目的调用。
当你的程序调用WSAAsyncSelect成功后,会在和hWnd窗口句柄对应的窗口以windows消息的形式接受网络事件的通知。
看窗口消息如何定义:
[cpp] view
plain copy
LRESULT CALLBACK WindowProc(
HWND hwnd,//一个窗口句柄,对窗口的调用就是由这个窗口发出的
UINT uMsg,//指定需要对那些消息进行处理。
WPARAM wParam,//指定在上面发生了一个网络事件的套接字。
LPARAM lParam//低字节制定了已经发生的网络事件,高字节包含了可能出现的错误代码。
);
来看一下步骤:当网络消息抵达窗口后,应用程序就会先检查lParam的高字节位,以判断是否是在网络错误。WSAGETSELECTERROR这个宏可返回高字节位包含的错误信息。
然后如果程序没有发现套接字上没产生错误,接着就要调查到底是那个网络事件类型,具体做法是读取lParam的低字节位。 WSAGETSELECTEVENT这个宏返回lParam的低字节部分。
看一下代码:
[cpp] view
plain copy
#include<Windows.h>
#include<tchar.h>
#define MSGSIZE 1024
#define WM_SOCKET WM_USER+100
#pragma comment(lib, "ws2_32.lib")
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain( __in HINSTANCE hInstance, __in_opt HINSTANCE hPrevInstance, __in LPSTR lpCmdLine, __in int nShowCmd )
{
TCHAR classname[] = "AsyncSelect";
WNDCLASS wndclass; //窗口类
wndclass.style = CS_HREDRAW | CS_VREDRAW;//窗口类型
wndclass.lpfnWndProc = WindowProc;//窗口处理函数
wndclass.cbClsExtra = 0;//窗口类无扩展
wndclass.cbWndExtra = 0;//窗口实例无扩展
wndclass.hInstance = hInstance;//当前实例句柄
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);//窗口最小化图标为缺省图标
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);//窗口用箭头光标
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);//窗口背景用白色
wndclass.lpszMenuName = NULL;//窗口没有菜单
wndclass.lpszClassName = classname;//窗口类名为“窗口”
//注册窗口类
if (!RegisterClass(&wndclass))
{
MessageBox(NULL, "register class error", classname, MB_ICONERROR);
return 0;
}
//创建窗口(窗口类名,窗口标题,窗口风格,坐标缺省, 有没有父窗口,有没有子窗口, 创建这个窗口的应用程序当前句柄,不适用)
HWND hwnd = CreateWindow(classname, "AsyncSelect Model", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL);
//显示和更新
ShowWindow(hwnd, nShowCmd);
UpdateWindow(hwnd);
MSG msg;
//消息循环
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
WSADATA wsd;
static SOCKET slisten;
SOCKET sClient;
SOCKADDR_IN local, client;
int ret, iAddrsize = sizeof(client);
char szMessage[MSGSIZE] = {0};
switch (uMsg)
{
case WM_DESTROY:
{
//退出清理
closesocket(slisten);
WSACleanup();
PostQuitMessage(0);
return 0;
}
case WM_CREATE:
{
//初始化
WORD sockVersion = MAKEWORD(2, 0);
WSAStartup(sockVersion, &wsd);
//创建socket
slisten = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
local.sin_family = AF_INET;
local.sin_port = htons(8888);
local.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
//绑定socket
if(bind(slisten, (sockaddr*)&local, sizeof(local)) == SOCKET_ERROR)
{
WSACleanup();
return 0;
}
//监听socket
if(listen(slisten, 5) == SOCKET_ERROR)
{
WSACleanup();
return 0;
}
//注册监听socket FD_ACCEPT事件,当socket有连接时,会发送WM_SOCKET消息给窗口
WSAAsyncSelect(slisten, hwnd, WM_SOCKET, FD_ACCEPT);
return 0;
}
case WM_SOCKET:
{
if (WSAGETSELECTERROR(lParam))
{
closesocket(wParam);
break;
}
switch(WSAGETSELECTEVENT(lParam))
{
case FD_ACCEPT:
{
//接受
sClient = accept(wParam, (sockaddr*)&client, &iAddrsize);
//注册客户socket FD_READ和FD_CLOSE事件
WSAAsyncSelect(sClient, hwnd, WM_SOCKET, FD_READ|FD_CLOSE);
break;
}
case FD_READ:
{
//接受
ret = recv(wParam, szMessage, MSGSIZE, 0);
if (ret == 0 || ret == SOCKET_ERROR && WSAGetLastError() == WSAECONNRESET)
{
closesocket(wParam);
}
else
{
//发送
szMessage[ret] = '\0';
send(wParam, szMessage, strlen(szMessage), 0);
}
break;
}
case FD_CLOSE:
{
closesocket(wParam);
break;
}
default:
{
closesocket(wParam);
break;
}
}
}
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
2012/9/2
jofranks于南昌
来看一下函数原型:
[cpp] view
plain copy
int WSAAsyncSelect(
_In_ SOCKET s,//我们感兴趣的套接字
_In_ HWND hWnd,//窗口的句柄,对于网络事件繁盛后,想要接收到的通知的那个窗口
_In_ unsigned int wMsg,//指定在发生网络事件时,打算接收的消息。
_In_ long lEvent//指定一个位掩码,对应于一系列网络事件的组合。
);
对于最后一个参数,在这里要说一下,他包括的网络事件模型:FD_READ、FD_WRITE、FD_ACCEPT、FD_CONNECT、FD_CLOSE。
到底使用FD_ACCEPT,还是使用FD_CONNECT类型,要取决于应用程序的身份是客户端,还是服务器。我们可以用或运算来一起使用;
FD_READ 应用程序想要接收有关是否可读的通知,以便读入数据
FD_WRITE 应用程序想要接收有关是否可写的通知,以便写入数据
FD_ACCEPT 应用程序想接收与进入连接有关的通知
FD_CONNECT 应用程序想接收与一次连接完成的通知
FD_CLOSE 应用程序想接收与套接字关闭的通知
实例:
[cpp] view
plain copy
WSAAsyncSeltct(s, hwnd, WM_SOCKET, FD_CONNECT | FD_READ | FD_WRITE | FD_CLOSE);
应用程序可以在套接字s上,接收到有关连接,发送,接收以及套接字关闭的一系列通知了。
要注意的是:通过上面的函数原型我们可以看出来,要想使用我们的这个模型,必须要先调用CreateWindow函数来创建一个窗口,然后再为窗口提供一个窗口例程函数(WinProc);
这里要说的注意是:
当你有多个事件的时候,你一定要在套接字上一次注册,一旦你在这个套接字上允许了事件通知,那么以后除非你明确的调用closesocket命令或者由应用程序针对的那个套接字调用了WSAAsyncSelect,那么你就更改了注册的网络事件的类型了,否则的话事件通知就会永远有效。 上面函数你最后一个参数设置为0,效果相当于你停止在套接字上进行的所有网络事件通知。
还有就是你的应用程序针对一个套接字调用了WSAAsyncSelect,那么套接字会从“锁定”模式变成“非锁定”模式。 这样以后,会导致错误。 为了防止错误的产生,应用程序依赖于由WSAAsyncSelect的第三个参数指定的用户自定义窗口消息,来判断网络事件类型何时在套接字上发生,不能盲目的调用。
当你的程序调用WSAAsyncSelect成功后,会在和hWnd窗口句柄对应的窗口以windows消息的形式接受网络事件的通知。
看窗口消息如何定义:
[cpp] view
plain copy
LRESULT CALLBACK WindowProc(
HWND hwnd,//一个窗口句柄,对窗口的调用就是由这个窗口发出的
UINT uMsg,//指定需要对那些消息进行处理。
WPARAM wParam,//指定在上面发生了一个网络事件的套接字。
LPARAM lParam//低字节制定了已经发生的网络事件,高字节包含了可能出现的错误代码。
);
来看一下步骤:当网络消息抵达窗口后,应用程序就会先检查lParam的高字节位,以判断是否是在网络错误。WSAGETSELECTERROR这个宏可返回高字节位包含的错误信息。
然后如果程序没有发现套接字上没产生错误,接着就要调查到底是那个网络事件类型,具体做法是读取lParam的低字节位。 WSAGETSELECTEVENT这个宏返回lParam的低字节部分。
看一下代码:
[cpp] view
plain copy
#include<Windows.h>
#include<tchar.h>
#define MSGSIZE 1024
#define WM_SOCKET WM_USER+100
#pragma comment(lib, "ws2_32.lib")
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain( __in HINSTANCE hInstance, __in_opt HINSTANCE hPrevInstance, __in LPSTR lpCmdLine, __in int nShowCmd )
{
TCHAR classname[] = "AsyncSelect";
WNDCLASS wndclass; //窗口类
wndclass.style = CS_HREDRAW | CS_VREDRAW;//窗口类型
wndclass.lpfnWndProc = WindowProc;//窗口处理函数
wndclass.cbClsExtra = 0;//窗口类无扩展
wndclass.cbWndExtra = 0;//窗口实例无扩展
wndclass.hInstance = hInstance;//当前实例句柄
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);//窗口最小化图标为缺省图标
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);//窗口用箭头光标
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);//窗口背景用白色
wndclass.lpszMenuName = NULL;//窗口没有菜单
wndclass.lpszClassName = classname;//窗口类名为“窗口”
//注册窗口类
if (!RegisterClass(&wndclass))
{
MessageBox(NULL, "register class error", classname, MB_ICONERROR);
return 0;
}
//创建窗口(窗口类名,窗口标题,窗口风格,坐标缺省, 有没有父窗口,有没有子窗口, 创建这个窗口的应用程序当前句柄,不适用)
HWND hwnd = CreateWindow(classname, "AsyncSelect Model", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL);
//显示和更新
ShowWindow(hwnd, nShowCmd);
UpdateWindow(hwnd);
MSG msg;
//消息循环
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
WSADATA wsd;
static SOCKET slisten;
SOCKET sClient;
SOCKADDR_IN local, client;
int ret, iAddrsize = sizeof(client);
char szMessage[MSGSIZE] = {0};
switch (uMsg)
{
case WM_DESTROY:
{
//退出清理
closesocket(slisten);
WSACleanup();
PostQuitMessage(0);
return 0;
}
case WM_CREATE:
{
//初始化
WORD sockVersion = MAKEWORD(2, 0);
WSAStartup(sockVersion, &wsd);
//创建socket
slisten = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
local.sin_family = AF_INET;
local.sin_port = htons(8888);
local.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
//绑定socket
if(bind(slisten, (sockaddr*)&local, sizeof(local)) == SOCKET_ERROR)
{
WSACleanup();
return 0;
}
//监听socket
if(listen(slisten, 5) == SOCKET_ERROR)
{
WSACleanup();
return 0;
}
//注册监听socket FD_ACCEPT事件,当socket有连接时,会发送WM_SOCKET消息给窗口
WSAAsyncSelect(slisten, hwnd, WM_SOCKET, FD_ACCEPT);
return 0;
}
case WM_SOCKET:
{
if (WSAGETSELECTERROR(lParam))
{
closesocket(wParam);
break;
}
switch(WSAGETSELECTEVENT(lParam))
{
case FD_ACCEPT:
{
//接受
sClient = accept(wParam, (sockaddr*)&client, &iAddrsize);
//注册客户socket FD_READ和FD_CLOSE事件
WSAAsyncSelect(sClient, hwnd, WM_SOCKET, FD_READ|FD_CLOSE);
break;
}
case FD_READ:
{
//接受
ret = recv(wParam, szMessage, MSGSIZE, 0);
if (ret == 0 || ret == SOCKET_ERROR && WSAGetLastError() == WSAECONNRESET)
{
closesocket(wParam);
}
else
{
//发送
szMessage[ret] = '\0';
send(wParam, szMessage, strlen(szMessage), 0);
}
break;
}
case FD_CLOSE:
{
closesocket(wParam);
break;
}
default:
{
closesocket(wParam);
break;
}
}
}
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
2012/9/2
jofranks于南昌
相关文章推荐
- DAY8
- win10 + VS2010 + OpenCV2.4.10重编译OpenCV开发环境搭建
- Android中使用socket通信实现消息推送的方法详解
- ubuntu chrome
- 华为老总推荐的文章
- HDU 4358-I - Boring counting-dfs序+离线+树状数组/线段树
- poj-3050-Hopscotch
- Process, program, thread 的区别
- 338. Counting Bits
- Basemap可视化地图信息
- oj数据结构题谜之输出格式
- 图片的压缩优化
- BestCoder Round #81 (div.1) C Robot
- 一款实用的火车票订票APP--高铁管家
- JAVA基础知识IO流(文本文件读取方式一)
- 插入备份数据 报 IDENTITY_INSERT 为 ON 时解决方法
- [bzoj4066]简单题
- pt-query-digest+Anemometer实现MySQL慢查询可视化
- C语言中不同类型的指针互相转换的注意点
- c++实现二叉树(递归)