5种winsock IO模型

合集下载

ch3 Winsock套接字IO模型(1)

ch3 Winsock套接字IO模型(1)

用于WSAAsyncSelect函数的网络事件类型
用法举例
要接收读写通知:
#define WM_SOCKET WM_USER + 101 int nResult = WSAAsyncSelect(s,hWnd,WM_SOCKET,FD_READ|FD_ WRITE); if(nResult==SOCKET_ERROR) { //错误处理 }
此函数用于检测一个或多个套接字的状态。套接 字的状态包括可读、可写、出错。 需要检测状态的套接字集合由一个fd_set结构指 示,分别为readfds,writefds,exceptfds(不能同 时为NULL)。 参数timeout指向timeval结构,用于指定select等 待I/O操作完成的时间。如timeout是一个空指针, I/O timeout select调用会无限期地,直到至少有一个套接字 满足条件。
readfds集合包括满足下述任何一个条件的套接字: 有数据可以读入。 连接已经关闭、重设或中止。 假如已调用了listen,而且一个连接正在建立,那么 accept函数调用会成功。 writefds集合包括满足下述任何一个条件的套接字: 有数据可以发出。 如果已完成了对一个非阻塞连接调用的处理,连接就 会成功。 exceptfds集合包括满足下述任何一个条件套接字: 假如已完成了对一个非阻塞连接调用的处理,连接尝 试就会失败。 有带外(Out-of-band,OOB)数据可供读取。
ioctlsocket( int ioctlsocket( SOCKET s, long cmd, u_long FAR *argp ); 用于设置和获取与套接字相关的操作参数。 参数 s:套接字描述符。 cmd:对套接字s的操作命令。 argp:指向cmd命令所带参数的指针。 成功返回0,否则,返回SOCKET_ERROR。

IOCP

IOCP

完成IO使用总结IOCP(I/O Completion Port,I/O完成端口)是性能最好的一种I/O模型。

它是应用程序使用线程池处理异步I/O请求的一种机制。

在处理多个并发的异步I/O请求时,以往的模型都是在接收请求是创建一个线程来应答请求。

这样就有很多的线程并行地运行在系统中。

而这些线程都是可运行的,Windows内核花费大量的时间在进行线程的上下文切换,并没有多少时间花在线程运行上。

再加上创建新线程的开销比较大,所以造成了效率的低下。

调用的步骤如下:抽象出一个完成端口大概的处理流程:1:创建一个完成端口。

2:创建一个线程A。

3:A线程循环调用GetQueuedCompletionStatus()函数来得到IO操作结果,这个函数是个阻塞函数。

4:主线程循环里调用accept等待客户端连接上来。

5:主线程里accept返回新连接建立以后,把这个新的套接字句柄用CreateIoCompletionPort 关联到完成端口,然后发出一个异步的WSASend或者WSARecv调用,因为是异步函数,WSASend/WSARecv会马上返回,实际的发送或者接收数据的操作由WINDOWS系统去做。

6:主线程继续下一次循环,阻塞在accept这里等待客户端连接。

7:WINDOWS系统完成WSASend或者WSArecv的操作,把结果发到完成端口。

8:A线程里的GetQueuedCompletionStatus()马上返回,并从完成端口取得刚完成的WSASend/WSARecv的结果。

9:在A线程里对这些数据进行处理(如果处理过程很耗时,需要新开线程处理),然后接着发出WSASend/WSARecv,并继续下一次循环阻塞在GetQueuedCompletionStatus()这里。

归根到底概括完成端口模型一句话:我们不停地发出异步的WSASend/WSARecv IO操作,具体的IO处理过程由WINDOWS系统完成,WINDOWS系统完成实际的IO处理后,把结果送到完成端口上(如果有多个IO 都完成了,那么就在完成端口那里排成一个队列)。

网络程序设计_socket_复习题_考点_知识点

网络程序设计_socket_复习题_考点_知识点

Winsock是什么?• Windows下网络编程的规范• Windows下得到广泛应用的、开放的、支持多种协议的网络编程接口。

• 已成为Windows网络编程的事实上的标准。

Windows socket规范• Windows Socket规范本意在于提供给应用程序开发者一套简单的API,并让各家网络软件供应商共同遵守。

Socket原理• Socket通常称为套接字、套管、插口,是两个程序间通信链路的端点。

• Socket实际上是一个编程接口,为网络应用程序提供各种接口函数。

Winsock基本概念• 多数网络协议都由软件实现,而且几乎所有计算机系统都将网络协议的实现作为操作系统的一部分,操作系统提供给用户的程序接口叫做应用程序编程接口(API )。

• 套接字接口(Socket Interface)就是一种API套接字及类型• 套接字(socket)是网络通信的基本构件,是可以被命名和寻址的通信端点,使用中的每一个套接字都有其类型和与之相连的进程。

• 套接字存在于通信区域中,通信区域也称地址族• 套接字通常只与同一区域中的套接字交换数据(也可跨区域通信,但要执行某种转换进程之后才能实现)。

• TCP/IP的socket提供三种类型的套接字:流式套接字(SOCK_STREAM)• 提供一个面向连接的、可靠的数据传输服务,• 内设流量控制,避免数据流超限;数据被看作是字节流,无长度限制。

• 文件传输协议(FTP)即使用流式套接字。

数据报式套接字(SOCK_DGRAM)• 提供一个无连接服务。

• 数据报以独立包形式被发送,不提供无错保证,数据可能丢失或重复,且接收顺序混乱。

• 网络文件系统(NFS)使用数据报式套接字。

原始式套接字(SOCK_RAW)• 该接口允许对较低层协议,如IP、ICMP直接访问。

• 常用于检验新的协议实现或访问现有服务中配置的新设备。

 • 服务方式面向连接(虚电路)• 面向连接服务是电话系统服务模式的抽象,每一次完整的数据传输都要经过建立连接、使用连接、终止连接的过程。

Windows网络编程复习重点

Windows网络编程复习重点

Windows⽹络编程复习重点⽬录第⼀章⽹络应⽤程序设计基础 (3)分布式⽹络应⽤程序的特点及分类 (3)⾯向应⽤的⽹络编程⽅法(3点) (3)基于TCP/IP的⽹络编程⽅法 (3)⾯向原始帧的⽹络编程⽅法(4种) (4)第⼆章⽹络程序通信模型 (4)常见的⽹络编程通信模型 (4)客户端、服务器的⼯作过程 (4)P2P模型的特点及其⽹络的拓扑结构 (5)第三章⽹络数据的内容与形态 (6)⽹络字节顺序与主机字节顺序 (6)⽹络字节顺序与主机字节顺序转换的函数 (6)⽹络数据传输形态 (6)字符编码 (7)数据校验 (7)第四章软件协议接⼝ (7)协议软件接⼝的位置与功能 (7)WinSock DLL的初始化与释放 (8)WinSock的地址描述 (9)套接字选项和IO控制命令 (9)处理WinSock的错误 (9)第五章流式套按字编程 (9)流式套接字通信的特点 (9)流式套接字的适⽤场景 (10)基本函数 (10)流式套接字编程的⼀般过程 (12)正确处理流数据的接收 (13)接收定长和变长数据 (13)流式套接字的编程实例 (13)第六章数据报套接字编程 (21)数据报套接字数据通信的特点 (21)数据报套接字的适⽤场景 (21)数据报套接字的编程的⼀般过程 (21)数据报套接字的常⽤函数 (22)数据报套接字的编程实例 (22)第七章原始套接字编程 (27)原始套接字的特点 (27)原始套接字的适⽤场景 (28)原始套按字的编程的⼀般过程 (28)原始套接字的编程实例 (28)第⼋章⽹络通信中的IO操作 (37)套接字的常见IO模式 (37)同步、异步、阻塞、⾮阻塞的概念及区别 (37)阻塞IO模型的⼯作原理与特点 (38)⾮阻塞IO模型的⼯作原理与特点 (38)IO复⽤模型的⼯作原理与特点(选择模型) (38)异步IO复⽤模型的⼯作原理与特点(WSAAsynsSelect) (38)完成端⼝模型的⼯作原理与特点(异步) (39)第九章WinPcap编程 (39)Wpcap.dll的⼯作流程 (39)Wpcap.dll的编程实例 (40)Packet.dll的⼯作流程 (45)Packet.dll编程实例 (45)第⼀章⽹络应⽤程序设计基础分布式⽹络应⽤程序的特点及分类1、特点:3点A)分布式⽹络应⽤程序将整个应⽤程序的处理分成⼏个部分,分别在不同的机器上运⾏,这⾥的“分布”包含两层含义:地理上的分布和数据处理的分布。

Socket编程模型之完成端口模型

Socket编程模型之完成端口模型

Socket编程模型之完成端口模型一、回顾重叠IO模型用完成例程来实现重叠I/O比用事件通知简单得多。

在这个模型中,主线程只用不停的接受连接即可;辅助线程判断有没有新的客户端连接被建立,如果有,就为那个客户端套接字激活一个异步的WSARecv操作,然后调用SleepEx使线程处于一种可警告的等待状态,以使得I/O完成后CompletionROUTINE可以被内核调用。

如果辅助线程不调用SleepEx,则内核在完成一次I/O操作后,无法调用完成例程(因为完成例程的运行应该和当初激活WSARecv异步操作的代码在同一个线程之内)。

完成例程内的实现代码比较简单,它取出接收到的数据,然后将数据原封不动的发送给客户端,最后重新激活另一个WSARecv异步操作。

注意,在这里用到了“尾随数据”。

我们在调用WSARecv的时候,参数lpOverlapped实际上指向一个比它大得多的结构PER_IO_OPERATION_DATA,这个结构除了WSAOVERLAPPED以外,还被我们附加了缓冲区的结构信息,另外还包括客户端套接字等重要的信息。

这样,在完成例程中通过参数lpOverlapped拿到的不仅仅是WSAOVERLAPPED结构,还有后边尾随的包含客户端套接字和接收数据缓冲区等重要信息。

这样的C语言技巧在我介绍完成端口的时候还会使用到。

二、完成端口模型“完成端口”模型是迄今为止最为复杂的一种I/O模型。

然而,假若一个应用程序同时需要管理为数众多的套接字,那么采用这种模型,往往可以达到最佳的系统性能!但不幸的是,该模型只适用于Windows NT和Windows 2000操作系统。

因其设计的复杂性,只有在你的应用程序需要同时管理数百乃至上千个套接字的时候,而且希望随着系统内安装的CPU数量的增多,应用程序的性能也可以线性提升,才应考虑采用“完成端口”模型。

要记住的一个基本准则是,假如要为Windows NT或Windows 2000开发高性能的服务器应用,同时希望为大量套接字I/O请求提供服务(Web服务器便是这方面的典型例子),那么I/O完成端口模型便是最佳选择!完成端口模型是我最喜爱的一种模型。

IOCP完成端口原理-详解

IOCP完成端口原理-详解

本文主要探讨一下windows平台上的完成端口开发及其与之相关的几个重要的技术概念,这些概念都是与基于IOCP的开发密切相关的,对开发人员来讲,又不得不给予足够重视的几个概念:1) 基于IOCP实现的服务吞吐量2)IOCP模式下的线程切换3)基于IOCP实现的消息的乱序问题。

一、IOCP简介提到IOCP,大家都非常熟悉,其基本的编程模式,我就不在这里展开了。

在这里我主要是把IOCP中所提及的概念做一个基本性的总结。

IOCP的基本架构图如下:如图所示:在IOCP中,主要有以下的参与者:--》完成端口:是一个FIFO队列,操作系统的IO子系统在IO操作完成后,会把相应的IO packet放入该队列。

--》等待者线程队列:通过调用GetQueuedCompletionStatus API,在完成端口上等待取下一个IO packet。

--》执行者线程组:已经从完成端口上获得IO packet,在占用CPU进行处理。

除了以上三种类型的参与者。

我们还应该注意两个关联关系,即:--》IO Handle与完成端口相关联:任何期望使用IOCP的方式来处理IO请求的,必须将相应的IO Handle与该完成端口相关联。

需要指出的时,这里的IO Handle,可以是File的Handle,或者是Socket的Handle。

--》线程与完成端口相关联:任何调用GetQueuedCompletionStatus API的线程,都将与该完成端口相关联。

在任何给定的时候,该线程只能与一个完成端口相关联,与最后一次调用的GetQueuedCompletionStatus为准。

二、高并发的服务器(基于socket)实现方法一般来讲,实现基于socket的服务器,有三种实现的方式(thread per request的方式,我就不提了:)):第一、线程池的方式。

使用线程池来对客户端请求进行服务。

使用这种方式时,当客户端对服务器的连接是短连接(所谓的短连接,即:客户端对服务器不是长时间连接)时,是可以考虑的。

WinSock三种选择IO模型

WinSock三种选择IO模型

在《套接字socket及C/S通信的基本概念》和《WinSock编程基础》中,我们介绍了套接字的基本概念和WinSock API的基本调用规范。

我们讨论了阻塞模式/非阻塞模式和同步I/O和异步I/O等话题。

从概念的角度,阻塞模式因其简洁易用便于快速原型化,但在应付建立连接的多个套接字或在数据的收发量不均、时间不定时却极难管理。

另一方面,我们需要对非阻塞模式套接字的 WinSock API调用频繁返回的WSAEWOULDBLOCK错误加以判断处理也显得难于管理。

WinSock套接字I/O模型提供了管理I/O 完成通知的方法,帮助应用程序判断套接字何时可供读写。

共有6中类型的套接字I/O模型可让WinSock应用程序对I/O进行管理,它们包括blocking(阻塞)、select(选择)、WSAAsyncSelect(异步选择)、WSAEventSelect(事件选择)、overlapped(重叠)以及completionport(完成端口)。

本文讨论三种选择(都带select)模型。

1.基于套接字集合的select模型(1)select模型概述该模型时最初设计是在不使用UNIX操作系统的计算机上实现的,它们采用的是Berkeley套接字方案。

select模型已集成到Winsock 1.1中,它使那些想避免在套接字调用过程中被无辜“锁定”的应用程序,采取一种有序的方式,同时进行对多个套接字的管理。

之所以称其为“select模型”,是由于它的“中心思想”便是利用select函数,实现对I/O的管理!使用select模型,一般需要调用ioctlsocket 函数将一个套接字从锁定模式切换为非锁定模式。

// 将套接字s设置为非阻塞模式unsigned long nonBlocking = 1;ioctlsocket(s, FIONBIO, (u_long*)&nonBlocking);select模型本质上是一种分类处理思想,预先声明几个FD_SET(fd_set 结构)集合(使用FD_ZERO初始化),例如ReadSet,WriteSet,然后调用宏FD_SET(s,&ReadSet)将关注FD_READ事件的套接字s添加到ReadSet 集合,调用宏FD_SET(s,&WriteSet)将关注FD_WRITE事件的套接字s添加到WriteSet集合。

IOCP详解

IOCP详解

IOCP详解简介: IOCP(I/O Completion Port,I/O完成端⼝)是性能最好的⼀种I/O模型。

它是应⽤程序使⽤线程池处理异步I/O请求的⼀种机制。

IOCP详解IOCP(I/O Completion Port,I/O完成端⼝)是性能最好的⼀种I/O模型。

它是应⽤程序使⽤线程池处理异步I/O请求的⼀种机制。

在处理多个并发的异步I/O请求时,以往的模型都是在接收请求是创建⼀个线程来应答请求。

这样就有很多的线程并⾏地运⾏在系统中。

⽽这些线程都是可运⾏的,Windows内核花费⼤量的时间在进⾏线程的上下⽂切换,并没有多少时间花在线程运⾏上。

再加上创建新线程的开销⽐较⼤,所以造成了效率的低下。

Windows Sockets应⽤程序在调⽤WSARecv()函数后⽴即返回,线程继续运⾏。

当系统接收数据完成后,向完成端⼝发送通知包(这个过程对应⽤程序不可见)。

应⽤程序在发起接收数据操作后,在完成端⼝上等待操作结果。

当接收到I/O操作完成的通知后,应⽤程序对数据进⾏处理。

完成端⼝其实就是上⾯两项的联合使⽤基础上进⾏了⼀定的改进。

⼀个完成端⼝其实就是⼀个通知队列,由操作系统把已经完成的重叠I/O请求的通知放⼊其中。

当某项I/O操作⼀旦完成,某个可以对该操作结果进⾏处理的⼯作者线程就会收到⼀则通知。

⽽套接字在被创建后,可以在任何时候与某个完成端⼝进⾏关联。

众所皆知,完成端⼝是在WINDOWS平台下效率最⾼,扩展性最好的IO模型,特别针对于WINSOCK的海量连接时,更能显⽰出其威⼒。

其实建⽴⼀个完成端⼝的服务器也很简单,只要注意⼏个函数,了解⼀下关键的步骤也就⾏了。

分为以下⼏步来说明完成端⼝:0) 同步IO与异步IO1) 函数2) 常见问题以及解答3) 步骤4) 例程0、同步IO与异步IO同步I/O⾸先我们来看下同步I/O操作,同步I/O操作就是对于同⼀个I/O对象句柄在同⼀时刻只允许⼀个I/O操作,原理图如下:由图可知,内核开始处理I/O操作到结束的时间段是T2~T3,这个时间段中⽤户线程⼀直处于等待状态,如果这个时间段⽐较短,则不会有什么问题,但是如果时间⽐较长,那么这段时间线程会⼀直处于挂起状态,这就会很严重影响效率,所以我们可以考虑在这段时间做些事情。

IOCP模型总结

IOCP模型总结

IOCP(I/O Completion Port,I/O完成端口)是性能最好的一种I/O模型。

它是应用程序使用线程池处理异步I/O请求的一种机制。

在处理多个并发的异步I/O请求时,以往的模型都是在接收请求是创建一个线程来应答请求。

这样就有很多的线程并行地运行在系统中。

而这些线程都是可运行的,Windows内核花费大量的时间在进行线程的上下文切换,并没有多少时间花在线程运行上。

再加上创建新线程的开销比较大,所以造成了效率的低下。

调用的步骤如下:抽象出一个完成端口大概的处理流程:1:创建一个完成端口。

2:创建一个线程A。

3:A线程循环调用GetQueuedCompletionStatus()函数来得到IO操作结果,这个函数是个阻塞函数。

4:主线程循环里调用accept等待客户端连接上来。

5:主线程里accept返回新连接建立以后,把这个新的套接字句柄用CreateIoCompletionPort 关联到完成端口,然后发出一个异步的WSASend或者WSARecv调用,因为是异步函数,WSASend/WSARecv会马上返回,实际的发送或者接收数据的操作由WINDOWS系统去做。

6:主线程继续下一次循环,阻塞在accept这里等待客户端连接。

7:WINDOWS系统完成WSASend或者WSArecv的操作,把结果发到完成端口。

8:A线程里的GetQueuedCompletionStatus()马上返回,并从完成端口取得刚完成的WSASend/WSARecv的结果。

9:在A线程里对这些数据进行处理(如果处理过程很耗时,需要新开线程处理),然后接着发出WSASend/WSARecv,并继续下一次循环阻塞在GetQueuedCompletionStatus()这里。

归根到底概括完成端口模型一句话:我们不停地发出异步的WSASend/WSARecv IO操作,具体的IO处理过程由WINDOWS系统完成,WINDOWS系统完成实际的IO处理后,把结果送到完成端口上(如果有多个IO都完成了,那么就在完成端口那里排成一个队列)。

Windows之IOCP

Windows之IOCP

Windows之IOCP IOCP全称I/O Completion Port,中⽂译为I/O完成端⼝。

IOCP是⼀个异步I/O的Windows API,它可以⾼效地将I/O事件通知给应⽤程序,类似于Linux中的Epoll,关于epoll可以参考1. 简介 IOCP模型属于⼀种通讯模型,适⽤于Windows平台下⾼负载服务器的⼀个技术。

在处理⼤量⽤户并发请求时,如果采⽤⼀个⽤户⼀个线程的⽅式那将造成CPU在这成千上万的线程间进⾏切换,后果是不可想象的。

⽽IOCP完成端⼝模型则完全不会如此处理,它的理论是并⾏的线程数量必须有⼀个上限-也就是说同时发出500个客户请求,不应该允许出现500个可运⾏的线程。

⽬前来说,IOCP完成端⼝是Windows下性能最好的I/O模型,同时它也是最复杂的内核对象。

它避免了⼤量⽤户并发时原有模型采⽤的⽅式,极⼤的提⾼了程序的并⾏处理能⼒。

2. 原理图 ⼀共包括三部分:完成端⼝(存放重叠的I/O请求),客户端请求的处理,等待者线程队列(⼀定数量的⼯作者线程,⼀般采⽤CPU*2个) 完成端⼝中所谓的[端⼝]并不是我们在TCP/IP中所提到的端⼝,可以说是完全没有关系。

它其实就是⼀个通知队列,由操作系统把已经完成的重叠I/O请求的通知放⼊其中。

当某项I/O操作⼀旦完成,某个可以对该操作结果进⾏处理的⼯作者线程就会收到⼀则通知。

通常情况下,我们会在创建⼀定数量的⼯作者线程来处理这些通知,也就是线程池的⽅法。

线程数量取决于应⽤程序的特定需要。

理想的情况是,线程数量等于处理器的数量,不过这也要求任何线程都不应该执⾏诸如同步读写、等待事件通知等阻塞型的操作,以免线程阻塞。

每个线程都将分到⼀定的CPU时间,在此期间该线程可以运⾏,然后另⼀个线程将分到⼀个时间⽚并开始执⾏。

如果某个线程执⾏了阻塞型的操作,操作系统将剥夺其未使⽤的剩余时间⽚并让其它线程开始执⾏。

也就是说,前⼀个线程没有充分使⽤其时间⽚,当发⽣这样的情况时,应⽤程序应该准备其它线程来充分利⽤这些时间⽚。

系统需求分析模板

系统需求分析模板

目录1。

范围 02。

总体要求 02。

1总体功能要求 02.2软件开发平台要求 02。

3软件项目的开发实施过程管理要求 (1)2.3。

1 软件项目实施过程总体要求 (1)2.3。

2 软件项目实施变更要求 (1)2.3.3 软件项目实施里程碑控制 (1)3. 软件开发 (2)3。

1软件的需求分析 (2)3。

1.1 需求分析 (2)3。

1.2 需求分析报告的编制者 (3)3。

1.3 需求报告评审 (3)3.1。

4 需求报告格式 (3)3。

2软件的概要设计 (3)3。

2.1 概要设计 (3)3.2.2 编写概要设计的要求 (3)3。

2。

3 概要设计报告的编写者 (3)3.2。

4 概要设计和需求分析、详细设计之间的关系和区别 (3)3.2.5 概要设计的评审 (3)3。

2.6 概要设计格式 (3)3。

3软件的详细设计 (4)3.3.1 详细设计 (4)3.3.2 特例 (4)3.3.3 详细设计的要求 (4)3。

3.4 数据库设计 (4)3。

3。

5 详细设计的评审 (4)3.3.6 详细设计格式 (4)3。

4软件的编码 (4)3。

4.1 软件编码 (4)3.4。

2 软件编码的要求 (4)3。

4。

3 编码的评审 (5)3。

4.4 编程规范及要求 (5)3.5软件的测试 (5)3。

5。

1 软件测试 (5)3.5。

2 测试计划 (5)3.6软件的交付准备 (5)3。

6。

1 交付清单 (5)3。

7软件的鉴定验收 (6)3。

7.1 软件的鉴定验收 (6)3.7.2 验收人员 (6)3。

7.3 验收具体内容 (6)3.7。

4 软件验收测试大纲 (6)3.8培训 (6)3.8。

1 系统应用培训 (6)3。

8.2 系统管理的培训(可选) (7)附录A 软件需求分析报告文档模板 (9)附录B 软件概要设计报告文档模板 (21)附录C 软件详细设计报告文档模板 (33)附录D 软件数据库设计报告文档模板 (43)附录E 软件测试(验收)大纲.................................................................... 错误!未定义书签。

WindowsSocket五种IO模型(异步选择)

WindowsSocket五种IO模型(异步选择)

Winsock 提供了⼀个有⽤的异步I/O模型。

利⽤这个模型,应⽤程序可在⼀个套接字上,接收以Windows消息为基础的络事件通知。

具体的做法是在建好⼀个套接字后,调⽤WSAAsyncSelect函数。

该模型最早出现于Winsock的1.1版本中,⽤于帮助应⽤程序开发者⾯向⼀些早期的16位 Windows平台(如Windows for Workgroups),适应其“落后”的多任务消息环境。

应⽤程序仍可从这种模型中得到好处,特别是它们⽤⼀个标准的Windows例程(常称为 "WndProc"),对窗⼝消息进⾏管理的时候。

该模型亦得到了Microsoft Foundation Class(微软基本类,MFC)对象CSocket的采纳。

(节选⾃《Windows络编程》第⼋章) 还是先贴出代码,然后做详细解释: #include #include #define PORT 5150 #define MSGSIZE 1024 #define WM_SOCKET WM_USER+0 #pragma comment(lib, "ws2_32.lib") LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = _T("AsyncSelect Model"); HWND hwnd ; MSG msg ; WNDCLASS wndclass ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = WndProc ; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = 0 ; wndclass.hInstance = hInstance ; wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ; wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ; wndclass.lpszMenuName = NULL ; wndclass.lpszClassName = szAppName ; if (!RegisterClass(&wndclass)) { MessageBox (NULL, TEXT ("This program requires Windows NT!"), szAppName, MB_ICONERROR) ; return 0 ; } hwnd = CreateWindow (szAppName, // window class name TEXT ("AsyncSelect Model"), // window caption WS_OVERLAPPEDWINDOW, // window style CW_USEDEFAULT, // initial x position CW_USEDEFAULT, // initial y position CW_USEDEFAULT, // initial x size CW_USEDEFAULT, // initial y size NULL, // parent window handle NULL, // window menu handle hInstance, // program instance handle NULL) ; // creation parameters ShowWindow(hwnd, iCmdShow); UpdateWindow(hwnd); while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg) ; DispatchMessage(&msg) ; } return msg.wParam; } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { WSADATA wsd; static SOCKET sListen; SOCKET sClient; SOCKADDR_IN local, client; int ret, iAddrSize = sizeof(client); char szMessage[MSGSIZE]; switch (message) { case WM_CREATE: // Initialize Windows Socket library WSAStartup(0x0202, &wsd); // Create listening socket sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // Bind local.sin_addr.S_un.S_addr = htonl(INADDR_ANY); local.sin_family = AF_INET; local.sin_port = htons(PORT); bind(sListen, (struct sockaddr *)&local, sizeof(local)); // Listen listen(sListen, 3); // Associate listening socket with FD_ACCEPT event WSAAsyncSelect(sListen, hwnd, WM_SOCKET, FD_ACCEPT); return 0; case WM_DESTROY: closesocket(sListen); WSACleanup(); PostQuitMessage(0); return 0; case WM_SOCKET: if (WSAGETSELECTERROR(lParam)) { closesocket(wParam); break; } switch (WSAGETSELECTEVENT(lParam)) { case FD_ACCEPT: // Accept a connection from client sClient = accept(wParam, (struct sockaddr *)&client, &iAddrSize); // Associate client socket with FD_READ and FD_CLOSE event WSAAsyncSelect(sClient, hwnd, WM_SOCKET, FD_READ | FD_CLOSE); break; case FD_READ: ret = recv(wParam, szMessage, MSGSIZE, 0); if (ret == 0 || ret == SOCKET_ERROR && WSAGetLastError() == WSAECONNRESET) { closesocket(wParam); } else { szMessage[ret] = ’\0’; send(wParam, szMessage, strlen(szMessage), 0); } break; case FD_CLOSE: closesocket(wParam); break; } return 0; } return DefWindowProc(hwnd, message, wParam, lParam); } WSAAsyncSelect是最简单的⼀种Winsock I/O模型(之所以说它简单是因为⼀个主线程就搞定了)。

IOCP编程之基本原理

IOCP编程之基本原理

IOCP编程之基本原理在我的博客之前写了很多关于IOCP的“行云流水”似的看了让人发狂的文章,尤其是几篇关于IOCP 加线程池文章,更是让一些功力不够深厚的初学IOCP者,有种吐血的感觉。

为了让大家能够立刻提升内力修为,并且迅速的掌握IOCP这个Windows平台上的乾坤大挪移心法,这次我决定给大家好好补补这个基础。

要想彻底征服IOCP,并应用好IOCP这个模型,首先就让我们穿越到遥远的计算机青铜器时代(以出现PC为标志),那时候普通的PC安装的还是DOS平台,微软公司主要靠这个操作系统在IT界的原始丛林中打拼,在DOS中编写程序,不得不与很多的硬件直接打交道,而最常操作的硬件无非是键盘、声显卡、硬盘等等,这些设备都有一个特点就是速度慢,当然是相对于PC平台核心CPU的速度而言,尤其是硬盘这个机械电子设备,其速度对于完全电子化得CPU来说简直是“相对静止”的设备。

很多时候CPU 可以干完n件(n>1000)事情的时间中,这些硬件可能还没有完成一件事情,显然让CPU和这些硬件同步工作将是一种严重的浪费,并且也不太可能,此时,聪明的硬件设计师们发明了一种叫做中断的操作方式,用以匹配这种速度上的严重差异。

中断工作的基本原理就是,CPU首先设置一个类似回调函数的入口地址,其次CPU对某个硬件发出一个指令,此时CPU就去干别的活计了,最后那个慢的象蜗牛一样的硬件执行完那个指令后,就通知CPU,让CPU暂时“中断”手头的工作,去调用那个“回调函数”。

至此一个完整的中断调用就结束了。

这个模型曾经解决了显卡与CPU不同步的问题,最重要的是解决了硬盘速度与CPU速度严重不匹配的问题,并因此还派生出了更有名的DMA(直接内存访问技术,主要是指慢速硬件可以读写原本只能由CPU直接读写的内存)硬盘IO方式。

(注意这里说的中断工作方式只是中断工作方式的一种,并不是全部,详细的中断原理请参阅其它专业文献。

)其实“中断”方式更像是一种管理模型,比如在一个公司中,如果要老板时时刻刻盯着员工作事情,那么除非是超人,否则无人能够胜任,同时对于老板这个稀缺资源来说也是一种极起严重的浪费。

windows网络结构图

windows网络结构图

Native API
调用NtCreateFile系统服务 系统服务函数 IO管理器 通过I/O管理器创建并发送IRP 驱动程序 READ_PORT_BUFFER_UCHAR(硬件抽象层提供的一组宏) 微内核 硬件抽象层 具体硬件
Windows NT总体架构—理解驱动 程序的工作过程
IP在首部中存入一个长度为8 bit的数据,称作协议域。
Windows NT总体架构
APP
APP 其他子系统
Win32子系统 Native API 系统服务函数
IO管理器 对象管理器 驱动程序 微内核 硬件抽象层 具体硬件
执行体组件 进程管理器 虚拟内存管理器 配置管理器 其他组件
Windows NT总体架构
APP
调用CreateFile API
Win32子系统 调用NtCreateFile NativeAPI
TCP
TCP 协议 路由器
TCP
IP
IP 协议
IP
IP 协议
IP
以太网驱动程序
以太网驱动程序
令牌环驱动程序
令牌环驱动程序
以太网
令牌环
通过TCP/IP和路由器连接的两台主机
知识点:
Windows网络结构体系与协议模型的对照
TCP/IP协议模型
Windows网络结构
Winsock应用程32.dll)
Winsock SPI(.DLL)
用户态 内核态
传输层
TDI(.VXD, .SYS)如TCPIP.SYS
网际层
网络接口层
NDIS (.VXD, .SYS)如TCPIP.SYS
网卡驱动(.VXD, .SYS)如ne2000.SYS .VXD—95/98 .SYS---NT/2K

基于winsock的阻塞和非阻塞通信模型

基于winsock的阻塞和非阻塞通信模型

摘要:在应用程序开发中,经常涉及各式各样的机器的交互通信问题。

在Windows操作系统下,可以使用MFC中的CSocket,也可以使用以Windows Api 为基础的Winsock等等。

本文主要描述了Winsock的两种实现方式,即阻塞方式和非阻塞方式。

并对应这两种方式,描述了Select模式和IOCP模式。

关键字:Winsock Blocking NonBlocking Select模式完成端口(IOCP)模式一、Winsock简介对于众多底层网络协议,Winsock是访问它们的首选接口。

而且在每个Win32平台上,Winsock都以不同的形式存在着。

Winsock是网络编程接口,而不是协议。

在Win32平台上,Winsock接口最终成为一个真正的“与协议无关”接口,尤其是在Winsock 2发布之后。

Win32平台提供的最有用的特征之一是能够同步支持多种不同的网络协议。

Windows重定向器保证将网络请求路由到恰当的协议和子系统;但是,有了Winsock,就可以编写可直接使用任何一种协议的网络应用程序了。

在广泛使用的windows平台下,winsock2被简单包装为一组庞大的Api库,通过WSA Start up加载的关于Winsock版本的信息,初始了winsock相关的dll 和lib,在成功调用了WSA Startup之后,即可设计自主的与通信有关的行为,当确认了执行完操作后,调用相应的WSA Cleanup,释放对winsock DLL的引用次数。

几乎所有在windows平台下可使用的通信框架都是由Winsock扩展而来的。

这里,之所以要再提windows下的winsock Api编程,并不多余,虽然也可以使用CSocket或ACE(ADAPTIVE Communication Environment)框架,但直接对较底层的本地操作系统API,会使我们更深的理解隐藏在框架下的实现,有时也可以解决一些实用问题。

完成端口模型

完成端口模型
际上,在一段较短的时间内,系统有可能超过这个值,但很快便会把它减少至事先在
CreateIoComletionPort函数中设定的值。那么,为何实际创建的工作者线程数量有时要比
CreateIoComletionPort函数设定的多一些呢?这样做有必要吗?如先前所述,这主要取决于
应用程序的总体设计情况。假定我们的某个工作者线程调用了一个函数,比如 Sleep或
return;
}
// Setup an I/O completion port.
if ((CompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0)) == NULL)
GetSystemInfo(&SystemInfo);
// Create worker threads based on the number of processors available on the
// system. Create two worker threads for each processor.
);
在我们深入探讨其中的各个参数之前,首先要注意该函数实际用于两个明显有别的目的:
■ 用于创建一个完成端口对象。
■ 将一个句柄同完成端口关联到一起。
最开始创建一个完成端口时,唯一感兴趣的参数便是 NumberOfConcurrentThreads(并发
线程的数量);前面三个参数都会被忽略。NumberOfConcurrentThreads参数的特殊之处在于,
都指定一个线程(处理器的数量有多少,便指定多少线程)以避免由于频繁的线程“场景”
交换活动,从而影响系统的整体性能。CreateIoComletionPort函数的NumberOfConcurrentThreads参数明确指示系统:在一个完成端口上,一

Winsock的异步模式探讨

Winsock的异步模式探讨

摘 要 : 讨论 了使用 W n o k实现异 步通信 的 IO模型 的特 点,通过分析 比较 ,总结 了各种 IO模 型的适用情况 ,为应 isc / / 用开发提供 了合理 的建议。 关 键 词 : 接 字: / 模 型: 步: 成 端 口 套 I 0 异 完 中图分类号 :T 9 91 N 1. 文献标识码 :A 文章编号 :1 7 — 7 2 (0 75 0 9 0 1 4 9 一 2 0 )— 1 - 3 6 5
点进行总结 ,有助于选择更适合 自己应用程 序的 I0 型。 /模
1 S lc 模型 ee t ’

e s le
S l c 模型是 W n ok中最常见 的 I0 eet i sc / 模型 。它的中心

思想是利用 Slc 函数 , eet 实现对 IO / 的管理, 判断套接字上 是否存在数据 ,或者 能否 向一个套接字写人数据 。

N L U L; IL ,N L ) I
W n o k 过异 步选择 函 ̄W A s nS lc 实现非阻塞 i sc 通 SAyceet
0 引言 套接字 ( ce) S kt 是一 种网络编程接 口。 o 它是对通信端点
的一种抽象 , 提供 了一种 发送和接收数据 的机 制。W n o s i dw
S lc函数具体用法 : eet 如果程序要检查 套接 字上是否有 数 据到来, 则首先需要把套 接字句柄 加入可读性 监视集合
Ab t c: h r i l ic s e h h r c e f W n o k i0 m d s n o i p e e t n s n h o o s sr tT e a t c e d s u s s t e c a a t r o i s c / o e u {g f r m lm n i g A y c r n u a c m u ia i n n ya a y ig a d cm a i g u s u h r p r s t a i n f i o m d h c s a u i g o m n c t o ,a d b n 1 z n n o p r n ,s m p t e p o e i u t o s o / o e w j h i s n

Winsock编程的五种模型

Winsock编程的五种模型

Winsock编程的五种模型上面介绍的仅仅是最简单的winsock通讯的方法,而实际中很多网络通讯的却很多难以解决的意外情况.例如,Winsock提供了两种套接字模式:锁定和非锁定.当我们使用锁定套接字的时候,我们使用的很多函数,例如accpet,send,recv等等,如果没有数据需要处理,这些函数都不会返回,也就是说,你的应用程序会阻塞在那些函数的调用处.而如果使用非阻塞模式,调用这些函数,不管你有没有数据到达,他都会返回,所以,有可能我们在非阻塞模式里,调用这些函数大部分的情况下会返回失败,所以就需要我们来处理很多的意外出错.这显然不是我们想要看到的情况.我们可以采用Winsock的通讯模型来避免这些情况的发生。

Winsock提供了五种套接字I/O模型来解决这些问题.他们分别是select(选择),WSAAsyncSelect(异步选择),WSAEventSelect (事件选择), overlapped(重叠) , completionport(完成端口) .我们在这里详细介绍一下select,WSAASyncSelect两种模型.select模型是最常见的I/O模型.使用int select( int nfds , fd_set FAR* readfds , fd_set FAR* writefds , fd_set FAR* exceptfds ,const struct timeval FAR * timeout ) ;函数来检查你要调用的socket套接字是否已经有了需要处理的数据.select包含三个socket队列,分别代表:readfds ,检查可读性,writefds,检查可写性,exceptfds,例外数据.timeout是select函数的返回时间.例如,我们想要检查一个套接字是否有数据需要接收,我们可以把套接字句柄加入可读性检查队列中,然后调用select,如果,该套接字没有数据需要接收,select函数会把该套接字从可读性检查队列中删除掉,所以我们只要检查该套接字句柄是否还存在于可读性队列中,就可以知道到底有没有数据需要接收了.Winsock提供了一些宏用来操作套接字队列fd_set.FD_CLR( s,*set) 从队列set删除句柄s.FD_ISSET( s, *set) 检查句柄s是否存在与队列set中.FD_SET( s,*set )把句柄s添加到队列set中.FD_ZERO( *set ) 把set队列初始化成空队列.WSAAsyncSelect(异步选择)模型:WSAASyncSelect模型就是把一个窗口和套接字句柄建立起连接,套接字的网络事件发生时时候,就会把某个消息发送到窗口,然后可以在窗口的消息响应函数中处理数据的接收和发送.int WSAAsyncSelect( SOCKET s, HWND hWnd , unsigned int wMsg , long lEvent ) ; 这个函数可以把套接字句柄和窗口建立起连接,wMsg 是我们必须自定义的一个消息.lEvent就是制定的网络事件.包括FD_READ , FD_WRITE , FD_ACCEPT, FD_CONNECT , FD_CLOSE .几个事件.例如,我需要接收FD_READ , FD_WRITE , FD_CLOSE 的网络事件.可以调用WSAAsyncSelect( s , hWnd , WM_SOCKET , FD_READ | FD_WRITE | FD_CLOSE ) ;这样,当有FD_READ , FD_WRITE 或者FD_CLOSE网络事件时,窗口hWnd将会收到WM_SOCKET消息,消息参数的lParam标志了是什么事件发生.其实大家应该见过这个模型,因为MFC的CSocket类,就是使用这个模型.。

iocp 编程

iocp 编程

IOCP编程什么是IOCPIOCP(Input/Output Completion Ports)是一种高效的异步I/O模型,它在Windows操作系统中提供了对网络编程的支持。

通过使用IOCP,我们可以实现高性能、可伸缩性强的网络应用程序。

在传统的同步I/O模型中,当一个线程在等待数据时,它会被阻塞,直到数据到达。

而在异步I/O模型中,线程不会被阻塞,它可以继续执行其他任务。

IOCP就是基于这种异步I/O模型实现的。

IOCP的工作原理使用IOCP进行编程主要涉及以下几个核心概念:端口(Port)、完成包(Completion Packet)、套接字(Socket)和重叠操作(Overlapped Operation)。

•端口:一个端口代表一个I/O设备或者一个文件。

每个端口都有一个关联的完成端口。

•完成包:完成包是指一个I/O操作完成时所生成的信息块。

它包含了完成的状态、相关参数和返回值等信息。

•套接字:套接字是网络编程中用于进行通信的抽象概念。

•重叠操作:重叠操作是指一次I/O操作请求,在请求发出之后,线程就可以继续执行其他任务了。

IOCP主要通过以下几个步骤来实现异步I/O:1.创建一个完成端口(Completion Port)。

2.创建一个或多个工作者线程(Worker Thread),这些线程用于处理I/O操作。

3.将套接字关联到完成端口上,使得该套接字上的I/O操作能够被异步处理。

4.当有I/O操作完成时,系统会将相关的完成包放入完成队列中。

5.工作者线程从完成队列中获取完成包,并进行相应的处理。

IOCP的优势和适用场景相比于传统的同步阻塞模型,IOCP具有以下几个优势:1.高性能:IOCP能够充分利用CPU资源,提高程序的并发处理能力。

它通过异步I/O模型,使得线程在等待数据时不被阻塞,可以继续执行其他任务,从而充分利用了CPU资源。

2.可伸缩性:IOCP可以轻松地扩展到支持大量的并发连接。

理解I O C P(完成端口)

理解I O C P(完成端口)

理解I/O Completion Port(完成端口)欢迎阅读此篇IOCP教程。

我将先给出IOCP的定义然后给出它的实现方法,最后剖析一个Echo 程序来为您拨开IOCP的谜云,除去你心中对IOCP的烦恼。

OK,但我不能保证你明白IOCP 的一切,但我会尽我最大的努力。

以下是我会在这篇文章中提到的相关技术:I/O端口同步/异步堵塞/非堵塞服务端/客户端多线程程序设计Winsock API 2.0在这之前,我曾经开发过一个项目,其中一块需要网络支持,当时还考虑到了代码的可移植性,只要使用select,connect,accept,listen,send还有recv,再加上几个#ifdef的封装以用来处理Winsock和BSD套接字[socket]中间的不兼容性,一个网络子系统只用了几个小时很少的代码就写出来了,至今还让我很回味。

那以后很长时间也就没再碰了。

前些日子,我们策划做一个网络游戏,我主动承担下网络这一块,想想这还不是小case,心里偷着乐啊。

网络游戏好啊,网络游戏为成百上千的玩家提供了乐趣和令人着秘的游戏体验,他们在线上互相战斗或是加入队伍去战胜共同的敌人。

我信心满满的准备开写我的网络,于是乎,发现过去的阻塞同步模式模式根本不能拿到一个巨量多玩家[MMP]的架构中去,直接被否定掉了。

于是乎,就有了IOCP,如果能过很轻易而举的搞掂IOCP,也就不会有这篇教程了。

下面请诸位跟随我进入正题。

什么是IOCP?先让我们看看对IOCP的评价I/O完成端口可能是Win32提供的最复杂的内核对象。

[Advanced Windows 3rd] Jeffrey Richter这是[IOCP]实现高容量网络服务器的最佳方法。

[Windows Sockets2.0:Write Scalable Winsock Apps Using Completion Ports]Microsoft Corporation完成端口模型提供了最好的伸缩性。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

winsock IO模型如果你想在Windows平台上构建服务器应用,那么I/O模型是你必须考虑的。

Windows操作系统提供了1 选择(Select)2异步选择(WSAAsyncSelect)3事件选择(WSAEventSelect)4重叠I/O(Overlapped I/O)5完成端口(Completion Port)共五种I/O模型每一种模型均适用于一种特定的应用场景。

程序员应该对自己的应用需求非常明确,而且综合考虑到程序的扩展性和可移植性等因素,作出自己的选择。

我会以一个回应反射式服务器来介绍这五种I/O模型我们假设客户端的代码如下(为代码直观,省去所有错误检查,以下同):#include <WINSOCK2.H>#include <stdio.h>#define SERVER_ADDRESS "127.0.0.1" //服务端地址#define PORT 5200 //服务端所开放的端口#define MSGSIZE 1024#pragma comment(lib, "ws2_32.lib")int main(){WSADATA wsaData;SOCKET sClient;SOCKADDR_IN server;char szMessage[MSGSIZE];int ret;// 初始化windows socket库WSAStartup(0x0202, &wsaData);// 创建客户端socketsClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);// 连接到服务端memset(&server, 0, sizeof(SOCKADDR_IN));server.sin_family = AF_INET;server.sin_addr.S_un.S_addr = inet_addr(SERVER_ADDRESS);server.sin_port = htons(PORT);connect(sClient, (struct sockaddr *)&server, sizeof(SOCKADDR_IN));while (TRUE){printf("Send:");gets(szMessage);// 发送消息send(sClient, szMessage, strlen(szMessage), 0);// 接收消息ret = recv(sClient, szMessage, MSGSIZE, 0);szMessage[ret] = '\0';printf("Received [%d bytes]: '%s'\n", ret, szMessage);}// 退出前的清理closesocket(sClient);WSACleanup();return 0;}客户端所做的事情相当简单,创建套接字,连接服务器,然后不停的发送和接收数据。

比较容易想到的一种服务器模型就是采用一个主线程,负责监听客户端的连接请求,当接收到某个客户端的连接请求后,创建一个专门用于和该客户端通信的套接字和一个辅助线程。

以后该客户端和服务器的交互都在这个辅助线程内完成这种方法比较直观,程序非常简单而且可移植性好,但是不能利用平台相关的特性例如,如果连接数增多的时候(成千上万的连接),那么线程数成倍增长,操作系统忙于频繁的线程间切换,而且大部分线程在其生命周期内都是处于非活动状态的,这大大浪费了系统的资源所以,如果你已经知道你的代码只会运行在Windows平台上,建议采用Winsock I/O模型。

========================================================一、选择模型Select(选择)模型是Winsock中最常见的I/O模型,之所以称其为Select模型,是由于它的中心思想便是利用select函数实现对I/O的管理。

最初设计该模型时,主要面向的是某些使用UNIX操作系统的计算机,它们采用的是Berkeley套接字方案。

Select模型已集成到Winsock 1.1中,它使那些想避免在套接字调用过程中被无辜锁定的应用程序,采取一种有序的方式,同时进行对多个套接字的管理。

由于Winsock 1.1向后兼容于Berkeley套接字实施方案,所以假如有一个Berkeley套接字应用使用了select函数,那么从理论角度讲,毋需对其进行任何修改,便可正常运行。

下面的这段程序就是利用选择模型实现的Echo服务器的代码(已经不能再精简了):#include <winsock.h>#include <stdio.h>#define PORT 5200#define MSGSIZE 1024#pragma comment(lib, "ws2_32.lib")int g_iTotalConn = 0; //global int total connectionSOCKET g_CliSocketArr[FD_SETSIZE]; //global client socket arrayDWORD WINAPI WorkerThread(LPVOID lpParameter);int main(){WSADATA wsaData;SOCKET sListen, sClient;SOCKADDR_IN local, client;int iaddrSize = sizeof(SOCKADDR_IN);DWORD dwThreadId;// 初始化windows socket库WSAStartup(0x0202, &wsaData);// 创建监听socketsListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);// 绑定local.sin_addr.S_un.S_addr = htonl(INADDR_ANY);local.sin_family = AF_INET;local.sin_port = htons(PORT);bind(sListen, (struct sockaddr *)&local, sizeof(SOCKADDR_IN));// 监听listen(sListen, 3);// 创建工作者线程CreateThread(NULL, 0, WorkerThread, NULL, 0, &dwThreadId);while (TRUE){// 接受一个连接sClient = accept(sListen, (struct sockaddr *)&client, &iaddrSize);printf("Accepted client:%s:%d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));// Add socket to fdTotalg_CliSocketArr[g_iTotalConn++] = sClient;}return 0;}DWORD WINAPI WorkerThread(LPVOID lpParam){int i;fd_set fdread;int ret;struct timeval tv = {1, 0};char szMessage[MSGSIZE];while (TRUE){FD_ZERO(&fdread);//将fdread初始化成空集合for (i = 0; i < g_iTotalConn; i++){FD_SET(g_CliSocketArr[i], &fdread); //将套接字加入集合fdread }// 我们只在意可读性ret = select(0, &fdread, NULL, NULL, &tv);if (ret == 0){// 超时或没有可读socketcontinue;}for (i = 0; i < g_iTotalConn; i++){if (FD_ISSET(g_CliSocketArr[i], &fdread)){// 该socket可读ret = recv(g_CliSocketArr[i], szMessage, MSGSIZE, 0);if (ret == 0 || (ret == SOCKET_ERROR && WSAGetLastError() == WSAECONNRESET)){// 客户端socket关闭了printf("Client socket %d closed.\n", g_CliSocketArr[i]);closesocket(g_CliSocketArr[i]);if (i < g_iTotalConn - 1){g_CliSocketArr[i--] = g_CliSocketArr[--g_iTotalConn];}}else{// 从客户端接受到信息了szMessage[ret] = '\0';send(g_CliSocketArr[i], szMessage, strlen(szMessage), 0);}}}}return 0;}服务器的几个主要动作如下:1.创建监听套接字,绑定,监听;2.创建工作者线程;3.创建一个套接字数组,用来存放当前所有活动的客户端套接字,每accept一个连接就更新一次数组;4.接受客户端的连接这里有一点需要注意的,就是我没有重新定义FD_SETSIZE宏,所以服务器最多支持的并发连接数为64。

而且,这里决不能无条件的accept,服务器应该根据当前的连接数来决定是否接受来自某个客户端的连接。

一种比较好的实现方案就是采用WSAAccept函数,而且让WSAAccept回调自己实现的Condition Function如下所示:int CALLBACK ConditionFunc(LPWSABUF lpCallerId,LPWSABUF lpCallerData, LPQOS lpSQOS,LPQOS lpGQOS,LPWSABUF lpCalleeId, LPWSABUF lpCalleeData,GROUP FAR * g,DWORD dwCallbackData){if (当前连接数< FD_SETSIZE)return CF_ACCEPT;elsereturn CF_REJECT;}工作者线程里面是一个死循环,一次循环完成的动作是:1.将当前所有的客户端套接字加入到读集fdread中;2.调用select函数;3.查看某个套接字是否仍然处于读集中,如果是,则接收数据如果接收的数据长度为0,或者发生WSAECONNRESET错误,则表示客户端套接字主动关闭,这时需要将服务器中对应的套接字所绑定的资源释放掉,然后调整我们的套接字数组(将数组中最后一个套接字挪到当前的位置上)除了需要有条件接受客户端的连接外,还需要在连接数为0的情形下做特殊处理,因为如果读集中没有任何套接字,select函数会立刻返回,这将导致工作者线程成为一个毫无停顿的死循环,CPU的占用率马上达到100%二、异步选择Winsock提供了一个有用的异步I/O模型。

相关文档
最新文档