关于WSAAsyncSelect 的使用注意点

基于MFC的消息处理,微软提供一个函数WSAAsyncSelect, 把winsock的事件通知抛到的UI线程。

其实我们在MFC中编程,代码处理大多在这个主UI线程中,类定义变量也好处理,最重要的一点是不需要考虑多线程的机制了。我见过之前的工作同事,他们就是用单独的读写线程来处理socket,然后通过sendmessage发送消息到主UI线程来进行事务处理,他们觉得还挺好用。其实这就是WSAAsyncSelect实现的机制了。

但是WSAAsyncSelect的使用需要我们注意几点,在微软的MSDN文档中有详细的描述了(https://docs.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-wsaasyncselect)。但真正理解的人却不多。总结一下:

 

1. WSAAsyncSelect自动就把socket置为非阻塞模式,所以在后续的读写当中会很容易碰到读写失败,但是我想要的数据(以读为例),还未读够。这时候你得通过WSAGetLastError判断是否由于WSAEWOULDBLOCK的原因,如果是的话,你得循环继续读,直到满足你的要求了;

2.WSAAsyncSelect上报FD_READ消息,但是你读的时候有可能失败,返回WSAEWOULDBLOCK的错误,是WSAAsyncSelect误报了吗? 不是,过程可能是这样的:

*WSAAsyncSelect 发现有100个字节收到了,上报FD_READ消息;

*在你的消息处理中,你第一次通过recv收了50个字节,winsock在你调用recv的时候判断是否含有数据可读,它继续发一个FD_READ到你的UI线程(现在还没出来呢)

*还是在当前的消息处理中,你又recv读了25个字节,winsock还会再发一个FD_READ

*还是在当前的消息处理中,你最后又recv读了剩下25个字节,winsock还会再发一个FD_READ

3.当你处理完这个事务之后,你还会收到3个FD_READ,但是你去读的时候,却返回WSAEWOULDBLOCK的错误了---没有数据可读啊!

所以第3点的读和第1点的读应该要分别设计的,第3点发现WSAEWOULDBLOCK时要放弃返回,第1点则要循环死读下去。

Read3()

{

int rd;

 

rd = recv(m_Sock, (char *)&cmd, sizeof(int), 0);

if(rd == 0 || rd == SOCKET_ERROR)

{

return false;

}

return true;

}

 

Read1()

{

int rd, j = 0;

while(j < bytes)

{

rd = recv(sock, pbuf+j, bytes-j, 0);

if(rd == 0 || rd == SOCKET_ERROR)

{

int errnon = WSAGetLastError();

if(errnon == WSAEWOULDBLOCK)

continue;

else

return false;

}

j += rd;

}

}

 

4.还有比较重要的一点是FD_READ的重入问题:例如对方定期在发送查询udp包,我们通过WSAAsyncSelect收到消息之后,在处理,而如果你在处理过程中进入了UI消息处理(例如,MessageBox),那么又会被后续收到的udp包,再次重入你的处理函数了,这会把你搞惨的!

 

posted @ 2021-08-02 11:50  slowtech-QQ914100608  阅读(282)  评论(0编辑  收藏  举报