Chinaunix首页 | 论坛 | 博客
  • 博客访问: 466760
  • 博文数量: 41
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 140
  • 用 户 组: 普通用户
  • 注册时间: 2015-02-05 14:08
个人简介

c++ programmer

文章分类

全部博文(41)

文章存档

2020年(2)

2018年(3)

2017年(23)

2016年(13)

我的朋友

分类: C/C++

2016-12-25 16:02:51

winsock 模型


1. SELECT (选择模型)

  1.相关函数和数据结构

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

    nfds一个整数值,是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1,在windows系统中被忽略。

    readfds指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的读变化,即我们关心是否可以从这些文件中读取数据了,可以传入NULL值,表示不关心任何文件的读变化

    writefds指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的写变化,即我们关心是否可以向这些文件中写入数据了,可以传入NULL值,表示不关心任何文件的写变化。 

    exceptfds同上面两个参数的意图,用来监视文件错误异常

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

    返回值负值:select错误

    正值:某些文件可读写或出错,值为准备就绪的描述符数

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

注意:select返回时,无论返回值为何值,内核都会清除fd_setIO未就绪的套接字,只保留IO就绪的套接字。故返回0时,fd_set中套接字数量也为0

 ② FD_ZERO(fd_set*):清空fd_set
      
FD_SETSIZE(fd_set*):获取fd_set中套接字的个数。
      
FD_SET(SOCKET,fd_set*):将某套接字加入到fd_set中。
      
FD_CRL(SOCKET,fd_set*):从fd_set中删除某套接字。
      
FD_ISSET(SOCKET,fd_set*):判断某套接字是否在fd_set中。
   
③ timeval: 超时值结构体数据。

 2.编程流程(server)

 ① 创建套接字(socket)

 ② 绑定本地地址(bind)

 ③ 监听套接字(listen)

 ④ 定义fd_set数据,并将监听套接字加入到fd_set(fd_set,FD_ZERO,FD_SET)

 ⑤ 另一线程调用select等待IO就绪(select)

 ⑥ 判断监听套接字是否在fd_set中,接受连接或在客户端套接字上读写数据(FD_ISSET,accept,recv,send)

 ⑦ 清空fd_set,将监听套接字和客户端套接字加入到fd_set中;

 ⑧ 循环调用-⑦。

 3.优缺点及适用场景

 优点:

 ① 可以在单线程中对多个套接字进行操作,消耗资源少;

 ② 跨平台性好,方便移植性。

 缺点:

 ① 默认最多同时支持64个套接字;

 ② 实时性不够,非并发。

 适用场景:

2. WSAEVENTSELECT (事件模型)

 1.相关函数和数据结构

 ① int WSAEventSelect(SOCKET socketObject, WSAEVENT hEventObject, long lNetworkEvents)

    socketObject: 目标套接字,将与事件对象绑定。

    hEventObject: 目标时间对象,将与套接字绑定。

    inetworkEvent网络事件标志组合,标志相关类型IO操作就绪时,事件自动置为有信号状态。

    返回值:成功返回0,失败返回SOCKET_ERROR

 ② int WSAEnumNetworkEvents (SOCKET sockObject, WSAEVENT hEventObject, LPWSANETWORKEVENTS lpNetworkEvents, LPINT lpiCount)

    sockObject与事件对象绑定的套接字。

    hEventObject与套接字绑定的事件对象。

    lpNetworkEventsWSANETWORKEVENTS结构的数组,每一个元素记录了一个网络事件和相应的错误代码。

    lpiCount中的元素数目。在返回时,本参数表示中的实际元素数目;如果返回值是WSAENOBUFS,则表示为获取所有网络事件所需的元素数目。

    返回值:成功返回0,失败返回SOCK_ERROR

 ③ DWORD WSAWaitForMultipleEvents(DWORD cEvents, const WSAEVENT FAR * lphEvents, BOOL fWaitAll, DWORD dwTimeout, BOOL fAlertable )

    cEvents指出lphEvents所指数组中句柄的数目。句柄的最大值为WSA_MAXIMUM_WAIT_EVENTS

    lphEvents指向一个句柄

    fWaitAll指定等待类型。若为真TRUE,则当lphEvents中的所有同时有信号时,返回。若为假FALSE,则当任意一个有信号时即返回。在后一种情况下,返回值指出是哪一个造成返回。

    dwTimeout指定超时时间间隔(以毫秒计)。当超时间隔到,即返回,不论fWaitAll参数所指定的条件是否满足。如果dwTimeout为零,则测试指定的的状态,并立即返回。如果dwTimeoutWSA_INFINITE,则的超时间隔永远不会到。

    fAlertable指定当系统将一个输入/输出完成例程放入队列以供执行时,是否返回。若为真TRUE,则返回且执行完成例程。若为假FALSE不返回,不执行完成例程。

    返回值:成功,返回IO准备就绪的套接字对应的事件的索引;失败,返回WSA_WAIT_FAILED

 2.编程流程

 ① 创建套接字(socket)

 ② 绑定本地地址(bind)

 ③ 监听套接字(listen)

 ④ 定义存放套接字和事件的容器,创建事件对象,并将其与监听套接字绑定(WSACreateEventWSAEventSelect)

 ⑤ 另一线程调用WSAWaitForMultipleEvents等待事件触发;

 ⑥ 循环遍历事件容器,对有信号的事件及对应套接字,调用WSAEnumNetworkEvents 取出其对应就绪的IO操作,并进行处理;

 ⑦ 若为FD_ACCEPT,则创建新的套接字和事件对象,加入到对应容器中;

 ⑧ 重复-⑦。 

 3.优缺点及适用场景

 优点:

 ① 相对于select的主动查询,事件模型为被动通知,不需要一直轮询,并且具备较高的实时性。

 缺点:

 ① WSAWaitForMultipleEvents最多支持64个事件对象的监听,若要增加,则需另外开辟线程,增加线程上下文切换的资源消耗。

 适用场景:

3. 重叠IO

 Event

 1.相关函数和数据结构

 ① BOOL WSAGetOverlappedResult(SOCKET socketObject, LPWSAOVERLAPPED lpOverlapped, LPDWORD lpcbTransfer, BOOL fWait, LPDWORD lpdwFlags)

   socketObject套接字。

   lpOverlapped重叠IO数据结构,OVERLAPPED

   lpcbTransfer接受或发送数据的字节数。

   fWait等待标志,TURE一直等待,FALSE立即返回。

   lpdwFlags接受返回标志,当投递的操作为WSARecv时,一定不能为NULL

   返回值:成功:返回TRUE;失败:返回FALSE

 ② typedef struct _WSAOVERLAPPED

   
     ULONG_PTR Internal;
      ULONG_PTR InternalHigh;  

      union
       {
         struct { DWORD Offset; DWORD OffsetHigh; };
         PVOID  Pointer;  
       };  

     HANDLE    hEvent;
    } WSAOVERLAPPED, *LPWSAOVERLAPPED;

 2.编程流程

 ① 创建套接字(socket)

 ② 绑定本地地址(bind)

 ③ 监听套接字(listen)

 ④ 接受连接(accept;)

 ⑤ 定义OVERLAPPED数据结构;

 ⑥ 投递WSARecvWSASend等异步操作;

 ⑦ 调用SleepEx,如果不调用该函数,系统将无法调用完成例程函数。

 ⑧ 循环遍历事件容器,对有信号的事件及对应套接字和重叠IO数据,

 ⑨ 重复-⑧。

 3.优缺点及适用场景

 优点:

 ① 可以运行在支持Winsock2的所有Windows平台 ,而不像完成端口只是支持NT系统。

    ② 比起阻塞、selectWSAAsyncSelect以及WSAEventSelect等模型,重叠I/O(Overlapped I/O)模型使应用程序能达到更佳的系统性能。因为它和这4种模型不同的是,使用重叠模型的应用程序通知缓冲区收发系统直接使用数据

 缺点:

 ① 单线程最多支持64个事件,增加线程,需考虑线程上下文切换的代价。 

 完成例程

 1.相关函数和数据结构

 2.编程流程

 ① 创建套接字(socket)

 ② 绑定本地地址(bind)

 ③ 监听套接字(listen)

 ④ 定义存放套接字和事件的容器,创建事件对象;

 ⑤ 定义OVERLAPPED数据结构,将事件对象绑定到OVERLAPPED数据;

 ⑥ 投递AeecptExWSARecvWSASend等异步操作;

 ⑦ 另一线程调用WSAWaitForMultipleEvents等待事件触发;

 ⑧ 循环遍历事件容器,对有信号的事件及对应套接字和重叠IO数据,

 ⑨ 重置OVERLAPPED套接字的Event对象(WSAResetEvent)

 ⑩ 查看重叠操作结果,做相处理(WSAGetOverlappedResult)

 ① 重复-⑩。

 3. 优缺点及适用场景

4. IOCP (完成端口)

 1.相关函数和数据结构

 ① HANDLE CreateIoCompletionPort( HANDLE ileHandle,   HANDLE ExistingCompletionPort,  ULONG_PTR CompletionKey,  WORD NumberOfConcurrentThreads)
       ileHandle: 一个必须支持支持重叠IO操作的句柄,socket需强制转换成HANDLE。可以为INVALID_HANDLE_VALIE,为INVALID_HANDLE_VALIE时创建一个完成端口对象,不为INVALID_HANDLE_VALIE时,将一个handle与完成端口绑定。
       
ExistingCompletionPort: 将要与句柄绑定的目标完成端口,当句柄为空时,函数功能为创建一个完成端口,该值也必须为NULL。
       CompletionKey: 传递给完成端口处理线程的线程参数,若函数功能为创建一个完成端口时,须为NULL。
       NumberOfConCurrentThreads: 完成端口可调用的最大线程数。当函数功能为绑定一个句柄到完成端口时,该参数被忽略,该参数为0时,系统自动调用尽可能多的线程来处理完成端口操作。
       返回值:成功:返回一个新的完成端口或者目标绑定完成端口,失败:返回NULL。

 ② BOOL GetQueuedCompletionStatus(HANDLE CompletionPort, LPDWORD lpNumberOfBytes, PULONG_PTR lpCompletionKey, LPOVERLAPPED *lpOverlapped, DWORD dwMilliseconds)

    CompletionPort等待重叠IO操作就绪的完成端口对象。

    lpNumberOfBytes完成的重叠IO操作的字节数。

    lpCompletionKeyCreateIoCompletionPort中绑定的线程参数。

    lpOverlapped重叠IO操作投递时绑定的OVERLAPPED数据结构。
       
dwMilliseconds超时时间。
       
返回值:成功:返回TRUE;失败:返回FALSE

 ③ BOOL PostQueuedCompletionStatus(HANDLE CompletionPort, DWORD dwNumberOfBytesTransferred, ULONG_PTR dwCompletionKey, LPOVERLAPPED lpOverlapped)

     CompletionPort完成端口

    dwNumberOfBytesTransferred传递给GetQueuedCompletionStatusIO操作字节数的参数。

    dwCompletionKey传递给GetQueuedCompletionStatus的线程参数的参数。

    lpOverlapped传递给GetQueuedCompletionStatusOVERLAPPED的参数。

    返回值:成功:返回非0;失败:返回0

 ④ BOOL AcceptEx(SOCKET sListenSocket, SOCKET sAcceptSocket, PVOID lpOutputBuffer, DWORD dwReceiveDataLength, DWORD dwLocalAddressLength, 
               
DWORD dwRemoteAddressLength, LPDWORD lpdwBytesReceived, LPOVERLAPPED lpOverlapped)

     sListenSocket监听套接字。

     sAcceptSocket:接受连接客户端套接字。

     lpOutputBuffer:接收数据的buffer,包含第一次发送的数据,本地地址信息,远程地址信息。

     dwReceiveDataLength:接收数据部分的的长度,值为sizeof(lpOutputBuffer) - 2*(sizeof(SOCKADDR)+16)

     dwLocalAddressLength:本地地址长度,值为sizeof(SOCKADDR)+16

     dwRemoteAddressLength远端地址长度,值为sizeof(SOCKADDR)+16

     lpdwBytesReceived接收到的字节数。

     lpOverlapped本次操作绑定的重叠IO数据结构。

     返回值:成功::返回TRUE;失败:返回FALSE

 ⑤ void GetAcceptExSockaddrs(PVOID lpOutputBuffer, DWORD dwReceiveDataLength, DWORD dwLocalAddressLength, DWORD dwRemoteAddressLength, LPSOCKADDR *LocalSockaddr,
                       
LPINT LocalSockaddrLength, LPSOCKADDR *RemoteSockaddr, LPINT RemoteSockaddrLength)

      lpOutputBufferAccepEx中用于接受数据的buffer

      dwReceiveDataLength接收数据部分的的长度,值为sizeof(lpOutputBuffer) - 2*(sizeof(SOCKADDR)+16)

      dwLocalAddressLength本地地址长度,值为sizeof(SOCKADDR)+16

      dwRemoteAddressLength远端地址长度,值为sizeof(SOCKADDR)+16
       
LocalSockaddr接收本地地址。
       
LocalSockaddrLength接收到本地地址的长度。
       
RemoteSockaddr接收远程地址。
       
RemoteSockaddrLength接收到远程地址的长度。

 ⑥ BOOL TransmitFile(SOCKET hSocket, HANDLE hFile, DWORD nNumberOfBytesToWrite, DWORD nNumberOfBytesPerSend, 
                 
LPOVERLAPPED lpOverlapped, LPTRANSMIT_FILE_BUFFERS lpTransmitBuffers, DWORD dwReserved)

     hSocket传输文件使用的套接字。

     hFile文件句柄。

     nNumberOfBytesToWrite指定需要传输的字节数,若要传输整个文件,设置为0

     nNumberOfBytesPerSend每次返送的数据块的大小,使用默认大小,设置为1

     lpOverlapped绑定OVERLAPEED数据结构。

     lpTransmitBuffers在文件发送之前和发送完成之后要发送的数据。

     dwReserved指定传输标识。

     返回值:成功:返回true;失败:返回false

注意:使用TransmitFile传输文件时,文件传输将一直在内核状态下进行,从而避免了使用WSASendReadFile等操作产生的用户态和内核态之间的频繁状态切换。

 2.编程流程

 ① 创建套接字(WSASocket,必须指定WSA_FALG_OVERLAPPED标识)

 ② 创建完成端口并与套接字绑定(CreateIoCompletionPort)

 ③ 绑定本地地址(bind)

 ④ 监听套接字(listen)

 ⑤ 投递ACCEPT操作(AcceptExOVERLAPPED)

 ⑥ 各处理线程调用GetQueuedCompletionStatus等待重叠IO-ACCPET操作完成;

 ⑦ 接受客户端连接,将与客户端通信的套接字与完成端口绑定,继续投递ACCEPT操作(CreateIoCompletionPortAcceptExOVERLAPPED);

 ⑧ 根据需求,接收或发送数据到客户端(WSARecvWSASend);

 ⑨ 重复-⑧。

 ⑨ 传递关闭信息给各个处理线程,释放相关资源(PostQueuedCompletionStatus)

 3.优缺点及适用场景

 优点:

 ① 无并发上限限制;

 ② 在收到通知时数据已经收发完成,处理效率高;

 ③ cpu负载均衡。

 缺点:

 ① 只适合windows NT及以上平台。

 适用场景:大并发服务器。

阅读(1720) | 评论(0) | 转发(0) |
0

上一篇:UDP穿透简介

下一篇:IP地址分类

给主人留下些什么吧!~~