listen():设定 Socket 为监听状态,准备被连接。
格式: int PASCAL FAR listen( SOCKET s, int backlog );
参数: s Socket 的识别码
backlog 未真正完成连接前(尚未呼叫 accept 前)彼端的连接要求的最大个数
传回值: 成功 - 0
失败 - SOCKET_ERROR (呼叫 WSAGetLastError() 可得知原因)
说明: 使用者可利用此函式来设定 Socket 进入监听状态,并设定最多可有多少个在未真正完成连接前的彼端的连接要求。(目前最大值限制为 5, 最小值为1)
程式中我们将 backlog 设为 1 。
listen(listen_sd, 1)
呼叫完 listen 後,此时 Client 端如果来连接的话,Client 端的连接动作(connect)会成功,不过此时 Server 端必须再呼叫 accept() 函式,才算正式完成 Server 端的连接动作。但是我们什麽时候可以知道 Client 端来连接,而适时地呼叫 accept 呢?在这里我们就要利用一个很好用的 WSAAsyncSelect 函式,将 Server 端的这个 socket 转变成 Asynchronous 模式,让系统主动来通知我们有 Client 要连接了。(图1. 中并未将此函式绘出)
WSAAsyncSelect():要求某一 Socket 有事件 (event) 发生时通知使用者。
格 式: int PASCAL FAR WSAAsyncSelect( SOCKET s, HWND hWnd,unsigned int wMsg, long lEvent );
参 数: s Socket 的编号
hWnd 动作完成後,接受讯息的视窗 handle
wMsg 传回视窗的讯息
lEvent 应用程式有兴趣的网路事件
传回值: 成功 - 0
失败 - SOCKET_ERROR (呼叫 WSAGetLastError() 可得知原因)
说明: 此函式是让使用者用来要求 Windows Sockets DLL 在侦测到某一 Socket有网路事件时送讯息到使用者指定的视窗;网路事件是由参数 lEvent 设定。呼叫此函式会主动将该 Socket 设定为 Non-blocking 模式。lEvent 的值可为以下之「OR」组合:(参见 WINSOCK第1.1版88、89页) FD_READ、FD_WRITE、FD_OOB、FD_ACCEPT、FD_CONNECT、FD_CLOSE 使用者若是针对某一Socket再次呼叫此函式时,会取消对该 Socket 原先之设定。若要取消对该Socket 的所有设定,则 lEvent 的值必须设为 0。
(图2) WSAAsyncSelect 函式参数与应用程式关系
我们在程式中要求 Winsock 系统知道 Client 要来连接时,送一个ASYNC_EVENT 的讯息到程式中 hwnd 这个视窗;由於我们想知道的只有 accept事件,所以我们只设定 FD_ACCEPT。
WSAAsyncSelect(listen_sd, hwnd, ASYNC_EVENT, FD_ACCEPT)
(图 3)demoserv 在 WinKing 系统上建立 socket 并进入监听状态
读者必须注意一点,WSAAsyncSelect 的设定是针对「某一个 socket」;也就是说,只有当您设定的这个 socket (listen_sd)的那些事件(FD_ACCEPT)发生时,您才会收到这个讯息(ASYNC_EVENT)。如果您开启了很多 sockets,而要让每个 socket 都变成 asynchronous 模式的话,那麽就必须对「每一个 socket」都呼叫 WSAAsyncSelect 来一一设定。而如果您想将某一个 socket 的 async 事件通知设定取消的话,那麽同样也是用 WSAAsyncSelect 这个函式;且第四个参数lEvent 一定要设为 0。
WSAAsyncSelect( s, hWnd, 0, 0 ) -- 取消所有 async 事件设定
在这里笔者还要告诉各位一点,呼叫 WSAAsyncSelect 的同时也将此一 socket改变成「非阻拦」(non-blocking)模式。但是此时这个 socket 不能很简单地用 ioctlsocket() 这个函式就将它再变回「阻拦」(blocking)模式。也就是说WSAAsyncSelect 和 ioctlsocket 所改变的「非阻拦」模式仍是有些不同的。如果您想将一个「非同步」(asynchronous)模式的 socket 再变回「阻拦」模式的话,必须先呼叫 WSAAsyncSelect() 将所有的 async 事件取消,再用