160 likes | 298 Views
异步 IO. 计算机网络实验课. 两种模式. 阻塞模式 非阻塞模式. 阻塞模式. 套接字创建时,默认工作在阻塞模式下。例如对于 recv 函数的调用会使程序进入等待状态,直到接收到数据才返回 使用阻塞模式时,需要处理多个套接字连接时,就必须创建多个线程,即一个连接使用一个线程 One thread per client 阻塞模式为什么一定是 One thread per client ? 考虑一个线程正在等待接收数据,却有别的客户在申请连接。。。. One thread per client. 优点
E N D
异步IO 计算机网络实验课
两种模式 • 阻塞模式 • 非阻塞模式
阻塞模式 • 套接字创建时,默认工作在阻塞模式下。例如对于recv函数的调用会使程序进入等待状态,直到接收到数据才返回 • 使用阻塞模式时,需要处理多个套接字连接时,就必须创建多个线程,即一个连接使用一个线程 • One thread per client • 阻塞模式为什么一定是One thread per client? • 考虑一个线程正在等待接收数据,却有别的客户在申请连接。。。
One thread per client • 优点 • 简单,并且在用户数量为a few tens的时候,是响应速度最快的一种模型 • 缺点 • 扩展性不好 1)每一个线程都要消耗系统资源 2)系统在线程间切换需要花费时间 • 当more than a few tens的线程同时运行时,因素2)首先影响系统性能,线程进一步增加时,因素1)的作用也会越来越明显
Thread的理论值 • 从资料来看,每一个线程默认分配的堆栈是1M • 每一个用户进程最大的可用地址空间是2G • 所以理论上默认的线程数量是2000 • 限制堆栈大小之后,例如4K,出现了另外一个地址空间分配粒度的问题,最小为64K,所以最多线程数约13000. 注意到还有一些系统DLL等需要占用内存
阻塞模式小结 • One thread per client • 主要问题是系统性能受影响 • 线程的数量限制不是主要因素 • 为了开发高效的服务器程序,我们需要使用非阻塞模式
非阻塞模式 • 非阻塞模式下,执行I/O的函数调用send和recv会马上返回 • 大多数情况下,调用后返回的是出错代码 WSAWOULDBLOCK,该代码意味着请求的操作在调用期间没有完成 • 例如,系统输入缓存中没有待处理的数据,那么执行recv会返回WSAWOULDBLOCK
非阻塞模式 • 关键的问题在于确定套接字什么时候可读可写,或者说确定套接口上的可读可写网络事件何时发生。。。 • 可以使用Winsock提供的各种不同的I/O模型
非阻塞小结 • 结合对One thread per client的讨论,我们看到非阻塞模式主要解决的问题在于 • 如何在使用尽量少的线程的条件下,接受尽可能多的并发用户连接。。。
异步I/O——Select-1 • 使用了一个数据结构 typedef struct fd_set { u_int fd_count; SOCKET fd_array[FD_SETSIZE]; } fd_set; • 其中fd_count指名了fd_array中有多少套接字;fd_array是存放套接字的数组; • 有几个相关宏 • FD_ZERO( *set); FD_CLR(s, *set); • FD_ISSET(s, *set); FD_SET(s, *set); • 需要主动把套接字设置为非阻塞模式 • Ioctlsocket();
异步I/O——Select-2 • int select ( int nfds, //忽略,为了兼容 fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timeval *timeout ); • readfds: 指向套接字集合,检查其中套接字的可读性。相应writefds为检查可写性。最后的exceptfds用于查错。 • Timeout参数设置为0表示无限等待,直到有网络事件发生
异步I/O——Select-3 • 用法示例 • 假设已经生成了socket sListen; • 声明套接字集合 fd_set fdSoket; • 初始化该集合 FD_ZERO(&fdSoket); • 放入第一个套接字 FD_SET(sListen, &fdSoket) • 声明用于Select函数的套接字集合 • fdRead= fdSoket; • 使用Select函数 • select(0, &fdRead, NULL, NULL, NULL);
异步I/O——Select-4 • 当select函数返回时,意味着发生了某些网络事件 • 我们传递给select函数不同的套接字集合时,select返回后意味着发生了不同的网络事件 • 如果我们传递了readfds指针,则有新的连接请求、数据可读、连接关闭/重启/中断等事件时返回; • 如果我们传递了writefds指针,则连接成功(调用connect)、数据可写的时候返回; • 如果我们传递了exceptfds指针,则当连接失败(调用connect) 、OOB数据可读时返回;
异步I/O——Select-5 • 操作步骤 • 初始化套接字集合fdSocket,向该集合添加监听套接字 • 将fdScoket集合的拷贝传递给select函数,当有事件发生时,select函数移除fdRead集合中没有连接事件发生的套接字句柄,返回 • 比较fdSocket与select处理过的fdRead集合,确定哪些套接字有连接,并进一步处理I/O • 返回第2步继续
异步I/O——Select-6 • Fd_set结构对套接字数量有限制,winsock.h中定义为64;自定义之后不能超过1024 • 因为select需要遍历套接字来决定状态,返回后也需要遍历,所以套接字数量太大性能也会受到影响。
异步I/O——WSAAsynSelect • Windows 系统专用 • 自动把套接字设置为非阻塞模式 • 为一个套接字绑定到一个窗口句柄 • 先绑定,后listen,之后进入消息循环,处理listen端口上发生的网络事件 • 每一次一个socket上发生某个事件,会触发一次消息发送,执行一次消息处理函数 • 单个Windows函数处理上千用户请求时服务器性能也会受影响