C++Socket编程—socket网络模型之异步选择模型
一、什么是异步选择模型
异步选择(WSAAsyncSelect)模型是一个异步 I/O 模型。利用这个模型,应用程序可在一个套接字上,接收以 Windows 消息机制为基础的网络事件通知,开发者将socket注册到消息机制,当socket有事件(新的连接,新的数据,连接断开,可以写入)来时候。具体的做法是在建好一个套接字后,调用WSAAsyncSelect函数。
该模型的核心即是WSAAsyncSelect函数,该函数是非阻塞的。
二、与select模型比较
相同点:
他们都可以对Windows套接字应用程序所使用的多个套接字进行有效的管理。
不同点:
1.WSAAsyncSelect模型是异步的。在应用程序中调用WSAAsyncSelect()函数,通知系统感兴趣的网络事件,该函数立即返回,应用程序继续执行;
2.发生网络事件时,应用程序得到的通知方式不同。Select()函数返回时,说明某个或者某些套接字满足可读可写的条件,应用程序需要使用FD_ISSET宏,判断套接字是否存在可读可写集合中。而对于WSAAsyncSelect模型来说,当网络事件发生时,系统向应用程序发送消息。
3.WSAAsyncSelect模型应用在基于消息的Windos环境下,使用该模型时必须创建窗口。而Select模型广泛应用在Unix系统和Windows系统,使用该模型不需要创建窗口。
4.应用程序调用WSAAsyncSelect()函数后,自动将套接字设置为非阻塞模式。而应用程序中调用select()函数后,并不能改变套接字的工作方式
三、异步选择模型API函数
WSAAsyncSelect函数定义如下:
int WSAAsyncSelect( __in SOCKET s, //指定的是我们感兴趣的那个套接字。 __in HWND hWnd, //指定一个窗口句柄,它对应于网络事件发生之后,想要收到通知消息的那个窗口。 __in unsigned int wMsg, //指定在发生网络事件时,打算接收的消息。该消息会投递到由hWnd窗口句柄指定的那个窗口。 __in long lEvent //指定一个位掩码,对应于一系列网络事件的组合 );
WSAAsyncSelect模型是Select模型的异步版本,在调用select()函数时,会发生阻塞现象。可以通过select()函数timeout参数,设置函数调用的阻塞时间。在设定的时间内,线程保持等待,直到其中一个或多个套接字满足可读可写的条件时,该函数返回。
四、异步选择模型缺陷
不适合高并发网络结构,因为是基于窗口消息机制,消息太多就处理速度较慢。
五、代码示例
创建windwos窗口项目:
//1.添加头文件 #include<WinSock2.h> #pragma comment(lib,"Ws2_32.lib") #define WM_MYSOCKMSG WM_USER+1 bool HandleData(SOCKET sockClient);//处理收发数据 //2.初始化WSAstartup WORD wVersionRequested; WSADATA wsaData; int err; wVersionRequested = MAKEWORD( 1, 1 ); err = WSAStartup( wVersionRequested, &wsaData ); if ( err != 0 ) { /* Tell the user that we could not find a usable */ /* WinSock DLL. */ return 0; } /* Confirm that the WinSock DLL supports 1.1.*/ /* Note that if the DLL supports versions greater */ /* than 1.1 in addition to 1.1, it will still return */ /* 1.1 in wVersion since that is the version we */ /* requested. */ if ( LOBYTE( wsaData.wVersion ) != 1 || HIBYTE( wsaData.wVersion ) != 1 ) { /* Tell the user that we could not find a usable */ /* WinSock DLL. */ WSACleanup( ); return 0; } //3.再处理消息处添加代码 LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_CREATE: { //1) 创建socket SOCKET sockServer = socket( AF_INET, SOCK_STREAM, //流式 IPPROTO_TCP);//tcp协议 // 2) 绑定端口 sockaddr_in siServer; siServer.sin_family = AF_INET; siServer.sin_port = htons(9527); siServer.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); int nRet = bind(sockServer, (sockaddr*)&siServer, sizeof(siServer)); if (nRet == SOCKET_ERROR) { printf("绑定失败 \r\n"); return 0; } // 3) 监听 nRet = listen(sockServer, SOMAXCONN); if (nRet == SOCKET_ERROR) { printf("监听失败 \r\n"); return 0; } //4)接受连接 //这个socket只关系新连接和关闭时间 WSAAsyncSelect(sockServer, hWnd, WM_MYSOCKMSG, FD_ACCEPT | FD_CLOSE); break; } case WM_MYSOCKMSG: { SOCKET sock = (SOCKET)wParam; WORD wErrCode = WSAGETSELECTERROR(lParam); WORD wSelectEvent = WSAGETSELECTEVENT(lParam); switch (wSelectEvent) { case FD_ACCEPT: { sockaddr_in siClient; int nSize = sizeof(siClient); SOCKET sockClient = accept(sock, (sockaddr*)&siClient, &nSize); //为新的连接注册对应的网络事件 WSAAsyncSelect(sockClient, hWnd, WM_MYSOCKMSG, FD_READ | FD_CLOSE); break; } case FD_READ://数据来了 { HandleData(sock); //处理数据 break; } case FD_CLOSE://连接断开 { //将sock从网络事件中移除 closesocket(sock); break; } } break; } case WM_COMMAND: { int wmId = LOWORD(wParam); // 分析菜单选择: switch (wmId) { case IDM_ABOUT: DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About); break; case IDM_EXIT: DestroyWindow(hWnd); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } } break; case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd, &ps); // TODO: 在此处添加使用 hdc 的任何绘图代码... EndPaint(hWnd, &ps); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; } bool HandleData(SOCKET sockClient) { // 5) 收发数据 char aryBuff[MAXWORD] = { 0 }; int nRet = recv(sockClient, aryBuff, sizeof(aryBuff), 0); if (nRet == 0 || nRet == SOCKET_ERROR) { printf("接受数据失败 \r\n"); return false; } printf("收到数据: %s \r\n", aryBuff); char szBuff[] = { "recv OK " }; nRet = send(sockClient, szBuff, sizeof(szBuff), 0); if (nRet == SOCKET_ERROR) { printf("数据发送失败 \r\n"); return false; } return true; }
原文地址:https://www.cnblogs.com/zhaoyixiang/p/12964762.html
- 改变开发者编码思维的六种编程范式
- PostgreSQL并行查询是个什么“鬼"?
- 想弄一台简单的区块链服务器?来这里看看!!
- NNabla:索尼开源的一款神经网络框架
- 黑客与C语言
- 浅谈分布式事务
- 漫谈千亿级数据优化实践:一次数据优化实录
- Facebook开源游戏平台ELF:一个用于实时战略游戏研究的轻量级平台
- 用不到50行的Python代码构建最小的区块链
- 学习笔记CB002:词干提取、词性标注、中文切词、文档分类
- 深入浅出 Retrofit,这么牛逼的框架你们还不来看看?
- 用Python从零开始构建反向传播算法
- 备战CDA数据分析竞赛!Kaggle赛题大揭秘
- 如何用Python将时间序列转换为监督学习问题
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Flutter开发之路由与导航的实现
- Android BSearchEdit 搜索结果选择框的实例代码
- 使用AccessibilityService实现微信自动切换账号功能
- Android评分RationBar控件使用详解
- Flutter里面错误捕获的正确方法
- django3 websockets
- 使用AccessibilityService实现自动遍历点赞功能
- Android自定义字母导航栏
- [-Flutter 自组篇-] 圆形进度条
- Flutter 滚动监听及实战appBar滚动渐变的实现
- Android 开机充电图标和充电动画效果
- 使用tea算法对数据进行加密
- Android实现页面翻转和自动翻转功能
- Android实现自定义手势和识别手势的功能
- android 使用okhttp可能引发OOM的一个点