基于消息的异步套接字

         Windows套接字在两种模式下执行I/O操作,阻塞模式和非阻塞模式。在阻塞模式下,执行操作的函数会一直等待,不会立即返回,知道发送完数据或者接受完数据为止。这在一定条件下是对性能的浪费,例如recvfrom函数没有收到数据的时候吧就会一直等待下去。

         为了提高系统的性能,Winsock提供了基于消息的异步socket。下面介绍主要的Socket异步通信函数。

<1>int       WSASyncSelect(SOCKET s,HWND hwnd,unsigned int uMsg,longlEvent);

         这个函数的作用是注册一个lEvent事件,那么当lEvent事件被触发的时候(比如注册FD_READ,那么一旦有数据发送过来,那么就会触发lEvent事件),系统就会发送一个uMsg消息,发送的窗口就是hwnd句柄所指向的窗口。

<2>int       WSAEnumProtocols(LPINT lpiProtocols, LPWSAPROTOCOL_INFOlpProtocolBuffer,ILPDWORD lpdwBufferLength);

lpiProtocols:以NULL结尾的协议标识号数组,此参数可选,如果lpiProtocols为NULL,则函数返回所有可用协议的信息,否则只返回数组中列出的协议信息。

lpProtocolBuffer:out类型数据,作为返回值使用,一个用WSAPROTOCOL_INFO结构体填充的缓冲区。这个结构体用存放一个指定协议的完整信息。

lpdwBufferLength :In/out类型参数,在输入的时候,指定传递给lpProtocolBuffer缓冲区的长度,在输出的时候,获取存取所有请求信息传递给WSAEnumProtocols函数的最小缓冲区的长度。

<3>int       WSASocket(int af,int type,intprotocol,LPWSAPROTOCOL_INFO,lpProtocolInfo,GROUP g,DWORD dwFlags);

lpProtocolInfo:指向 WSAPROTOCOL_INFO结构体的指针,该结构体指定了所创建套接字的特性,如果lpProtocolInfo为NULL,则win32的Dll会使用前三个参数来决定使用哪一个服务提供者。

g:保留

dwFlags:指定套接字属性的描述。如果该参数为WSA_FLAG_OVERLAPPED,那么将创建一个重叠套接字。

<4> int WSARecvFrom(SOCKET s,LPWSABUFlpBuffers,DWORD dwBufferCount,LPDWORD lpNumerOfBytesRecvd,LPDWORDlpFlags,sockaddr *lpFrom,LPINT lpFromlen,LPWSAOVERLAPPED lpOverlapped,LPWSAOVERLAPPED_COMPLETION_ROUTINElpCompletionRoutine);

lpBuffers:in/out类型的参数,一个指向WSABUF结构体数组的指针,该结构体定义如下所示:

typedef struct  _WSABUF{
u_long len;
char *buf;
}WSABUF,*LPWSABUF;

可见这个结构体就是指向的接受数据的缓冲区以及长度。

dwBufferCount:lpBuffers所指向的数组中WSABUF结构体的数目。

lpNumberOfBytesRecvd:out类型的参数,如果接受操作立即完成,那么该参数是指向本次调用所接受字节数的指针。

lpFlags:in/out类型的参数,一个指向标志位的指针。

lpFrom:out类型的指针,是一个可选的指针,指向重叠操作完成后存放源地址的缓冲区。

lpFromlen:in/out类型的参数,一个指向lpFrom指定缓冲区大小的指针,仅当指定了lpFrom参数的时候才需要使用这个参数。

lpOverlapped:指向WSAOVERLAPPED结构体的指针,对于非重叠socket则忽略。

lpCompletionRoutine:一个指向接受操作完成时调用的完成例程的指针,对于非重叠socket则忽略。

<5>int     WSASendTo(SOCKET s,LPWSABUF lpBuffers,DWORDdwBufferCount,LPDWORD lpNumberOfByteSent,DWORD dwFlags,sockaddr* lpTo,intlen,LPWSAOVERLAPPED lpOverlapped,LPWSAOVERLAPPED_COMPLETION_ROUTINElpCompletionRoutine)

这个函数的参数是和WSARecvFrom的对应着,所以这个不用多说。

示例程序:

下面是一个简单的网络聊天程序,先说下它的流程:

1 加载套接字库

2 初始化并且绑定套接字,中间包括自定义消息的定义。

3 自定义消息响应函数完成

4 发送消息函数的完成。

核心代码如下所示:

//socket lib load
BOOLCmfcAChatApp::InitInstance()
{
         //加载套接字库
         WORD wVersionRequested;
         WSADATA wsaData;
         interr;
         wVersionRequested = MAKEWORD(2,2);
         err =WSAStartup(wVersionRequested,&wsaData);
         if (err!= 0)
         {
                   returnFALSE;
         }
         if(LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
         {
                   WSACleanup();
                   returnFALSE;
         }
//……
}

//  创建并且初始化套接字
BOOLCmfcAChatDlg::InitSocket(void)
{
         m_socket =WSASocket(AF_INET,SOCK_DGRAM,0,NULL,0,WSA_FLAG_OVERLAPPED);
         if(INVALID_SOCKET == m_socket)
         {
                   MessageBox(TEXT("Create socket failed"));
                   returnFALSE;
         }
        
         sockaddr_in addrSock;
         addrSock.sin_family = AF_INET;
         addrSock.sin_port = htons(6000);
         addrSock.sin_addr.S_un.S_addr =htonl(INADDR_ANY);
 
         interror = bind(m_socket,(sockaddr*)&addrSock,sizeof(sockaddr));
         if(SOCKET_ERROR == error)
         {
                   MessageBox(TEXT("bind socket failed"));
                   returnFALSE;
         }
 
         error =WSAAsyncSelect(m_socket,m_hWnd,UM_SOCK,FD_READ);
         if(SOCKET_ERROR == error)
         {
                   MessageBox(TEXT("register network event failed"));
                   returnFALSE;
         }
 
         returnTRUE;
}

//接受消息响应函数
fx_msg LRESULTCmfcAChatDlg::OnSock(WPARAM wParam, LPARAM lParam)
{
         switch(LOWORD(lParam))
         {
         caseFD_READ:
                   WSABUF wsaBuf;
                   wsaBuf.buf = new char[200];
                   wsaBuf.len = 200;
                   DWORD dwRead;
                   DWORD dwFlag = 0;
                   sockaddr_in addrFrom;
                   intlen = sizeof(sockaddr);
                   CString str;
                   CString strTemp;
                   if(SOCKET_ERROR ==WSARecvFrom(m_socket,&wsaBuf,1,&dwRead,&dwFlag,(sockaddr*)&addrFrom,&len,NULL,NULL))
                   {
                            MessageBox( TEXT("receive  datafailed") );
                            delete[] wsaBuf.buf;
                            wsaBuf.buf = NULL;
                            return 0;
                   }
                   str.Format(TEXT("%s say :%s"),inet_ntoa(addrFrom.sin_addr),wsaBuf.buf);
                   str += TEXT("\r\n");
                   GetDlgItemText(IDC_EDIT_RECV,strTemp);
                   strTemp += str;
                   SetDlgItemText(IDC_EDIT_RECV,strTemp);
                   delete[]wsaBuf.buf;
                   break;
         }
         return0;
}

//发送消息
void CmfcAChatDlg::OnBnClickedBtnSend()
{
         // TODO: 在此添加控件通知处理程序代码
         DWORD dwIP;
         CString strSend;
         WSABUF wsaBuf;
         DWORD dwSend;
         intlen;
         sockaddr_in addrTo;
         ((CIPAddressCtrl*)GetDlgItem(IDC_IPADDRESS1) )->GetAddress(dwIP);
         addrTo.sin_addr.S_un.S_addr =htonl(dwIP);
         addrTo.sin_family = AF_INET;
         addrTo.sin_port = htons(6000);
        
         GetDlgItemText(IDC_EDIT_SEND,strSend);
         len = strSend.GetLength();
         wsaBuf.buf = strSend.GetBuffer(len);
         wsaBuf.len = len + 1;
 
         if(SOCKET_ERROR == WSASendTo(m_socket,&wsaBuf,1,&dwSend,0,(sockaddr*)&addrTo,sizeof(sockaddr),NULL,NULL) )
         {
                   MessageBox(TEXT("Send message failed") );
                   return;
         }
         SetDlgItemText(IDC_EDIT_SEND,TEXT(""));
}

程序的运行结果

基于消息的异步套接字_第1张图片

你可能感兴趣的:(异步套接字)