前面介绍的socket操作中,有一个非常大的缺陷,当一个socket运行accept,recv,recvfrom等网络操作的时候,程序就要等待函数成功返回一个值后才能继续往下执行。譬如说,一个tcp socket用recv函数接收数据,那么程序的执行就停留在recv语句上一直等待数据的到来,这就是所谓的同步套接字。有米办法让程序不会卡在accept,recv,recvfrom这样的网络操作上呢?简单的方法是把这样的操作放到一个新开的线程上,但这不是一个好方法,会令代码变得复杂和线程增多难以管理。为了解决这个问题异步套接字就产生了。异步套接字的使用流程如下:
1.为工程链接导入库文件 ws2_32.lib ,然后添加头文件 #include <Winsock2.h> ,然后在App类的InitInstance()函数里面加载套接字库,注意必须加载2.0或以上版本的套接字库。
2.下面以对话框工程TCP socket为例:
为对话框类添加一个socket类型的成员变量: SOCKET socketrecv;
然后在On
socketrecv=WSASocket(AF_INET,SOCK_STREAM,0,NULL,0,0);
//WSASocket函数是用于创建套接字的扩展函数,与socket函数相比多了后面三个参数,从而使他比socket函数拥有更强大功能,如果不需要用到后三个参数新增的功能,可以直接使用socket函数创建套接字,作用一样。
if(INVALID_SOCKET==socketrecv)
{
closesocket(socketrecv);
MessageBox("套接字创建失败!");
return FALSE;
}
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY); //本机任意IP地址
addrSrv.sin_family=AF_INET;
addrSrv.sin_port=htons(6500); //6500端口
int ret1;
ret1=bind(socketrecv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR)); //绑定套接字
if(SOCKET_ERROR==ret1)
{closesocket(socketrecv);
MessageBox("套接字绑定失败!");
return FALSE;
}
int ret2;
ret2=listen(socketrecv,5); //开始监听
if(SOCKET_ERROR==ret2)
{
closesocket(socketrecv);
MessageBox("套接字监听失败!");
return FALSE;
}
if(SOCKET_ERROR==WSAAsyncSelect(socketrecv,m_hWnd,UM_SOCK,FD_ACCEPT))
{MessageBox("注册网络ACCEPT事件失败!");
return FALSE;
}
//WSAAsyncSelect函数用于请求一个网络事件通知,最后的参数是事件类型,本例注册了一个FD_ACCEPT事件,这样,当有socket 请求连接(connect)socketrecv的时候,就会触发FD_ACCEPT事件,系统就会发送UM_SOCK消息(UM_SOCK是用户自定义消息),后面必须在UM_SOCK消息的消息响应函数里进行socketrecv的accept操作。查看MSDN可查询到其他可以注册的网络事件类型。
3.UM_SOCK消息处理:
假定UM_SOCK消息的消息响应函数是void On
void CXXXDlg::On
{
switch(LOWORD(lParam)) //参数lParam包含了网络事件的类型
{ case FD_ACCEPT:
if((SOCKET)wParam==socketrecv) //参数wParam包含了消息是针对哪个socket发出的
{
SOCKADDR_IN addrconn;
int len=sizeof(SOCKADDR);
socknec=accept(socketrecv,(SOCKADDR*)&addrconn,&len);
//开始接收连接,socknec是对话框类的一个socket类型成员变量,连接成功后负责与客户端socket收送数据
if(INVALID_SOCKET ==socknec)
{ closesocket(socknec);
MessageBox("接收连接失败!");
break; //连接失败,跳出switch
}
break; //连接成功,跳出switch
}
}
}
socket使用完毕后调用closesocket()函数关闭一个socket以回收资源。程序关闭前也必须使用WSACleanup函数终止对套接字库使用,注意必须在App类(应用程序类)的析构函数中调用WSACleanup函数。
以上的例子只是显示了对TCP socket的accept操作的异步处理,可以通过MSDN查询WSAAsyncSelect函数查看其他能进行异步处理的网络事件。