Windows的网络编程-之四-套接字模型
来源:互联网 发布:数据库方言 编辑:程序博客网 时间:2024/05/25 21:34
1 套接字模型
1.1 select模型
利用select( ),我们可以判断套接字上是否存在数据,或者能否向一个套接字写入数据。
intselect( int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds,
conststruct timeval* timeout );
typedefstruct fd_set
{
u_int fd_count; //Number of sockets in theset
SOCKET fd_array[FD_SETSIZE]; // Array of sockets that are in the set
}fd_set;
FD_CLR(int fd, fd_set *set ) :从set中删除fd
FD_ISSET(intfd, fd_set *set) :检查fd是否是set的一名成员
FD_SET(intfd, fd_set *set) :将fd加入set
FD_ZERO(fd_set *set) :将set初始化成空
参数nfds被忽略。
参数readfds包括符合下述任何一个条件的套接字:
1.该套接字内存在数据可供读取;
2.该套接字已经被关闭、重设或中止。
3.如果已经调用过listen(),对该套接字调用accept( )会成功返回。
参数writefds包括符合下述任何一个条件的套接字:
1.该套接字内存在数据可以发出;
2.如果已经在非阻塞模式下调用过connect( ),该套接字的连接已经成功建立。
参数exceptfds包括符合下述任何一个条件的套接字:
1.该套接字内存在OOB数据可供读取;
2.如果已经在非阻塞模式下调用过connect( ),该套接字的连接没有被建立。
例如我们想测试一个套接字是否可读,必须将该套接字增添到readfds,select( )完成之后,必须判断该套接字是否仍为readfds的一部分。
参数timeout指定select( )最多等待多长时间。如果timeout是一个空指针,那么select()会无限期地阻塞下去,直到有一个套接字符合条件后结束。如果timeout指定的时间是0,表明select( )会立即返回,允许应用程序对select()进行轮询。
如果成功,就返回三个set中的所有套接字的数目;
如果超时,就返回0;
如果失败,就返回SOCKET_ERROR。
1.2 WSAAsyncSelect模型
要使用WSAAsyncSelect模型,首先必须用CreateWindow()创建一个窗口,再为该窗口提供一个窗口例程函数(Winproc)。
intWSAAsyncSelect( SOCKET s, HWND hWnd, unsigned int wMsg, long lEvent );
参数hWnd指定网络事件发生之后,收到消息的窗口。
参数wMsg指定在发生网络事件时发送到窗口的消息。
参数lEvent指定应用程序感兴趣的一系列网络事件的组合,若设为0,将清除所有网络事件。
FD_READ
套接字可读
FD_WRITE
套接字可写事件
FD_OOB
OOB数据到达套接字
FD_ACCEPT
连接请求事件
FD_CONNECT
连接事完成件
FD_CLOSE
套接字关闭事件
FD_QOS
套接字QoS更改事件
FD_GROUP_QOS
套接字组QoS更改事件
FD_ROUTING_INTERFACE_CHANGE
套接字的路由接口变化事件
FD_ADDRESS_LIST_CHANGE
套接字协议家族的地址列表变化事件
注意:若应用程序对一个套接字调用了WSAAsyncSelect( ),该套接字会自动变成非阻塞模式。
应用程序在一个套接字上成功调用了WSAAsyncSelect( )之后,在hWnd窗口例程中会以Windows消息的形式接收到网络事件通知。窗口例程通常定义如下:
LRESULTCALLBACK WindowProc( HWND hwnd, UINT uMsg,
WPARAMwParam, LPARAM lParam );
参数uMsg指定需要对哪些消息进行处理,应该设为WSAAsyncSelect( )中指定的消息。
参数wParam存放发生网络事件的套接字。
参数lParam的低两位字节存放发生的网络事件,高两位字节存放错误代码。
所以当网络事件消息抵达一个窗口例程后,应用程序首先应检查lParam的高字位,以判断是否在套接字上发生了一个网络错误。若没有产生任何错误,接着便读取lParam低字位的内容,以确定到底是哪种网络事件类型。
#defineWSAGETSELECTERROR(lParam) HIWORD(lParam)
#defineWSAGETSELECTEVENT(lParam) LOWORD(lParam)
1.3 WSAEventSelect模型
intWSAEventSelect( SOCKET s, WSAEVENT hEventObject, long lNetworkEvents );
事件通知模型首先要创建一个事件对象:
WSAEVENTWSACreateEvent(void);
WSACreateEvent( )返回一个创建好的事件对象句柄。WSACreateEvent( )创建的事件有两种状态:signaled和non-signaled,以及两种模式:manual-reset和auto-reset。刚创建好的事件对象处于non-signaled的状态,并用manual-reset模式。当注册过的网络事件放生后,事件对象的状态便会从non-signaled变成signaled。
BOOLWSAResetEvent( WSAEVENT hEvent );
WSAResetEvent( )将事件对象的状态从signaled变为non-signaled。
BOOLWSACloseEvent( WSAEVENT hEvent );
WSACloseEvent( )释放事件句柄占用的系统资源。
DWORDWSAWaitForMultipleEvents( DWORDcEvents, const WSAEVENT* lphEvents,
BOOLfWaitAll, DWORD dwTimeout,
BOOLfAlertable );
参数cEvents和lphEvents指定了一个事件对象数组,cEvents指定的是事件对象的数量,而lphEvents对应的是一个指针,用于直接引用该事件对象数组。
参数fWaitAll若设为TRUE,只有等lphEvents数组内的所有事件对象都进入signaled状态后,函数才会返回;若设为FALSE,任何一个事件对象进入signaled状态,函数就会返回,而且函数返回值指出了到底是哪个事件对象造成了函数的返回。
参数dwTimeout规定了等待的最长时间,以毫秒为单位。如设为0,函数会检查指定的事件对象的状态,并立即返回。假如没有signaled状态的事件对象,函数就会返回WSA_WAIT_TIMEOUT。如设为WSA_INFINITE,那么只有在一个事件对象的状态变为signaled后,函数才会返回。
参数fAlertable在WSAEventSelect模型的时候是可以忽略的,且应设为FALSE。
1.3.1 WSAEventSelect模型的实例
Index = WASWaitForMultipleEvents(……);
MyEvent = EventArray[Index];
知道了产生网络事件的套接字后,可以用WSAEnumNetworkEvents( )查询发生了哪种类型的网络事件:
intWSAEnumNetworkEvents( SOCKET s, WSAEVENT hEventObject,
LPWSANETWORKEVENTSlpNetworkEvents );
参数hEventObject是可选的,指定的事件对象必须处于signaled状态,而且WSAEnumNetworkEvents( )会自动将该事件对象重新变为non-signaled状态。
参数lpNetworkEvents用于接收套接字上发生的网络事件类型以及出现错误代码。
typedefstruct _WSANETWORKEVENTS
{
long lNetworkEvents;
int iErrorCode[FD_MAX_EVENTS];
}WSANETWORKEVENTS, *LPWSANETWORKEVENTS;
iErrorCode是一个错误代码数组,对每个网络事件类型都存在着一个事件索引,索引名字是在网络事件名字后面添加一个“_BIT”,例如,FD_READ的iErrorCode数组的索引标识符便是FD_READ_BIT:
if( NetworkEvents.lNetworkEvents &FD_READ )
{
if(NetworkEvents.iErrorCode[FD_READ_BIT ] != 0 )
{
printf("FD_READ failed with error %d\n ",NetworkEvents.iErrorCode[FD_READ_BIT] );
}
}
1.4 重叠模型
要在一个套接字上使用重叠模型,必须使用WSA_FLAG_OVERLAPPED标志创建一个套接字。如下所示:
s= WSASocket( AF_NET, SOCK_STREAM, 0, NULL,0, WSA_FLAG_OVERLAPPED );
创建套接字的时候,如果使用的是socket( ),那么会默认设置WSA_FLAG_OVERLAPPED标志。成功建好一个重叠模型的套接字后,可以调用下面的Winsock函数进行重叠I/O操作:
WSASend;WSASendTo;WSARecv;WSARecvFrom;WSAIoctl;AcceptEx
参数lpOverlapped指向WSAOVERLAPPED结构:
typedefstruct _WSAOVERLAPPED
{
DWORD Internal;
DWORD InternalHigh;
DWORD Offset;
DWORD OffsetHigh;
WSAEVENT hEvent;
}WSAOVERLAPPED, *LPWSAOVERLAPPED;
其中,Internal、InternalHigh、Offset和OffsetHigh均由系统在内部使用,不应由应用程序直接进行处理或使用。一个重叠操作完成之后,会将WSAOVERLAPPED结构中的hEvent状态从non-signaled变成signaled。通过WSAWaitForMultipleEvents( )发现一次重叠操作完成之后,可以调用WSAGetOverlappedResult( )判断重叠操作是否成功:
BOOLWSAGetOverlappedResult( SOCKET s, LPWSAOVERLAPPED lpOverlapped,
LPDWORDlpcbTransfer, BOOL fWait,
LPDWORDlpdwFlags );
参数lpcbTransfer负责接收一次重叠发送或接收操作实际传输的字节数。
参数fWait决定是否应该等待重叠操作完成。若设为TRUE,只有重叠操作完成后函数才会返回;若设为FALSE,而且重叠操作仍未完成,那么函数就会返回FALSE。
参数lpdwFlags负责接收标志,如果重叠调用WSARecv()或WSARecvFrom( )发出的。
WSAGetOverlappedResult( )成功返回TRUE,意味着重叠操作成功完成。
重叠模型也允许应用程序以一种重叠方式实现对连接的接受:
BOOLAcceptEx( SOCKET sListenSocket, SOCKET sAcceptSocket,
PVOIDlpOutputBuffer, DWORDdwReceiveDataLength,
DWORDdwLocalAddressLength, DWORDdwRemoteAddressLength,
LPDWORDlpdwBytesReceived, LPOVERLAPPEDlpOverlapped );
参数sListenSocket指定的是监听套接字。
参数sAcceptSocket指定的是接受连接请求的套接字。AcceptEx( )和accept( )的区别在于,我们必须提供接受连接请求的套接字,而不是让系统自动创建。
参数lpOutputBuffer指定的是一个缓冲区,负责三种数据的接收:服务器的本地地址,客户机的远程地址,以及在新建连接上发送的第一个数据块。
参数dwReceiveDataLength以字节为单位,指定在lpOutputBuffer中,保留多大的空间,用于数据的接收。如果设为0,表示在连接的接受过程中,不接收任何数据。
参数dwLocalAddressLength和dwRemoteAddressLength以字节为单位,指定在lpOutputBuffer中,保留多大的空间,在一个套接字被接受的时候,用于本地和远程地址信息的保存。要注意的是,和当前传送协议允许的最大地址长度比较起来,这里指定的缓冲区大小至少应多出16字节。
参数lpdwBytesReceived用于返回接收到的实际数据量,以字节为单位。
参数lpOverlapped对应的是一个OVERLAPPED结构。
GetAcceptExSockaddrs( )可以从lpOutputBuffer中解析出本地和远程地址元素:
voidGetAcceptExSockaddrs( PVOIDlpOutputBuffer, DWORDdwReceiveDataLength,
DWORDdwLocalAddressLength,
DWORDdwRemoteAddressLength,
LPSOCKADDR*LocalSockaddr,
LPINTLocalSockaddrLength,
LPSOCKADDR*RemoteSockaddr,
LPINTRemoteSockaddrLength );
参数lpOutputBuffer应设为AcceptEx()返回的lpOutputBuffer。
dwReceiveDataLength、dwLocalAddressLength以及dwRemoteAddressLength参数应设为与AcceptEx( )的dwReceiveDataLength、dwLocalAddressLength以及dwRemoteAddressLength参数相同的值。
1.4.1 重叠模型的回调函数
完成重叠操作的Winsock函数中有一个常用的参数:lpCompletionROUTINE,该参数指定一个回调函数,在重叠操作完成后调用。
voidCALLBACK CompletionROUTINE( DWORDdwError, IN
DWORDcbTransferred, IN
LPWSAOVERLAPPEDlpOverlapped, IN
DWORDdwFlags IN
);
参数dwError表明了重叠操作的完成状态。
参数cbTransferred表明了重叠操作实际传输的字节数。
参数lpOverlapped是Winsock函数中的WSAOVERLAPPED结构。
参数dwFlags目前尚未使用,应设为0。
- Windows的网络编程-之四-套接字模型
- windows 网络编程 几种套接字模型
- 使用异步套接字模式进行Windows网络编程
- Windows套接字I/O模型之套接字模式
- 套接字模型
- 套接字模型
- Linux网络通信编程(套接字模型TCP\UDP与IO多路复用模型select\poll\epoll)
- NetBots5.5代码分析之套接字模型1
- NetBots5.5代码分析之套接字模型2
- Windows的网络编程-之五-套接字选项和I/O控制
- windows下简单的网络套接字编程
- 网络编程之套接字
- 网络编程之套接字
- 套接字网络编程基础(四)
- Windows Socket网络编程--异步套接字
- Windows Socket 网络编程--异步套接字
- windows网络套接字编程总结
- 使用windows套接字进行网络编程
- Windows的网络编程-之二-面向连接的协议
- Windows的网络编程-之三-面向无连接的协议
- 我的2011--领导者与研发流程感悟
- C++项目中的extern "C" {}
- 概率、先验概率、后验概率
- Windows的网络编程-之四-套接字模型
- Windows的网络编程-之五-套接字选项和I/O控制
- kinect 学习笔记二(深度图像的利用--抠取用户躯体)
- Mark 一个类...一个很简单的二叉查找树...
- Windows中的字符类型和数据类型
- Linux Command Line 解析
- TOJ 1959 POJ 1562 Oil Deposits BFS DFS入门题 C语言
- 2011年个人总结
- 在view中使用ComControl