读者们必须注意,不同的 Winsock Stack 在执行这三件事时的顺序可能会不相同;有的 Winsock Stack 可能会先检查 Blocking 函式的动作是否已经完成了,然後再执行 Blocking Hook 函式;所以 Blocking Hook 函式有可能不会被呼叫到。待会解释完Blocking Hook 函式的重点後,读者们就可以知道笔者为什麽在前面告诉各位在使用 polling 方式时一定要非常小心了。
由上面的回圈,我们现在可以知道 Blocking Hook 函式的使用时机是让系统在等待 Blocking 函式完成前所呼叫的,它并不是给我们自己的应用程式所使用的。Winsock 系统本身内部就有一个预设的 Blocking Hook 函式;现在我们就来看一下 这个预设的 Blocking Hook 函式会做些什麽事?
BOOL DefaultBlockingHook(void) {
MSG msg;
BOOL ret;
/* 取得下一个讯息;如果有,就处理它;如果没有,就释出控制权 */
ret = (BOOL) PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
if (ret) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return ret;
}
哦!原来 Blocking Hook 函式中很重要的地方就是:让 Blocking 函式在等待动作完成前能够处理其他讯息,或是释出 CPU 控制权,以便让其他的应用程式也有执行的机会。现在回到前面一点的地方,大家仔细想一想:如果在一个 Winsock Stack 的Blocking 函式的回圈内,先检查 Blocking 函式的动作是否已经完成了,然後再执行 Blocking Hook 函式的话;那麽是否就有可能不会释出 CPU 控制权来让其他的程式有执行的机会呢?如果我们的程式中再有类似下面的一个回圈,那麽整个 Windows 环境可能就会因我们的程式而 hang 住了。
for (;;) {
FD_ZERO(&writefds);
FD_SET( s, &writefds );
timeout.tv_sec = timeout.tv_usec = 0;
n = select( 64, NULL, &writefds, NULL, &timeout );
if ( n > 0 )
break;
if ( n == 0) /* timeout */
continue;
}
send( s, data );
在这个回圈例子中,我们原是希望利用 select() 及 polling 的方式来检查 socket
的 output buffer 中是否尚有空间可写入资料?如果此时 output buffer 恰好满了,select() 函式中一检查到如此的情况,且 timeout 又是 {0,0},那麽就会马上 return
0,而不会呼叫到 Blocking Hook 函式来释放 CPU 控制权给 Windows 环境中的其他程式(包括 Winsock 收送的 Protocol Stack );由於没有分配到 CPU 时间,所以 Winsock Kernel 便无法将 output buffer 中任何资料送出;回圈中由 select() 回?.後,又回到回圈的最前面,然後又呼叫 select(),马上又 timeout;Windows 系统因此就hang 住了 !
Blocking Hook 函式中除了 CPU 控制权释放的问题外,还需注意什麽呢?大家再看一看前面 Blocking 函式的回圈;回圈内呼叫 Blocking Hook 函式是包在另一个无穷的 while 回圈内。如果一个 Blocking Hook 函式的 return 值永远不为 0 的话,那麽也就永远被困在这个无穷回圈内了;所以我们在设计自己的 Blocking Hook 函式时一定也要非常小心这个 return 值。
知道了 Blocking Hook 函式的用途及设计 Blocking Hook 函式该注意的地方後,我们究竟要如何取代掉系统原有的 Blocking Hook 函式呢?那就要利用WSASetBlockingHook() 函式了。
◎ WSASetBlockingHook():建立应用程式指定的 blocking hook 函式。
格式: FARPROC PASCAL FAR WSASetBlockingHo