1.2.1 流式套接字编程(www.cppentry.com)(2)
(4) 监听
当服务器端的Socket对象绑定完成之后,必须建立一个监听的队列来接收客户端的连接请求。listen()函数使服务器端的Socket进入监听状态,并设定可以建立的最大连接数(目前最大值限制为5,最小值为1),该函数调用成功返回0,否则返回SOCKET_ERROR:
- int listen(
- SOCKET s, //需要建立监听的Socket
- int backlog //最大连接个数
- );
服务器端的Socket调用完listen()后,如果此时客户端调用connect()函数提出连接申请的话,服务器端必须再调用accept()函数,这样服务器端和客户端才算正式完成通信程序的连接动作。
为了知道什么时候客户端提出连接要求,从而服务器端的Socket在恰当的时候调用accept()函数完成连接的建立,我们就要使用WSAAsyncSelect()函数,让系统主动来通知我们有客户端提出连接请求了,该函数调用成功返回0,否则返回SOCKET_ERROR:
- int WSAAsyncSelect(
- SOCKET s, //Socket 对象
- HWND hWnd, //接收消息的窗口句柄
- unsigned int wMsg, //传给窗口的消息
- long lEvent //被注册的网络事件
- );
被注册的网络事件lEvent就是应用程序向窗口发送消息的网路事件,该值为下列值FD_READ、FD_WRITE、FD_OOB、FD_ACCEPT、FD_CONNECT、FD_CLOSE的组合,各个值的具体含义如下。
FD_READ:希望在套接字s收到数据时收到消息。
FD_WRITE:希望在套接字s上可以发送数据时收到消息。
FD_ACCEPT:希望在套接字s上收到连接请求时收到消息。
FD_CONNECT:希望在套接字s上连接成功时收到消息。
FD_CLOSE:希望在套接字s上连接关闭时收到消息。
FD_OOB:希望在套接字s上收到OOB数据时收到消息。
具体应用时,wMsg是在应用程序中定义的消息名称,而消息结构中的lParam则为以上各种网络事件名称。所以,可以在窗口处理自定义消息函数中使用以下结构来响应Socket的不同事件:
- switch(lParam) {
- case FD_READ:
- ...
- break;
- case FD_WRITE:
- ...
- break;
- ...
- }
(5) 服务器端接受客户端的连接请求
当Client提出连接请求时,Server端的hwnd窗口会收到Winsock Stack送来的我们自定义的一个消息,这时,我们可以分析lParam,然后调用相关的函数来处理此事件。为了使服务器端接受客户端的连接请求,就要使用accept()函数,该函数新建一个Socket与客户端的Socket相通,原先监听的Socket继续进入监听状态,等待其他客户端的连接要求,该函数调用成功返回一个新产生的Socket对象,否则返回INVALID_SOCKET:
- SOCKET accept(
- SOCKET s, //Socket的识别码
- struct sockaddr FAR *addr, //存放连接的客户端地址
- int FAR *addrlen //地址长度
- );
(6) 结束Socket连接
结束服务器和客户端的通信连接是很简单的,这一过程可以由服务器或客户机的任一端启动,只要调用closesocket()就可以了,而要关闭Server端监听状态的Socket,同样也是利用此函数。另外,与程序启动时调用WSAStartup()函数相对应,程序结束前,需要调用WSACleanup()来通知Winsock Stack释放Socket所占用的资源。这两个函数都是调用成功返回0,否则返回SOCKET_ERROR。closesocket()函数的原型如下:
- int closesocket(
- SOCKET s; //Socket的识别码
- );
(7) 最后调用WSACleanup
代码如下:
- int WSACleanup(void);