《网络编程技术(西电课件)_第8章-1.ppt》由会员分享,可在线阅读,更多相关《网络编程技术(西电课件)_第8章-1.ppt(63页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、网络编程技术网络编程技术第第8章章 WinSock的的I/O模型模型(1)本次课内容:本次课内容:u 回顾阻塞和非阻塞套接字回顾阻塞和非阻塞套接字u WinSock 的的I/O模型介绍模型介绍u select 模型模型u WSAAsyncSelect 模型模型8.1 8.1 套接字的阻塞和非阻塞操作套接字的阻塞和非阻塞操作阻塞阻塞阻塞的阻塞的accept()accept()操作操作 非阻塞的非阻塞的recv()recv()操作操作套接字套接字I/OI/O模型模型问题:套接字何时可以进行读写等操作?问题:套接字何时可以进行读写等操作?Winsock2提供提供5种种I/O模型模型u选择模型(选择模
2、型(select Model)u异步选择模型(异步选择模型(WSAAsyncSelect Model)u事件选择模型(事件选择模型(WSAEventSelect Model)u重叠模型(重叠模型(Overlapped Model)u完成端口模型(完成端口模型(Completion Port Model)非阻塞模式非阻塞模式uWinSock给异步模式提供了五种给异步模式提供了五种I/O操作模型,操作模型,可以灵活的用于各类应用需求可以灵活的用于各类应用需求u非阻塞模式套接字除具备阻塞套接字已有的各非阻塞模式套接字除具备阻塞套接字已有的各项优点之外,还进行了少许扩充,功能更强。项优点之外,还进行了
3、少许扩充,功能更强。决定了,就决定了,就用非阻塞的!用非阻塞的!How?ioctlsocket()ioctlsocket()函数设置阻塞函数设置阻塞/非阻塞非阻塞创建套接字后,要将套接字设置为非阻塞,函数:创建套接字后,要将套接字设置为非阻塞,函数:int ioctlsocket(SOCKETint ioctlsocket(SOCKET s s,long long cmd cmd,u_long FAR u_long FAR*argp*argp););用于设置和获取与套接字相关的操作参数。用于设置和获取与套接字相关的操作参数。参数参数us:套接字描述符。套接字描述符。ucmd:对套接字对套接字s
4、的操作命令。的操作命令。uargp:指向:指向cmd命令所带参数的指针。命令所带参数的指针。成功返回成功返回0,否则否则,返回返回SOCKET_ERROR。cmdcmd FIONBIOFIONBIO:允许或禁止套接字:允许或禁止套接字s s的非阻塞模式。的非阻塞模式。uargp is zero,argp is zero,禁止非阻塞模式;禁止非阻塞模式;uargp is nonzero,argp is nonzero,允许非阻塞模式;允许非阻塞模式;u当一个套接字建立时,默认为阻塞模式;当一个套接字建立时,默认为阻塞模式;FIONREADFIONREAD:确定套接字:确定套接字s s可读入的数据
5、量。可读入的数据量。argp argp 指向一个无符号长整型。指向一个无符号长整型。u如果如果s s是是SOCKET_STREAMSOCKET_STREAM类型,则类型,则FIONREADFIONREAD返回在返回在一次一次recv()recv()中所接收的所有数据量。中所接收的所有数据量。u如果如果s s是是SOCK_DGRAM SOCK_DGRAM 型,则型,则FIONREADFIONREAD返回套接字返回套接字上排队的第一个数据报大小。上排队的第一个数据报大小。ioctlsocketioctlsocket使用举例使用举例unsigned long mode=0;/禁止非阻塞禁止非阻塞io
6、ctlsocket(sockfd,FIONBIO,&mode);unsigned long mode=1;/允许非阻塞允许非阻塞ioctlsocket(sockfd,FIONBIO,&mode);unsigned long arg;/取得数据量取得数据量ioctlsocket(sockfd,FIONREAD,&arg);阻塞与非阻塞通信小结阻塞与非阻塞通信小结u通信包括阻塞和非阻塞两种模式。在网络编程时,通信包括阻塞和非阻塞两种模式。在网络编程时,选择通信模式是一件很重要的事情。对于不同的协选择通信模式是一件很重要的事情。对于不同的协议,阻塞通信和非阻塞通信有不同的表现。议,阻塞通信和非阻塞通
7、信有不同的表现。u对于对于UDP协议而言,由于协议而言,由于UDP没有发送缓存,因没有发送缓存,因此所有此所有UDP协议即使在阻塞模式下也不会发生阻塞。协议即使在阻塞模式下也不会发生阻塞。u对于面向连接的协议,在连接建立阶段,阻塞与对于面向连接的协议,在连接建立阶段,阻塞与非阻塞也表现不一。在阻塞模式下,如果没有连接非阻塞也表现不一。在阻塞模式下,如果没有连接请求到达,则等待连接调用将阻塞直到有连接请求请求到达,则等待连接调用将阻塞直到有连接请求到达;但在非阻塞模式下,如果没有连接请求到达,到达;但在非阻塞模式下,如果没有连接请求到达,等待连接调用将直接返回。等待连接调用将直接返回。u在连接建
8、立阶段,不管是阻塞模式还是非阻塞模在连接建立阶段,不管是阻塞模式还是非阻塞模式,发起连接请求的一方总是会使调用它的进程式,发起连接请求的一方总是会使调用它的进程阻塞,阻塞间隔最少等于到达服务器的一次往返阻塞,阻塞间隔最少等于到达服务器的一次往返时间。时间。u通信模式对应用程序的设计方法也有直接的影响。通信模式对应用程序的设计方法也有直接的影响。在非阻塞模式下,应用程序必须不断地轮询是否在非阻塞模式下,应用程序必须不断地轮询是否有数据到达或有连接请求到达。有数据到达或有连接请求到达。u这种轮询的方式耗费的这种轮询的方式耗费的CPU资源较大,要尽可资源较大,要尽可能避免使用,而在阻塞模式下则不存在
9、这一问题,能避免使用,而在阻塞模式下则不存在这一问题,但其缺点是进程或线程在执行但其缺点是进程或线程在执行I/O操作时将被阻操作时将被阻塞而不能执行其他的工作,因此在单进程或单线塞而不能执行其他的工作,因此在单进程或单线程应用中不能使用这种模式。程应用中不能使用这种模式。u在多线程应用中比较适合采用阻塞模式,一个线在多线程应用中比较适合采用阻塞模式,一个线程被阻塞不影响其他线程的工作。程被阻塞不影响其他线程的工作。8.2 8.2 套接字的套接字的I/OI/O模型模型套接字套接字I/OI/O模型模型套接字处于非阻塞模式时,何时可以读套接字处于非阻塞模式时,何时可以读/写套接字?写套接字?使用套接
10、字使用套接字I/O模型来确定模型来确定Winsock2提供提供5种种I/O模型模型u选择模型(选择模型(select Model)u异步选择模型(异步选择模型(WSAAsyncSelect Model)u事件选择模型(事件选择模型(WSAEventSelect Model)u重叠模型(重叠模型(Overlapped Model)u完成端口模型(完成端口模型(Completion Port Model)五种五种I/OI/O模型的概述模型的概述l选择模型:选择模型:程序自发程序自发select查询多个套接字状态查询多个套接字状态l异步选择模型:异步选择模型:套接字有事件,通知指定窗口程序套接字有事
11、件,通知指定窗口程序l事件选择模型:事件选择模型:套接字有事件,通知指定事件对象套接字有事件,通知指定事件对象l重叠模型:重叠模型:提交多个提交多个I/O请求,完成后通知事件或执行请求,完成后通知事件或执行指定程序指定程序l完成端口模型:完成端口模型:创建指定数目的线程,组成线程池,创建指定数目的线程,组成线程池,池内的空闲线程对提交的池内的空闲线程对提交的I/O请求逐个处理。请求逐个处理。操作系统对套接字I/O模型的支持情况所有Windows平台都支持套接字以阻塞或非阻塞方式工作。然而,并非每种平台都支持每一种I/O模型。在当前版本的Windows CE 中,仅提供了一个I/O模型。套接字的
12、套接字的selectselect模型模型selectselect模型模型u select模型是模型是WinSock中最常见的中最常见的I/O模型。模型。u程序调用程序调用select函数自发查询一个或多个套接字的函数自发查询一个或多个套接字的状态,判断套接字上是否存在数据,或者能否向状态,判断套接字上是否存在数据,或者能否向一个套接字写入数据。一个套接字写入数据。u既能防止应用程序在套接字处于阻塞模式时,在既能防止应用程序在套接字处于阻塞模式时,在一次一次I/O操作后被阻塞,同时也能防止在套接字处操作后被阻塞,同时也能防止在套接字处于非阻塞模式时,产生于非阻塞模式时,产生WSAEWOULDBL
13、OCK错错误。误。select函数如下:函数如下:int select(int nfds,/描述符值域,描述符值域,Windows下可忽略下可忽略 fd_set *readfds,/检查检查可读性集合可读性集合 fd_set *writefds,/检查检查可写性集合可写性集合 fd_set *exceptfds,/检查检查错误集合错误集合 const struct timeval *timeout/等待时间等待时间);select函数如下:函数如下:int select(int nfds,/描述符值域,描述符值域,Windows下可忽略下可忽略 fd_set *readfds,/检查检查可读性
14、集合可读性集合 fd_set *writefds,/检查检查可写性集合可写性集合 fd_set *exceptfds,/检查检查错误集合错误集合 const struct timeval *timeout/等待时间等待时间);fd_set 数据类型代表着一系列特定套接字的集合。数据类型代表着一系列特定套接字的集合。typedef struct fd_set u_int fd_count;/反映反映fd_array的的实际实际元素数量元素数量 SOCKET fd_arrayFD_SETSIZE;/套接字句柄数套接字句柄数组组 fd_set;u此函数用于检测一个或多个套接字的状态。套接字此函数用于
15、检测一个或多个套接字的状态。套接字的状态包括可读、可写、出错。的状态包括可读、可写、出错。u需要检测状态的套接字集合由一个需要检测状态的套接字集合由一个fd_setfd_set结构指示,结构指示,分别为分别为readfds,writefds,exceptfds(不能同时为(不能同时为NULL)。u参数参数timeout指向指向timeval结构,用于指定结构,用于指定select等等待待I/O操作完成的时间。如操作完成的时间。如timeout是一个空指针,是一个空指针,select调用会无限期地,直到至少有一个套接字满调用会无限期地,直到至少有一个套接字满足条件。足条件。timeval结构的格
16、式为:结构的格式为:struct timevallong tv_sec;/秒秒(s)long tv_usec;/毫秒毫秒(ms);selectselect函数调用失败,返回函数调用失败,返回SOCKET_EEROR;SOCKET_EEROR;超时返回超时返回0 0;成功;成功返回返回所有集合中所有集合中满足条件的套接字数量满足条件的套接字数量。同。同时时传入的套接字集合被更新传入的套接字集合被更新,集合中,集合中保留满足条件保留满足条件的套接字的套接字readfds集合集合包括等待可读性检查的套接口:包括等待可读性检查的套接口:u有数据可以读入。有数据可以读入。u连接已经关闭、重设或中止。连接
17、已经关闭、重设或中止。u侦听套接字调用了侦听套接字调用了listen(),若有连接请求到达,那么,若有连接请求到达,那么accept函数调用会成功。函数调用会成功。writefds集合集合包括等待可写性检查的套接口:包括等待可写性检查的套接口:u未处于未处于connect()调用中,意味着调用中,意味着send()和和sendto()调用调用可以无阻塞的发出数据。可以无阻塞的发出数据。u如果正在如果正在connect()连接(非阻塞),可写性意味着连接连接(非阻塞),可写性意味着连接顺利建立。顺利建立。exceptfds集合集合包括满足下述任何一个条件套接字:包括满足下述任何一个条件套接字:u
18、假如套接口正在进行假如套接口正在进行connect()(非阻塞),则连接试图(非阻塞),则连接试图的失败将会表现在的失败将会表现在exceptfds参数中。参数中。u有带外有带外(Out-of-band,OOB)数据可供读取。数据可供读取。readfdswritefdsexceptfds套接字集合套接字集合有事情发生有事情发生有未决的连接请求有未决的连接请求数据可读数据可读连接关闭连接关闭/重启重启/中断中断连接成功连接成功(connect)可写数据可写数据连接失败连接失败(connect)带外数据可读带外数据可读选择选择(select)(select)模型处理方式模型处理方式select()
19、l假定想测试一个套接字是否假定想测试一个套接字是否“可读可读”,必须将该,必须将该套接字增添到套接字增添到readfds集合,再等待集合,再等待select函数函数完成。完成。lselect完成之后,必须判断该套接字是否仍在完成之后,必须判断该套接字是否仍在readfds集合中,若在,便表明该套接字集合中,若在,便表明该套接字“可读可读”,可从它上面读取数据。,可从它上面读取数据。Winsock提供了下列宏操作,对提供了下列宏操作,对fd_set套接字集合套接字集合进行处理与检查:进行处理与检查:uFD_CLR(s,*set):从:从set中删除套接字中删除套接字s。uFD_ISSET(s,*
20、set):检查:检查s是否在是否在set集合中,集合中,若是返回若是返回TRUE,否则返回,否则返回FALSE。uFD_SET(s,*set):将套接字:将套接字s加入集合加入集合set。uFD_ ZERO(*set):将:将set初始化成空集合初始化成空集合使用使用select模型的编程步骤:模型的编程步骤:1.建立建立fd_set集合集合s,用来存放待使用的套接字。,用来存放待使用的套接字。2.将套接字添加到集合将套接字添加到集合s中。中。3.确定要检查的套接字集合确定要检查的套接字集合Xi(1=i=3)。)。4.使用使用FD_ZERO宏宏,初始化初始化Xi。5.使用使用FD_SET宏宏,
21、根据需要将套接字句柄添加到根据需要将套接字句柄添加到Xi中中6.调用调用select函数函数7.根据根据select函数的返回值进行处理,当成功返回函数的返回值进行处理,当成功返回时,判断时,判断s中套接字是否在中套接字是否在Xi中,并进行相应处中,并进行相应处理理8.回到回到4selectselect模型实例分析模型实例分析selectselect模式实例模式实例u实现一个阻塞的实现一个阻塞的TCPTCP套接字服务器,端口套接字服务器,端口5150;5150;u主线程不断的接受客户端的连接主线程不断的接受客户端的连接;u工作线程利用工作线程利用 select()函数不断检查是否函数不断检查是
22、否有数据可以读取;有则读取,原样发送给有数据可以读取;有则读取,原样发送给客户端。客户端。selectselect模式实例模式实例/头文件头文件#include stdafx.h#include winsock2.h#include stdio.h#define PORT 5150#define MSGSIZE 1024#pragma comment(lib,ws2_32.lib)int g_iTotalConn=0;/全局计数器全局计数器SOCKET g_CliSocketArrFD_SETSIZE;/全局通信套接字全局通信套接字DWORD WINAPI WorkerThread(LPVOI
23、D lpParam);/声明处理数据接收的线程函数声明处理数据接收的线程函数int main(int argc,char*argv)WSADATA wsaData;SOCKET sListen,sClient;SOCKADDR_IN local,client;int iAddrSize=sizeof(SOCKADDR_IN);DWORD dwThreadId;/初始化初始化Winsock动态链接库动态链接库 WSAStartup(0 x0202,&wsaData);/创建套接字创建套接字 sListen=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);/绑定到绑
24、定到IP和端口和端口 local.sin_family=AF_INET;local.sin_addr.S_un.S_addr=htonl(INADDR_ANY);local.sin_port=htons(PORT);bind(sListen,(sockaddr*)&local,sizeof(SOCKADDR_IN);/侦听,队列为侦听,队列为3 listen(sListen,3);/创建新线程,运行创建新线程,运行WorkerThread函数来处理函数来处理socket的数据接收的数据接收 CreateThread(NULL,0,WorkerThread,NULL,0,&dwThreadId)
25、;while(TRUE)/接受客户端连接接受客户端连接 sClient=accept(sListen,(sockaddr*)&client,&iAddrSize);printf(Accepted client:%s:%d/n,inet_ntoa(client.sin_addr),ntohs(client.sin_port);/将生成的通信套接字,保存到套接字数组将生成的通信套接字,保存到套接字数组 g_CliSocketArr g_iTotalConn+=sClient;return 0;DWORD WINAPI WorkerThread(LPVOID lpParam)int i;fd_set
26、 fdread;/定义一个定义一个FD集合,用于测试集合,用于测试read int ret;struct timeval tv=1,0;/1秒的间隔秒的间隔 char szMessageMSGSIZE;/接收缓冲区接收缓冲区 while(TRUE)FD_ZERO(&fdread);/将集合清将集合清0 for(i=0;i g_iTotalConn;i+)/将所有通信套接字放入将所有通信套接字放入FD集合集合 FD_SET(g_CliSocketArri,&fdread);/我们只关心这些套接字里面有没有数据可以我们只关心这些套接字里面有没有数据可以read ret=select(0,&fdre
27、ad,NULL,NULL,&tv);if(ret=0)/定时时间到定时时间到 continue;for(i=0;i g_iTotalConn;i+)/逐个检查通信套接字是否还在逐个检查通信套接字是否还在FD集合中集合中 if(FD_ISSET(g_CliSocketArri,&fdread)/在,就意味着有数据可读在,就意味着有数据可读 ret=recv(g_CliSocketArri,szMessage,MSGSIZE,0);if(ret=0|(ret=SOCKET_ERROR&WSAGetLastError()=WSAECONNRESET)/如果收到数据为如果收到数据为0,或者套接字出错,
28、或者套接字出错 printf(Client socket%d closed./n,g_CliSocketArri);closesocket(g_CliSocketArri);/关闭相应的套接字关闭相应的套接字 if(i 0,没出错,收到,没出错,收到ret个字节个字节 szMessageret=/0;send(g_CliSocketArri,szMessage,strlen(szMessage),0);select模型的模型的优点优点:可以在单线程内管理多个套接字,:可以在单线程内管理多个套接字,最大套接字数量取决于最大套接字数量取决于FD_SETSIZE的大小,的大小,Winsock2.h中
29、定义为中定义为64,用户也可自行定义,但不,用户也可自行定义,但不能超过能超过1024。缺点缺点:调用:调用select前后对所有套接字都要进行遍历操作,前后对所有套接字都要进行遍历操作,以便设置和检查。当以便设置和检查。当FD_SETSIZE太大时,服务器太大时,服务器性能明显下降。性能明显下降。最通俗浅显的最通俗浅显的“IOIO模式模式”解析解析老陈有几个在外地工作的女儿,不能经常回老陈有几个在外地工作的女儿,不能经常回来,老陈和她们通过信件联系。她们的信来,老陈和她们通过信件联系。她们的信会被邮递员投递到老陈的信箱里。会被邮递员投递到老陈的信箱里。情况与情况与Socket模型非常类似。模
30、型非常类似。selectselect模型模型 老陈非常想看到女儿的信。以至于老陈非常想看到女儿的信。以至于他每隔他每隔10分钟就下楼检查信箱分钟就下楼检查信箱,看是否有女儿的信,看是否有女儿的信,在这种情况下,在这种情况下,“下楼检查信箱下楼检查信箱”然后回然后回到楼上耽误了老陈太多的时间,以至于老到楼上耽误了老陈太多的时间,以至于老陈无法做其他工作。陈无法做其他工作。select模型和老陈的这种情况非常相似:模型和老陈的这种情况非常相似:周周而复始地去检查而复始地去检查.如果有数据如果有数据.接收接收/发发送送.套接字的套接字的WSAAsyncSelectWSAAsyncSelect模型模型
31、WSAWSAAsyncAsyncSelectSelect模型模型uWSAAsyncSelect模型是模型是WinSock中另一个常用的中另一个常用的异步异步I/O模型。模型。u该模型可在套接字上接收以该模型可在套接字上接收以Windows消息为基础消息为基础的网络事件通知。的网络事件通知。u调用调用WSAAsyncSelect函数自动将套接字设置为非函数自动将套接字设置为非阻塞模式,并向阻塞模式,并向WinSock DLL注册一个或多个感注册一个或多个感兴趣的网络事件,同时提供接收通知时使用的窗兴趣的网络事件,同时提供接收通知时使用的窗口句柄,当注册的网络事件发生时,对应的窗口口句柄,当注册的
32、网络事件发生时,对应的窗口将收到一个基于消息的通知。将收到一个基于消息的通知。WSAAsyncSelect函数如下:函数如下:int WSAAPI WSAAsyncSelect(SOCKET s,HWND hWnd,unsigned int wMsg,long lEvent);s为需要事件通知的套接字hWnd为接收消息的窗口句柄wMsg为要接收的消息lEvent为掩码,指定应用程序感兴趣的网络事件组合 用于WSAAsyncSelect函数的网络事件类型事件值事件值含义含义触发条件触发条件FD_READFD_READ套接字有可读消息通知套接字有可读消息通知recvrecv、recvfromrec
33、vfrom、WSARecvWSARecv或或WSARecvFromWSARecvFromFD_WRITEFD_WRITE套接字有可发消息通知套接字有可发消息通知sendsend、sendtosendto、WSASendWSASend或或WSASendToWSASendToFD_OOBFD_OOB套接字有外带数据消息通知套接字有外带数据消息通知recvrecv、recvfromrecvfrom、WSARecvWSARecv或或WSARecvFromWSARecvFromFD_ACCEPTFD_ACCEPT套接字有链接请求消息通知套接字有链接请求消息通知acceptaccept或或WSAAccep
34、t(WSAAccept(错误码不能错误码不能WSATRY_AGAIN)WSATRY_AGAIN)FD_CONNECTFD_CONNECT希望得到希望得到connectconnect或多点或多点joinjoin操操作完成信息通知作完成信息通知无无FD_CLOSE(FD_CLOSE(面向面向连接连接)套接字关闭消息通知套接字关闭消息通知无无FD_QOSFD_QOS套接字套接字QOSQOS状态发生变化消息通状态发生变化消息通知知WSAIoctl(SIO_GET_QOS)WSAIoctl(SIO_GET_QOS)用法举例用法举例要接收读写通知:要接收读写通知:#define WM_SOCKET WM_
35、USER+101int nResult=WSAAsyncSelect(s,hWnd,WM_SOCKET,FD_READ|FD_WRITE);if(nResult=SOCKET_ERROR)/错误处理错误处理 问题问题1:对监听套接字和通信套接字应如何设置消息通:对监听套接字和通信套接字应如何设置消息通知?设置哪些事件?知?设置哪些事件?监听套接字:监听套接字:WSAAsyncSelect(sListen,hWnd,WM_SOCKET,FD_ACCEPT|FD_CLOSE);通信套接字:通信套接字:WSAAsyncSelect(sComm,hWnd,WM_SOCKET,FD_READ|FD_WR
36、ITE|FD_CLOSE);注意注意:多个事件务必在套接字上一次注册!多个事件务必在套接字上一次注册!一旦在某个套接字上允许了事件通知,除非以后明一旦在某个套接字上允许了事件通知,除非以后明确调用确调用closesocket(),或者由应用程序针对该套,或者由应用程序针对该套接字调用了接字调用了WSAAsyncSelect,从而更改了注册,从而更改了注册的网络事件类型,否则的话,事件通知会永远有的网络事件类型,否则的话,事件通知会永远有效!效!若将若将lEvent参数设为参数设为0,效果相当于停止在套接字,效果相当于停止在套接字上进行的所有网络事件通知。上进行的所有网络事件通知。只接受一个消息
37、设置只接受一个消息设置.下面的代码将不会工作下面的代码将不会工作;第二个第二个调用将会使第一次调用的作用失效调用将会使第一次调用的作用失效,只有只有FD_WRITE会通过会通过wMsg2消息通知到。消息通知到。1、WSAAsyncSelect(s,hWnd,wMsg1,FD_READ);2、WSAAsyncSelect(s,hWnd,wMsg2,FD_WRITE);如果要取消所有的通知如果要取消所有的通知,也就是指出也就是指出Windows Sockets的实现不再在套接口上发送任何和网络事的实现不再在套接口上发送任何和网络事件相关的消息件相关的消息,则则lEvent应置为应置为0.WSAAs
38、yncSelect(s,hWnd,0,0);应用程序在一个套接字上成功调用了应用程序在一个套接字上成功调用了WSAAsyncSelect之后,应用程序会在与之后,应用程序会在与hWnd窗口句柄参数对应的窗窗口句柄参数对应的窗口处理函数中,以口处理函数中,以Windows消息的形式,接收网络消息的形式,接收网络事件通知。事件通知。窗口处理函数定义如下:窗口处理函数定义如下:LRESULT CALLBACK WindowProc(HWND hwhd,UINT uMsg,WPARAM wParam,LPARAM IParam)参数:参数:u hwnd:指向窗口的句柄。:指向窗口的句柄。u uMsg:
39、指定消息类型。:指定消息类型。u wParam:指定消息的附加信息。该参数的内容与:指定消息的附加信息。该参数的内容与UMsg参数值有关。参数值有关。u IParam:指定消息的附加信息。该参数的内容与:指定消息的附加信息。该参数的内容与uMsg参数值有关。参数值有关。返回值:返回值就是消息处理结果,它与发送的消息有关。返回值:返回值就是消息处理结果,它与发送的消息有关。wParam,IParam参数在参数在uMsg WM_SOCKET时时的含义:的含义:pwParam指定发生网络事件的套接字。指定发生网络事件的套接字。pIParam指定发生的网络事件和错误代码。指定发生的网络事件和错误代码。
40、其中其中高字位高字位指出指出网络错误网络错误。采用宏:。采用宏:WSAGETSELECTERROR,可用它返回高字节包,可用它返回高字节包含的错误信息,如下:含的错误信息,如下:#define WSAGETSELECTERROR(lParam)HIWORD(lParam)低字位低字位指出发生的指出发生的网络事件网络事件,采用宏:,采用宏:WSAGETSELECTEVENT,返回网络事件,返回网络事件#define WSAGETSELECTEVENT(lParam)LOWORD(lParam)收到收到 FD_READ 事件通知的情况:事件通知的情况:(1)调用)调用 WSAAsyncSelect
41、()()对对 socket 设设定定 FD_READ 事件时事件时,接收缓冲区中,接收缓冲区中已有已有数据数据。(2)接收缓冲区由空变为有数据接收缓冲区由空变为有数据(3)调用)调用 recv()()或或 recvfrom()从()从 接收缓冲区接收缓冲区读读取取数据时数据时没有没有读完。读完。收到收到FD_WRITE事件通知的情况:事件通知的情况:使用使用connect或或WSAConnect,一个套接字首次,一个套接字首次建立了连接。建立了连接。使用使用accept或或WSAAccept,套接字被接受以后。,套接字被接受以后。若若send、WSASend、sendto或或WSASendTo
42、操作操作失败,返回了失败,返回了WSAEWOULDBLOCK错误,而错误,而且缓冲区的空间变得可用。且缓冲区的空间变得可用。u应用程序,自收到首条应用程序,自收到首条FD_WRITE消息开始,便消息开始,便应认为自己必然能在一个套接字上发出数据,直至应认为自己必然能在一个套接字上发出数据,直至一个一个send、WSASend、sendto或或WSASendTo返回返回套接字错误套接字错误WSAEWOULDBLOCK。经过了这样。经过了这样的失败以后,系统要再用另一条的失败以后,系统要再用另一条FD_WRITE通知通知应用程序再次发送数据。应用程序再次发送数据。使用使用WSAAsyncSelec
43、tWSAAsyncSelect模型编程步骤:模型编程步骤:1.1.WinsockWinsock初始化初始化2.2.自定义自定义WM_SOCKET WM_SOCKET 消息消息3.3.创建窗口,必须有窗体才有消息处理创建窗口,必须有窗体才有消息处理4.4.创建套接字创建套接字5.5.调用调用WSAAsyncSelect()WSAAsyncSelect()定义需要响应的定义需要响应的SocketSocket动动作作6.6.编写编写WindowProc()WindowProc()完成完成SocketSocket相关动作处理相关动作处理示例:示例:WSAAsyncSelectWSAAsyncSelec
44、t模式的服务器模式的服务器#pragma once#include#include#define PORT 5150#define MSGSIZE 1024#define WM_SOCKET WM_USER+0#pragma comment(lib,ws2_32.lib)LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,PSTR szCmdLine,int iCmdShow)static TCHAR szAppName=
45、_T(AsyncSelect Model);HWND hwnd;MSG msg;WNDCLASS wndclass;/此处省略了此处省略了 wndclass 的风格定义的风格定义if(!RegisterClass(&wndclass)MessageBox(NULL,TEXT(This program requires Windows!),szAppName,MB_ICONERROR);return 0;hwnd=CreateWindow(szAppName,/window class name TEXT(AsyncSelect Model),/window caption WS_OVERLAP
46、PEDWINDOW,/window style CW_USEDEFAULT,/initial x position CW_USEDEFAULT,/initial y position CW_USEDEFAULT,/initial x size CW_USEDEFAULT,/initial y size NULL,/parent window handle NULL,/window menu handle hInstance,/program instance handle NULL);/creation parametersShowWindow(hwnd,iCmdShow);UpdateWin
47、dow(hwnd);while(GetMessage(&msg,NULL,0,0)TranslateMessage(&msg);DispatchMessage(&msg);return msg.wParam;LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)WSADATA wsd;static SOCKET sListen;SOCKET sClient;SOCKADDR_IN local,client;int ret,iAddrSize=sizeof(client);char szMessag
48、eMSGSIZE;switch(message)case WM_CREATE:WSAStartup(0 x0202,&wsd);/初始化初始化Winsock sListen=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);local.sin_addr.S_un.S_addr=htonl(INADDR_ANY);local.sin_family=AF_INET;local.sin_port=htons(PORT);bind(sListen,(struct sockaddr*)&local,sizeof(local);listen(sListen,3);/为为sLi
49、sten套接字定义套接字定义FD_ACCEPT事件的消息触发事件的消息触发 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,(struct sockaddr*)&client,&iAddrSize);/为为sClient套接字关联套接字关联FD_READ|
50、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 szMessageret=0;send(wParam,szMessage,strlen(szMessage),0);break;case FD_CLOSE:closesocket(wP