Windows Socket 异步编程(非阻塞模式)

1. Windows平台提供了5种非阻塞Socket编程模型:

Select模型(集合管理多个Socket,集合中有64个元素,可以管理1024个socket)

WSAAsyncSelect模型(消息通知应用程序)

WSAEventSelect模型(事件通知应用程序)

每次只能等待64个事件

重叠I/O模型(Overlapped I/O,事件通知和完成例程来通知应用程序)

事件通知即通过事件来通知应用程序I/O操作已完成,完成例程则是预先定义的回调函数。

完成端口模型(Completion Port)

比较成熟,使用线程池来处理异步I/O请求

2. ioctlsocket

int PASCAL FAR ioctlsocket( SOCKET s, long cmd, u_long FAR* argp);

s:一个标识套接口的描述字。

cmd:对套接口s的操作命令。

argp:指向cmd命令所带参数的指针。
 
 
cmd参数可选值:

FIONBIO:

  允许或禁止套接口s的非阻塞模式。argp指向一个无符号长整型。如允许非阻塞模式则非零,如禁止非阻塞模式则为零。

FIONREAD:

  确定套接口s自动读入的数据量。argp指向一个无符号长整型,其中存有ioctlsocket()的返回值。如果s是SOCKET_STREAM类型,则FIONREAD返回在一次recv()中所接收的所有数据量,这通常与套接口中排队的数据总量相同。如果s是SOCK_DGRAM 型,则FIONREAD返回套接口上排队的第一个数据报大小。

SIOCATMARK:

  确实是否所有的带外数据都已被读入。这个命令仅适用于SOCK_STREAM类型的套接口,且该套接口已被设置为可以在线接收带外数据(SO_OOBINLINE)。如无带外数据等待读入,则该操作返回TRUE真。否则的话返回FALSE假,下一个recv()或recvfrom()操作将检索“标记”前一些或所有数据。应用程序可用SIOCATMARK操作来确定是否有数据剩下。如果在“紧急”(带外)数据[前有常规数据,则按序接收这些数据(请注意,recv()和recvfrom()操作不会在一次调用中混淆常规数据与带外数]据)。argp指向一个BOOL型数,ioctlsocket()在其中存入返回值。
 
返回值:
成功后,ioctlsocket()返回0。否则的话,返回SOCKET_ERROR错误,应用程序可通过WSAGetLastError()获取相应错误代码。
错误代码:
WSANOTINITIALISED:在使用此API之前应首先成功地调用WSAStartup()。
WSAENETDOWN:WINDOWS套接口实现检测到网络子系统失效。
WSAEINVAL:cmd为非法命令,或者argp所指参数不适用于该cmd命令,或者该命令不适用于此种类型的套接口。
WSAEINPROGRESS:一个阻塞的WINDOWS套接口调用正在运行中。
WSAENOTSOCK:描述字不是一个套接口。

3. Select

int select(
  int nfds,
  fd_set FAR* readfds,
  fd_set FAR* writefds,
  fd_set FAR* exceptfds,
  const struct timeval FAR* timeout
);

nfds: 本参数忽略,仅起到兼容作用。

fd_set* readfds: 是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的读变化的,即我们关心是否可以从这些文件中读取数据了,如果这个集合中有一个文件可读,select就会返回一个大于0的值,表示有文件可读,如果没有可读的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的读变化。

fd_set*writefds: 是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的写变化的,即我们关心是否可以向这些文件中写入数据了,如果这个集合中有一个文件可写,select就会返回一个大于0的值,表示有文件可写,如果没有可写的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,若发生错误返回负值。可以传入NULL值,表示不关心任何文件的写变化。

fd_set *errorfds: 同上面两个参数的意图,用来监视文件错误异常。

struct timeval *timeout: 是select的超时时间,这个参数至关重要,它可以使select处于三种状态,第一,若将NULL以形参传入,即不传入时间结构,就是将select置于阻塞状态,一定等到监视文件描述符集合中某个文件描述符发生变化为止;第二,若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;第三,timeout的值大于0,这就是等待的超时时间,即select在timeout时间内阻塞,超时时间之内有事件到来就返回了,否则在超时后不管怎样一定返回,返回值同上述。

structtimeval{
  long tv_sec;//seconds
  long tv_usec;//mill seconds
};

返回值:

  负值:select错误

  正值:某些文件可读写或出错

  0:等待超时,没有可读写或错误的文件

 

对socket 和 fd_set集合的操作

FD_SET(int fd, fd_set *fdset);    //向集合中添加socket s

FD_CLR(int fd, fd_set *fdset);    //从集合中删除指定的socket s

FD_ISSET(int fd, fd_set *fdset);   //如果s是结合中的成员,则返回非0,否则返回0

FD_ZERO(fd_set *fdset);        //将集合初始化为空集合

4. WSAFunc

SOCKET WSASocket(
  __in  int af,
  __in  int type,
  __in  int protocol,
  __in  LPWSAPROTOCOL_INFO lpProtocolInfo,
  __in  GROUP g,
  __in  DWORD dwFlags
);

dwFlags:

WSA_FLAG_OVERLAPPED:Create a socket that supports overlapped I/O operations.

Most sockets should be created with this flag set. Overlapped sockets can utilize 

WSASendWSASendToWSARecvWSARecvFrom, andWSAIoctl for

overlapped I/O operations, which allow multiple operations to be initiated

and in progress simultaneously.

All functions that allow overlapped operation (WSASendWSARecvWSASendToWSARecvFromWSAIoctl)also support nonoverlapped usage on an overlapped socket if the values for parameters related to overlapped operations are NULL.

 

 (更加详细的解释,请参看MSDN http://msdn.microsoft.com/en-us/library/ms742212(v=vs.85).aspx)

示例代码:

ListenSocket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED); 

 
int WSASend(
  __in   SOCKET s,
  __in   LPWSABUF lpBuffers,
  __in   DWORD dwBufferCount,
  __out  LPDWORD lpNumberOfBytesSent,
  __in   DWORD dwFlags,
  __in   LPWSAOVERLAPPED lpOverlapped,
  __in   LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
);
  
 
int WSASendTo(
  __in   SOCKET s,
  __in   LPWSABUF lpBuffers,
  __in   DWORD dwBufferCount,
  __out  LPDWORD lpNumberOfBytesSent,
  __in   DWORD dwFlags,
  __in   const struct sockaddr *lpTo,
  __in   int iToLen,
  __in   LPWSAOVERLAPPED lpOverlapped,
  __in   LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
);
  
 
int WSARecv(
  __in     SOCKET s,
  __inout  LPWSABUF lpBuffers,
  __in     DWORD dwBufferCount,
  __out    LPDWORD lpNumberOfBytesRecvd,
  __inout  LPDWORD lpFlags,
  __in     LPWSAOVERLAPPED lpOverlapped,
  __in     LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
);
  
 
int WSARecvFrom(
  __in     SOCKET s,
  __inout  LPWSABUF lpBuffers,
  __in     DWORD dwBufferCount,
  __out    LPDWORD lpNumberOfBytesRecvd,
  __inout  LPDWORD lpFlags,
  __out    struct sockaddr *lpFrom,
  __inout  LPINT lpFromlen,
  __in     LPWSAOVERLAPPED lpOverlapped,
  __in     LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
);
  
 
int WSAIoctl(
  __in   SOCKET s,
  __in   DWORD dwIoControlCode,
  __in   LPVOID lpvInBuffer,
  __in   DWORD cbInBuffer,
  __out  LPVOID lpvOutBuffer,
  __in   DWORD cbOutBuffer,
  __out  LPDWORD lpcbBytesReturned,
  __in   LPWSAOVERLAPPED lpOverlapped,
  __in   LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
);
  

 

5. WSAAsyncSelect(具体参照MSDN)

int WSAAsyncSelect(
  __in  SOCKET s,
  __in  HWND hWnd,
  __in  unsigned int wMsg,
  __in  long lEvent
);
  s 标识一个需要事件通知的套接口的描述符.
  hWnd 标识一个在网络事件发生时需要接收消息的窗口句柄.
  wMsg 在网络事件发生时要接收的消息.
  lEvent 位屏蔽码,用于指明应用程序感兴趣的网络事件集合.
注: WSAAsyncSelect()函数会自动将socket s设置为非阻塞模式。
缺点:需要窗口环境

6. WSAEventSelect

int WSAEventSelect(
  __in  SOCKET s,
  __in  WSAEVENT hEventObject,
  __in  long lNetworkEvents
);
s: A descriptor identifying the socket.
hEventObject: A handle identifying the event object to be associated with the specified set of FD_XXX network events.
lNetworkEvents: A bitmask that specifies the combination of FD_XXX network events in which the application has interest.
 
 
WSAEventSelect自动将socket s设置为非阻塞模式。
缺点:每次只能等待64个事件,但不像Async那样需要窗口
 
 

7. WSAEventSelect中涉及到的函数

WSAEVENT WSACreateEvent(void)
创建事件对象
BOOL WSAResetEvent(
  __in  WSAEVENT hEvent
)
将事件对象设置为无信号状态
BOOL WSASetEvent(
  __in  WSAEVENT hEvent
)
将事件对象设置有无信号状态
BOOL WSACloseEvent(
  __in  WSAEVENT hEvent
)
关闭事件对象
DWORD WSAWaitForMultipleEvents(
  __in  DWORD cEvents,
  __in  const WSAEVENT *lphEvents,
  __in  BOOL fWaitAll,
  __in  DWORD dwTimeout,
  __in  BOOL fAlertable
)

DWORD cEvents:等候事件的总数量

const WSAEVENT* lphEvents:事件数组的指针

BOOL fWaitAll:如果设置为 TRUE,则事件数组中所有事件被传信的时候函数才会返回,FALSE则任何一个事件被传信函数都要返回

DWORD dwTimeout:如果设置为0,函数会立即返回, 如果设置为 WSA_INFINITE只有满足参数fWaitAll指定的条件才会返回

BOOL fAlertable:主要用于重叠I/O模型

 
int WSAEnumNetworkEvents(
  __in   SOCKET s,
  __in   WSAEVENT hEventObject,
  __out  LPWSANETWORKEVENTS lpNetworkEvents
)

s: A descriptor identifying the socket.

hEventObject: An optional handle identifying an associated event object to be reset.

lpNetworkEvents: A pointer to a WSANETWORKEVENTS structure that is filled with a record of network events that occurred and any associated error codes.

转载于:https://www.cnblogs.com/hanyuanbo/archive/2012/03/31/2426666.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值