网络基础 select 模型

(一)select简介

select模型为五种IO模型中的一种(I/O多路复用模型)。该模型的函数包括select、poll、epoll等函数。这个函数能够允许指示内核等待多个事件中的任意一个发生,并且仅仅在一个或者多个事件发生(或者经过指定的时间后)后才唤醒进程。select模型也是同步I/O模型,都需要在读写事件就绪后(此过程select函数阻塞),用户自己负责将数据从内核拷贝到用户进程空间(通过recv系统调用)。关于I/O模型的同步异步问题,前一篇总结已作描述。

(二)select优缺点

select优点:
     (1) 它能够同时监视多个文件描述符(以下简称fd),当select返回后,数组中就绪的fd中标志位会被修改,用户进程需要判断哪个fd的标志位修改了,从而进行后续的读写操作。
     (2) select良好的跨平台性,几乎支持所有的平台。

select缺点:
     (1) 单个进程所监视的最大数量的文件描述符为1024个(采用32个整数的32位标识),在linux下可以修改宏定义修改这个限制。
     (2) 每调用一次select,都需要遍历一次所有fd。随着fd的增加,调用开销将线性增加。且在调用select的时候,都需要将fd集合从用户态拷贝到内核态,这个过程也是随着fd,调用开销也是线性增长的。

(三)select函数

select函数原型如下:
     int select( int nfds, fd_set FAR * readfds, fd_set FAR * writefds, fd_set FAR *exceptfds, const struct timeval FAR * timeout );
     其中,select包含了三个socket队列,分别为:readfds 检查可读性; writefds 检查可写性;exceptfds 检查异常。 timeout是select函数 返回的时间。
     其中,fd_set为一个结构体:
    typedef struct fd_set
    {
    u_int fd_count; /* how many are SET? */
    SOCKET fd_array[FD_SETSIZE]; /* an array of SOCKETs */
    } fd_set;
    fd_set即为描述字集合,它是一个整数数组,每个数的每一位对应一个描述字。如果FD_SETSIZE为32,则数组中第一个整数元素对应描述字0~31,数组中第二个整数元素对应描述字32~63,以此类推。
    其中,timeout告诉内核等待一组fd中任一个准备好需要花多少时间,结构timeval指定了秒级和微妙级。
struct timeval{
long tv_sec;
long tv_usec;

对于时间的用法,有三种可能(这三种可能同样适用于其他事件等待机制的系统调用函数,pthread_cond_timedwait, WaitForSingleObject 等)

1. 永远等待下去,timeout设为NULL。仅有一个fd准备好I/O时才返回;
2. 等待固定事件,指定timeout中的秒数和微妙数。
3. 根本不等待,timeout设为0。检查fd后立即返回,这成为轮询。
     举例来说,如果需要监听一个fd是否有数据需要接收,则开始可以将该fd加入readfds中,然后调用select。此时该线程会阻塞在select处,直到有数据到来或者等待超时(由timeout参数决定)。
    当select在监听多个fd的读事件时,有数据到来,那如何判断是哪一个fd对应的数据? 当有数据到来时,select会将readfds中没有数据需要接受的fd从可读性检查队列中删除掉,所以只要检测每个fd是否还存在于readfds中,就能知道到底有没有需要接收的数据了。
    对于fd_set操作的宏,表示如下:
FD_CLR( s,*set) 从队列set删除描述符s。
FD_ISSET( s, *set) 检查描述符s是否存在与队列set中。
FD_SET( s,*set )把描述符s添加到队列set中。
FD_ZERO( *set ) 把set队列初始化成空队列。

(四)基础服务器模型

一种较为容易的服务器模型就是,一个线程负责监听客户端的连接请求。当收到客户端的连接请求后,再创建另一个工作线程,用于处理服务器与客户端的交互信息。该方法能够适用连接数目不多的情况,一旦连接数量增加,则相应线程的开销也会增加(频繁创建、销毁线程,线程间的频繁切换等)。而且大部分时候,线程都是处于非活动状态的,这也在无端消耗着CPU。 大体流程如下图所示:

#include <winsock.h>
#include <stdio.h>
#define PORT 5150
#define MSGSIZE 1024
#pragma comment(lib, "ws2_32.lib")
int g_iTotalConn = 0;
SOCKET g_CliSocketArr[FD_SETSIZE];
DWORD WINAPI WorkerThread(LPVOID lpParameter);
int main()
{WSADATA wsaData;SOCKET sListen, sClient;SOCKADDR_IN local, client;int iaddrSize = sizeof(SOCKADDR_IN);DWORD dwThreadId;// Initialize Windows socket libraryWSAStartup(0x0202, &wsaData);// Create listening socketsListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);// Bindlocal.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));// Listenlisten(sListen, 3);// Create worker threadHANDLE hHandle = CreateThread(NULL, 0, WorkerThread, NULL, 0, &dwThreadId);CloseHandle(hHandle);while (TRUE){// Accept a connectionsClient = 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);for (i = 0; i < g_iTotalConn; i++){FD_SET(g_CliSocketArr[i], &fdread);}// We only care read eventret = select(0, &fdread, NULL, NULL, &tv);if (ret == 0){// Time expiredcontinue;}for (i = 0; i < g_iTotalConn; i++){if (FD_ISSET(g_CliSocketArr[i], &fdread)){// A read event happened on pfdTotal->fd_array[i]ret = recv(g_CliSocketArr[i], szMessage, MSGSIZE, 0);if (ret == 0 || (ret == SOCKET_ERROR && WSAGetLastError() == WSAECONNRESET)){// Client socket closedprintf("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{ // We received a message from clientszMessage[ret] = '\0';send(g_CliSocketArr[i], szMessage, strlen(szMessage), 0);}}}}return 0;
}
当然,不仅仅是上面一种服务器的构建方式,还有其他的方式,比如开启一个守护线程不断监测所有套接字的读事件(不论是服务器的侦听套接字,还是与服务器建立的连接套接字),并分别做不同处理(对于侦听套接字有读数据,表示有新的连接到来;对于连接套接字有读数据,表示连接上有数据交互了)。该方式的特点是线程个数固定(就一个服务器处理线程处理跟所有客户端的信令交互),但是这同样也是缺点,对于连接数很多的情况,单个线程处理所有客户端的请求显然无法满足实时性要求较高的环境。该服务器设计的大体框架如下图所示:
  

连接套接字管理模块主要作用:
1.  管理连接后的客户端(主要对客户端连接套接字进行管理),以便与各个客户端通信;
2.  对TCP消息进行封装,TCP为流式套接字,可以通过该管理模块,使得客户端与服务器处理线程的消息一致(收发的消息内容、消息长度相同)。 

参考阅读:
IO多路复用之select总结:http://www.cnblogs.com/Anker/archive/2013/08/14/3258674.html
select函数实现原理分析:http://linux.chinaunix.net/techdoc/net/2009/05/03/1109887.shtml
winsock IO模型 select模型:http://www.cnblogs.com/azraelly/archive/2012/08/11/2633685.html

网络基础 select模型相关推荐

  1. 网络基础——OSI模型

    目录 一.物理层 二.数据链路层 三.传输层 四.网络层 五.会话层 六.表示层 七.应用层 详细了解它们之前需要牢记的是:下层协议是为上层服务的. 一.物理层 传输方式:         bit(比 ...

  2. 糖儿飞教你学C++ Socket网络编程——28. 使用select模型实现一对多通信

    在项目10中,采用多线程技术实现了TCP协议的一对多通信,但如果客户端过多,就会导致服务器端的线程数量膨胀,使得服务器的资源占用过大.能不能让TCP程序在一个线程中同时与多个客户端进行通信呢?答案是可 ...

  3. linux下多路复用模型之Select模型

    Linux关于并发网络分为Apache模型(Process per Connection (进程连接) ) 和TPC , 还有select模型,以及poll模型(一般是Epoll模型) Select模 ...

  4. WinSock I/O 模型 -- Select 模型

    简介 Select 模型是 WinSock 中最常见的 I/O 模型,这篇文章我们就来看看如何使用 Select api 来实现一个简单的 TCP 服务器. API 基础 Select 模型依赖 Wi ...

  5. 从网线到IO模型(网络基础整合)

    文章目录 网络基础 1.什么是协议,常见协议 2.网络应用设计模式 BS/CS优缺点 什么是C/S模式 什么是B/S模式 优缺点 3.分层模型(物数网传会表应) 4.以太网帧协议(为什么需要+报文分析 ...

  6. 朴素、Select、Poll和Epoll网络编程模型实现和分析——Select模型

    在<朴素.Select.Poll和Epoll网络编程模型实现和分析--朴素模型>中我们分析了朴素模型的一个缺陷--一次只能处理一个连接.本文介绍的Select模型则可以解决这个问题.(转载 ...

  7. 【Linux网络编程】并发服务器之select模型

    00. 目录 文章目录 00. 目录 01. 概述 02. I/O复用技术概述 03. select模型服务器实现思路 04. select模型服务器实现 05. 附录 01. 概述 服务器设计技术有 ...

  8. 使用socket实现基于select模型的网络聊天室

    假期闲来无事,便写了个小小的网络聊天室程序.以前一直都是写MFC的有界面的程序,时间长了,便失去了兴趣,感觉有些东西经过MFC的封装,反而失去了意义,让人学不到东西,所以,丢开MFC那层的东西,直接使 ...

  9. 网络基础知识 | 协议 | TCP/IP分层模型

    目录 网络基础知识 网络互联 互联网基础知识 网络的构成要素 网卡 中继器 网桥/2层交换机 路由器/3层交换机 4-7层交换机 网关 协议 分组交换协议 谁来规定协议 协议的标准化 协议的分层 分层 ...

最新文章

  1. 工具库 --- Validator (JS正则)
  2. 那些顶级的AI机器人“大脑”
  3. php引用数据库实例,PHP单例模式实例,连接数据库对类的引用
  4. 解题:POI 2004 String
  5. 【一天一个shell命令】好管家-磁盘-du
  6. hive最新UDF函数(2016-10-25)
  7. 七月时忙碌而充实的_如何减少忙碌而更有效
  8. 运营商取消话费余额有效期后改收闲置费
  9. android 按钮控制线程,关于手机安卓-线程测试
  10. 利用圆解一元二次方程
  11. UE4安装教程,虚幻引擎安装教程,UE4的安装
  12. 人心是暖的,眼泪是苦的,杜鹃花真的很香
  13. 论文图表录 出现几个 错误标签未定义的简单解决方法
  14. centos7通过startx后进入图形界面,使用eog命令提示unable to open display
  15. 【源码共读】Python 标准模块 collections 中 Counter 类详解
  16. “踢爆”职场焦虑、玩机车、文科转大厂程序媛,乘风破浪的 IT 女神太飒了!
  17. 最全量子计算硬件概述(建议收藏)
  18. 美团运维面试官没想到jenkins我用得这么溜,人直接傻掉
  19. pyhanlp 基础用法和教程
  20. Java调用so动态库方法

热门文章

  1. 纯HTML5+CSS3仿B站播放页
  2. opencv把图片转换成二进制_Python+OpenCV实现将图像转换为二进制格式
  3. 【论文阅读31】《OptCuts: Joint Optimization of Surface Cuts and Parameterization》
  4. javax.servlet.Filter详解(二) 转自:http://hi.baidu.com/jxnuywf/blog/item/f3834aee93e2fdf8b2fb95cc.html
  5. 计算机毕业设计java+ssm妇女联合会信息网站(源码+系统+mysql数据库+Lw文档)
  6. 博弈论 纳什均衡 囚徒困境 智猪博弈
  7. 与安装应用签名不同怎么解决_手动签名安装掉证书应用、多开任意应用教程
  8. 【软件】Chrome 浏览器下载文件崩溃
  9. 基于SSH的婴幼儿产品销售系统的开发与设计毕业设计论文
  10. MongoDB使用账号密码连接