非阻塞套接字在进行可能阻塞的操作时会立即返回。下面几个模型中关键函数所做的工作就是:使用一定的机制确定当调用可能会阻塞的函数时不会被阻塞。
辅助函数说明:
int getsockname(
__in SOCKET s,
__out struct sockaddr *name,
__inout int *namelen
);
该函数用于获得绑定到socket上的地址和端口。
select模型
之所以叫select模型是因为该模型主要依靠select函数来完成,下面是select的函数原型:
int select(
__in int nfds,
__inout fd_set *readfds,
__inout fd_set *writefds,
__inout fd_set *exceptfds,
__in const struct timeval *timeout
);
该函数返回0说明超时,-1(即SOKCET_ERROR)表示发生错误,>0估计表示三个fd_set中成员的个数,及发生了对应事件的所有套接字的个数。
该函数能够让一个线程具有处理多个套接字的能力,我们有些socket函数会阻塞主要是因为当前套接字状态与调用函数所需的状态不一致导致的,比如用recv接收数据时,若此时套接字没有到来数据,则该函数阻塞,所以我们只要明确知道套接字的状态信息就可以用来确定接下来的函数调用是否会阻塞,例如如果数据已经到来,则调用recv函数肯定会返回(不考虑数据接收的长度问题)。select函数就是用来探查socket的状态的。它的2-4参数给出需要检查状态的socket,返回时这三个参数里面只剩下发生了特定事件的socket。
使用这种模型应该不要求套接字是非阻塞模式。
WSAAsyncSelect模型
主要依靠WSAAsyncSelect函数完成,它的思想和上面的select模型一样:
预先探查socket的状态,从而确定接下来的操作不会被阻塞。但是它的做法是当相应事件发生时给绑定的窗口发送消息,消息的参数离包含了消息类型。
该模式的Select函数自动将套接字设置为非阻塞模型。操作函数可以是recv这类的。
WSAEventSelect模型
该模型和上一模型类型类似,不过是把通知机制改变了,该模型在相应事件发生后使用对应事件对象通过应用程序而不是上面的消息。
Overlapped I/O模型
该模型类似于CreateFileEx中的重叠模型,使用当操作可以进行或操作完成后使用事件对象或APC进行响应。
涉及的函数有:
WSASocket:创建套接字;
WSASend:发送数据,一般用于tcp;
WSARecv:接收数据,一般用于tcp;
WSASendTo:发送数据,一般用于UDP;
WSARecvFrom:接收数据,一般用于UDP;
AcceptEx:接收连接;
WSAGetOverlappedResult:取得重叠操作的结果。他一次在一个重叠操作上等待。
CreateIoCompletionPort:该函数用于将socket关联到完成端口上。
I/O Completion Port模型
这一种模型使用完成端口,是伸缩性最好的一种I/O模型,它非常适合用来处理上百上千个套接字。它广泛应用于各种类型的高性能服务器如Apache。
涉及函数有:
CreateCompletionPort:完成端口管理一些工作线程来分发任务,其中之一的参数NumberOfConcurrentThreads是告诉完成端口
同时可以唤醒多少个工作线程。工作线程需要我们在线程函数中添加代码加入到该完成端口的管理队列中去。
不要将工作线程数目和NumberOfConcurrentThreads的值混淆。
GetQueuedCompletionStatus:该函数用来将当前线程加入到端口的工作队列中去,这样当事件到达完成端口时,完成端口将从可用线程队列中挑一个线程,并将事件及相关参数通过该函数返回给线程,线程工作这些参数可以处理这个事件。
PostQueuedCompletionStatus:这个函数用来想完成端口投递自定义的完成封包,主要用来告诉工作线程特定事件发生了,如可以用这这样的包表示任务完成,线程可以退出了。
另外,closesocket函数关闭socket会导致该socket上所有重叠操作完成。
Windows为传输文件提供了一个专门的函数:TransmitFile。可以试试性能。
TransmitPackets函数扩展了TransmitFile,也可以试试效果。
内存资源管理:重叠操作所提交的数据缓冲区可能会被锁定在内存中(即不能被换出到分页文件中),操作系统对锁定内存的数量有限制,
达到限制后,重叠操作将会以WSAENOBUFS错误失败,注意该错误将在调用WSASend或WSARecv时就返回,也就是所这个内存锁定应该是在调用时就确定的。因为要锁定内存,所以要考虑缓冲区的大小,最好是页的整数倍(GetSystemInfo)。
参考书籍:Windows网络与通信程序设计-王艳平