socket多路复用的客户/服务器模型
来源:互联网 发布:我国科研成果数据 编辑:程序博客网 时间:2024/05/16 07:04
前面一篇介绍了服务器端使用多线程的方式来处理多个客户端的请求的,但是当客户端数量增多时线程数量会急剧增加,导致消耗大量的资源。
于是就引出了服务器端的一种新的模型。
1. 阻塞与非阻塞
首先介绍几个基本的概念。
阻塞方式( block ),顾名思义,就是进程或是线程执行到这些函数时必须等待某个事件的发生,假如事件没有发生,进程或线程就被阻塞,函数不能立即返回。
非阻塞方式( non-block ),就是进程或线程执行此函数时不必非要等待事件的发生,一旦执行肯定返回,以返回值的不同来反映函数的执行情况,假如事件发生则和阻塞方式相同,若事件没有发生则返回一个代码来告知事件未发生,而进程或线程继续执行,所以效率较高。
套接字相关函数默认时采用阻塞方式操作,而大多数情况下,程序不仅仅只拥有一个套接字。当进程以阻塞方式操作其中之一时,必将导致不能使用其他的套接字。如果希望这些套接字同时工作,就必须设计并发的套接字程序,即在一个套接字读写的同时保证另一个套接字也能正常地操作。
多路复用函数 select 把一些文件描述符集合在一起,如果某个文件描述符的状态发生变化,比如进入“写就绪”或者“读就绪”状态,函数 select 会立即返回,并且通知进程读取或写入数据;如果没有 I/O 到达,进程将进入阻塞,直到函数 select 超时退出为止。
套接字也是文件,函数 select 也支持套接字描述符,并且可以应用在 TCP 协议和 UDP 协议的套接字中。利用多路复用,进程就可以同时监控多个套接字信息,在多个套接字上并发地执行操作。
2.select介绍
select的大概思想:将多个套接字放在一个集合里,然后统一检查这些套接字的状态(可读、可写、异常等),调用select后,会更新这些套接字的状态,然后做判断,如果套接字可读,就执行read操作。这样就巧妙地避免了阻塞,达到同时处理多个连接的目的。当然如果没有事件发生,select会一直阻塞,如果不想一直让它等待,想去处理其它事情,可以设置一个最大的等待时间。
上面有两个结构体。
第一, struct timeval 代表时间值
第二, struct fd_set 能够理解为文件描述符的集合
fd_set 集合能够通过一些宏由人为来操作,比如清空集合 FD_ZERO(fd_set *) ,将一个给定的文件描述符加入集合之中 FD_SET(int ,fd_set*) ,将一个给定的文件描述符从集合中删除 FD_CLR(int,fd_set*),检查集合中指定的文件描述符是否能够读写 FD_ISSET(int ,fd_set* ) 。
3.操作fd_set的宏
下面是这些宏的详细描述:
- FD_ZERO(fd_set * set),是把集合清空(初始化为0,确切的说,是把集合中的元素个数初始化为0,并不修改描述符数组).使用集合前,必须用FD_ZERO初始化,否则集合在栈上作为自动变量分配时,fd_set分配的将是随机值,导致不可预测的问题。
- FD_SET(int s,fd_set * set),向集合中加入一个套接口描述符(如果该套接口描述符s没在集合中,并且数组中已经设置的个数小于最大个数时,就把该描述符加入到集合中,集合元素个数加1)。这里是将s的值直接放入数组中。
- FD_ISSET(int s,fd_set * set),检查描述符是否在集合中,如果在集合中返回非0值,否则返回0. 它的宏定义并没有给出具体实现,但实现的思路很简单,就是搜索集合,判断套接字s是否在数组中。
- FD_CLR(int s,fd_set * set),从集合中移出一个套接口描述符(比如一个套接字连接中断后,就应该移除它)。实现思路是,在数组集合中找到对应的描述符,然后把后面的描述依次前移一个位置,最后把描述符的个数减1。
下面是使用这些宏的基本方式。
- 调用FD_ZERO来初始化fd_set;
- 调用FD_SET将感兴趣的套接字描述符加入fd_set集合中(每次循环都要重新加入,因为select更新后,会将一些没有满足条件的套接字移除队列);
- 设置等待时间后,调用select函数--更新套接字的状态;
- 调用FD_ISSET,来判断套接字是否有相应状态,然后做相应操作,比如,如果套接字可读,就调用recv函数去接收数据。
4.自定义管理套接字的集合类SocketList
从上面可以看出,我们需要维护一个套接字集合,这个套接字的集合里存放的是和服务器建立连接的套接字,服务器每次循环调用select()时首先需要使用FD_ZERO宏来初始化fd_set对象,然后调用FD_SET将我们维护的这个套接字集合中的套接字加入fd_set这个集合中,然后就可以调用select函数了,调用完select函数后,再次遍历我们维护的这个套接字集合,通过FD_ISSET宏来判断这个套接字是否是我们需要进行处理的,如果是需要进行处理的,那么就对它进行处理。
所以现在这里的关键就是要维护一个套接字的集合,我把它定义为SocketList这个类,下面是我实现的C++类。这个套接字的集合类可以很方便地实现下面的功能:
- 添加socket
- 删除socket
- 将前类中的所有感兴趣的socket加入到fd_set中
5.服务器端的实现
仍然是在原来服务端的第6步进行更改,但是在这之前启动了一个新的线程workThread,并且服务端也只需要启动一个这样的线程用来访问SocketList的对象。此时第6步就只需要将当前建立的socket添加到SocketList这个对象中。在workThread这个线程中访问SocketList的对象,在这个线程中调用select()函数即可。
服务端代码:
客户端的代码和前面是一样的,这里就不再列出来了。
程序执行的效果:
上面服务器端显示selecet() is time-out!是指超过了你设置的传递给select的timeout时间后仍然没有socket发送数据过来,此时select会返回,但是可以从这个返回的状态码中判断是否有socket可读。
- socket多路复用的客户/服务器模型
- socket通信之五:select多路复用的客户/服务器模型
- Socket学习之select多路复用的客户/服务器模型
- socket通信之四:多线程版本的客户/服务器模型
- select的socket server多路复用模型
- socket通信之六:Overlapped I/O 事件通知模型实现的客户/服务器模型
- socket通信之七:Overlapped I/O 完成例程模型实现的客户/服务器模型
- socket通信之八:完成端口模型实现的客户/服务器模型
- linux socket多路复用服务器
- 多路复用服务器网络模型
- 多路复用 循环服务器模型
- IO多路复用服务器模型
- 服务器IO多路复用模型
- socket通信之二:实现一个基本的客户/服务器模型
- [C++]C++的客户/服务器模型
- 客户-服务器模型
- TCP客户/服务器模型
- 多路复用的server模型
- Android BaiduAi 人脸识别 集成教学 实战 亲测可用
- 自定义TextView/EditText,实现显示(或输入)小数、数字前缀(¥$...)、千分符、最大值等
- Python TCP服务器
- 一些三维计算几何算法
- React extracting components
- socket多路复用的客户/服务器模型
- 用一个栈实现另一个栈的排序(每日一道算法题)
- AjaxUpload实现文件上传+LibreOffice文件转码+flexPaper文件在线预览
- 图像生成与图像模型——光源、阴影与影调
- 【转】我和Linux
- MQ、JMS以及ActiveMQ
- mysql数据库向redis导入的一些常见问题
- linux 下 启动tomcat时 提示找不到catalina.sh
- 欢迎使用CSDN-markdown编辑器