WINSOCK I/O之WSAAsysncSelect

套接字模式有兩種模式一種是鎖定模式,一種是非鎖定模式

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錯誤,而且緩衝區的空間變得可用

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章