WINSOCK I/O之WSAAsysncSelect

来源:互联网 发布:app 竞品数据分析 编辑:程序博客网 时间:2024/04/29 01:42

套接字模式有两种模式一种是锁定模式,一种是非锁定模式

SOCKET sock;

char buff[256];

int done=0;

while(!done)

{

    nBytes=recv(sock,buff,65);

    if(nBytes==SOCKET_ERROR)

    {

        printf("recv failed with error %d/n",WSAGetLastError());

        Return;

    }

}

以上代码是锁定模式,他的问题在于如果程序调用了recv而网络中没有待处理的数据,那他就不会返回,直到有数据到达,如果这段程序处于主线程中,那主线程就会锁定,所以,一种好的方法的把读写线程分开处理

SOCKET s;

unsigned long ul=1;

int nRet;

s=socket(AF_INT,SOCK_STREAM,0);

nRet=ioctlsocket(s,FIOBIO,(unsigned long*)&ul);

if(nRet==SOCKET_ERROR)

{

}

将这个SOCKET程序设置为非锁定以后,以后的调用都会立即返回,即使没有处理任何数据,这样为了能够正确的返回数据,我们必须不断的调用一个函数,好像很麻烦--#事实....

 Wi n s o c k提供了一个有用的异步I / O模型。利用这个模型,应用程序可在一个套接字上,接收以Wi n d o w s消息为基础的网络事件通知。所以要使用这种模式你必须有一个能够接收消息的窗口,这个简单....函数定义为:

int WSAAsyncSelect(

SOCKET s, //你感兴趣的那个SOCKET

HWND hWnd, //接收消息的窗口句柄

unsigned int uMsg,//自定义消息,一定要比WM_USER大

long lEvent

);

使用这个函数以后你的SOCKET模式会自动更改为非杜塞模式,所以我们主要以来uMsg发出的消息来判断网络事件已经发生,然后再采取相应的调用

F D _ R E A D 应用程序想要接收有关是否可读的通知,以便读入数据
F D _ W R I T E 应用程序想要接收有关是否可写的通知,以便写入数据
F D _ O O B 应用程序想接收是否有带外( O O B)数据抵达的通知
F D _ A C C E P T 应用程序想接收与进入连接有关的通知
F D _ C O N N E C T 应用程序想接收与一次连接或者多点j o i n操作完成的通知
F D _ C L O S E 应用程序想接收与套接字关闭有关的通知
F D _ Q O S 应用程序想接收套接字“服务质量”(Q o S)发生更改的通知
F D _ G R O U P _ Q O S 应用程序想接收套接字组“服务质量”发生更改的通知(现在没什么用处,为未来套接字组的使用保留)
F D _ R O U T I N G _ I N T E R FA C E _ C H A N G E 应用程序想接收在指定的方向上,与路由接口发生变化的通知
F D _ A D D R E S S _ L I S T _ C H A N G E 应用程序想接收针对套接字的协议家族,本地地址列表发生变化的通知

发送给窗口句柄的消息模式为UNIT uMsg内为自定义消息,WPARAM wParam指向发生网络事件的那个SOCKET,,LPARAM lParam的低字指定了已经发生的网络事件,高字为可能出现的错误代码,可以用宏,WSAGETSELECTERROR取出错误信息,用宏WSAGETSELECTEVENT取出发生的消息一下的简单的实例

OnInitDialog()函数中添加的代码于初始化

ADATA data;// Set small icon
 WSAStartup(MAKEWORD(0,2),&data);
 // TODO: Add extra initialization here
 Listen=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
 internetaddr.sin_family=AF_INET;
 internetaddr.sin_addr.s_addr=htonl(INADDR_ANY);
 internetaddr.sin_port=htons(5150);
 bind(Listen,(PSOCKADDR)&internetaddr,sizeof(internetaddr));
 hwnd=AfxGetMainWnd()->GetSafeHwnd();
 WSAAsyncSelect(Listen,hwnd,WM_SOCKET,FD_ACCEPT|FD_CLOSE); //设定listen套接字中感兴趣的消息
 listen(Listen,5);
 // TODO: Add extra initialization here
 Bcanwrite=FALSE; //初始化变量,确定SOCKET已经可写

WindowProc()函数中处理我们感兴趣的代码,也可以经自定义消息添加到消息映射中

 switch(message)
 {
 case WM_SOCKET:
  switch (WSAGETSELECTEVENT(lParam))
  {
  case FD_ACCEPT:
   Accept=accept(wParam,NULL,NULL); //负责从listen端口接受连接
   WSAAsyncSelect(Accept,hwnd,WM_SOCKET,FD_READ|FD_WRITE); //重新设置消息
   return TRUE;
  case FD_READ:
   //此处的消息必须处理以后才会从网络队列中删除窗口才会准备接收第二条FD_READ消息
   char buf[1024];
   recv(wParam,buf,1024,0);
   //wparam存放着发送消息的SOCKET,可以用来区分不同的SOCKET
   return TRUE;
  case FD_WRITE:
   if (Bcanwrite==FALSE)
   {
    Bcanwrite=TRUE;
    return TRUE;
    //当第一次接收到FD_WRITE消息时标志可以发送数据
   }
  }
  break;
 }

只有在三种条件下,才会发出F D _ W R I T E通知:

■ 使用c o n n e c t或W S A C o n n e c t,一个套接字首次建立了连接。
■ 使用a c c e p t或W S A A c c e p t,套接字被接受以后。
■ 若s e n d、W S A S e n d、s e n d t o或W S A S e n d To操作失败,返回了W S A E W O U L D B L O C K错误,而且缓冲区的空间变得可用

原创粉丝点击