信阳网站设计,网站有必要使用伪静态么,建设一个asp网站,wordpress微信风格主题具有代表性的并发服务器端实现模型和方法#xff1a; 多进程服务器#xff1a;通过创建多个进程提供服务。 多路复用服务器#xff1a;通过捆绑并统一管理I/O对象提供服务。✔ 多线程服务器#xff1a;通过生成与客户端等量的线程提供服务。 目录
1. I/O复用
2. select函… 具有代表性的并发服务器端实现模型和方法 多进程服务器通过创建多个进程提供服务。 多路复用服务器通过捆绑并统一管理I/O对象提供服务。✔ 多线程服务器通过生成与客户端等量的线程提供服务。 目录
1. I/O复用
2. select函数
2.1 select函数的作用
2.2 设置文件描述符
2.3 指定监视范围
2.4 设置超时
2.5 查看调用select函数后的结果
2.7 与Windows系统的区别
3. 实现I/O复用的回声服务器端 1. I/O复用 “在一个通信频道中传递多个数据(信号)的技术。” “为了提高物理设备的效率用最少的物理要素传递最多数据时使用的技术。” 举个例子某个教师里有10名学生1位老师这10名学生都非等闲之辈他们会不停的提问所以学校没有办法只能给他们每个人都配一个老师这样这个教师就有10个老师10个学生但这样的话以后每当有一个新学生进来就要增加一个新老师这样下去也不是办法。这时学校来了个贼牛的老师他一个人就可以应对所有学生的提问而且速度很快所以学校就把其他老师给转移到了其他班。并且现在学生提问必须举手老师确认学生的提问再回答问题。现在这间教师就是以I/O复用方式运行的。 如图是I/O复用在服务端的模型。
2. select函数
#includesys/select.h
#includesys/time.hint select(
int maxfd, //监视对象文件描述符数量
fd_set* readset, //将所有关注是否存在待读取数据的文件描述符注册到fd_set型//变量并传递其地址值
fd_set* writeset, //将所有关注是否可传输无阻塞数据的文件描述符注册到fd_set型//变量并传递其地址值
fd_set* exceptset, //将所有关注是否发生异常的文件描述符注册到fd_set型变量//并传递其地址值
const struct timeval* timeout //调用select函数后为防止进入无限阻塞状态传递超时信息
);
成功返回0的值表示发生上述事件的文件描述符
超时返回0
失败返回-1
struct timaval
{long tv_sec; //secondslong tv_usec; //microseonds
}
2.1 select函数的作用
作用将多个文件描述符集中到一起统一监视。获取发生监视事件的文件描述符从而与这个文件描述符指定的套接字进行通信。 监视事件 1.是否存在套接字接收数据(read) 2.无需阻塞传输数据的套接字有哪些(write) 3.哪些套接字有异常(except) 2.2 设置文件描述符 select函数是怎么将多个文件描述符集中到一起监视的 答使用fd_set数组。
fd_set数组的结构 fd_set数组是一个位数组即只存储0与1。0表示当前文件描述符未被监视1表示当前文件描述符正在被监视。 那么fd_set数组是怎么将文件描述符集中到数组里的又是怎么设置它的位数的 答通过提供的宏来完成。
fd_set数组提供如下宏
宏含义FD_ZERO(fd_set* fdset);将fdset数组里的所有位初始化为0FD_SET(int fd,fd_set* fdset);将文件描述符fd的信息注册到fdset数组里即指定一个位置为此文件描述符并将其位设置为1。FD_CLR(int fd,fd_set* fdset);将fdset数组里的文件描述符fd的信息清除掉即设置为0。FD_ISSET(int fd,fd_set* fdset);判断fdset数组里有没有注册文件描述符fd的信息即指定位置处其位的值是否为1。有则返回true无则返回false。
所以使用select函数前的第一步就是先把要监视的文件描述符注册到fd_set数组里。
2.3 指定监视范围
select函数通过第一个参数来传递监视对象的文件描述符的数量。因为Linux里文件描述符的值是从3开始递增的所以你只需将最大的文件描述符的值再加1传递给select的第一个参数即可。
2.4 设置超时
struct timaval
{long tv_sec; //secondslong tv_usec; //microseonds
}
通过select函数的最后一个参数来设置超时因为select函数只有在有文件描述符发生变化时才会返回否则会一直阻塞住。
所以如果你不想阻塞住程序那么就可以设置超时时间传递给select的最后一个参数。
如果你想阻塞住程序直到有文件描述符发生变化你可以给select的最后一个参数传nullptr。
当达到超时时间而没有文件描述符改变时select函数返回0。
2.5 查看调用select函数后的结果
select函数在调用时除发生变化的文件描述符对应位外会把传递的fd_set数组里的其余位全部置为0。 如图你传入select函数的fd_set数组里要求监视的是文件描述符fd1、fd2、fd3等然后select函数就会将fd0、fd1、fd2、fd3等没有发生变化的文件描述符置为0发生变化的文件描述符就不改变。
所以你调用select函数后获取到的fd_set数组里的值位为1的都是发生变化的文件描述符然后你根据fd_set数组是第几个参数从而可以对它进行指定的操作例如你传递的fd_set数组的参数是select函数的第二个参数那么说明这个fd_set数组里位为1的文件描述符此时有输入流你就可以通过read函数来将里面的数据取出了。
2.7 与Windows系统的区别
上述都是在Linux系统下的select函数在Linux和Windows系统上完全相同。只是 1.在Windows系统上select函数的第一个参数是无意义的只是为了保持与Linux系统的兼容性。 2.Windows系统的fd_set与Linux的有区别。如下所示是Windows的fd_set结构体其中数组也是位数组并且使用的宏和Linux也是一样的。
typedef struct fd_set
{u_int fd_count; //套接字句柄数SOCKET fd_array[FD_SETSIZE]; //保存套接字句柄
}fd_set 为什么Windows要这样 因为在Linux中文件描述符是递增的所以你注册的时候系统可以很好的找出当前文件描述符和最后生成的文件描述符之间的关系。但是在Windows中套接字句柄SOCKET的生成并非是从0开始的值之间也没有规律所以需要直接保存句柄的数组和记录句柄数的变量。 3. 实现I/O复用的回声服务器端
实现思路
创建一个fdset数组里面要存放所有要监视的文件描述符。首先把server文件描述符注册到里面接着select监听这个server文件描述符因为TCP的连接也是以数据接收的形式进行的所以当有连接请求时select函数里的readfdset数组里注册的server文件描述符就会置为1。紧接着就处理这个连接请求把后面每连接的一个客户端的文件描述符就注册到fdset数组里用循环来遍历只要是server文件描述符为1就处理连接请求不是就是客户端文件描述符就处理读写。
代码
#includeiostream
#includesys/socket.h
#includeunistd.h
#includesignal.h
#includesys/wait.h
#includearpa/inet.h
#includecstring
#includesys/time.h
#includesys/select.hint main()
{int socketfdsocket(PF_INET,SOCK_STREAM,IPPROTO_TCP);if(socketfd-1){std::coutsocket fail!std::endl;return 0;}int bReusetrue;socklen_t reuseLensizeof(bReuse);setsockopt(socketfd,SOL_SOCKET,SO_REUSEADDR,(void*)bReuse,reuseLen);sockaddr_in serverAddr;memset(serverAddr,0,sizeof(serverAddr));serverAddr.sin_familyAF_INET;serverAddr.sin_addr.s_addrhtonl(INADDR_ANY);serverAddr.sin_porthtons(9130);if(-1bind(socketfd,(sockaddr*)serverAddr,sizeof(serverAddr))){std::coutbind fail!std::endl;return 0;}if(-1listen(socketfd,1)){std::coutlisten fail!std::endl;return 0;}fd_set fdset;FD_ZERO(fdset);FD_SET(socketfd,fdset);timeval timeout;timeout.tv_sec5;timeout.tv_usec0;fd_set tempset;int fdmaxsocketfd;while(1){tempsetfdset;int resultselect(fdmax1,tempset,0,0,timeout);if(result0){continue;}else if(result-1){std::coutselect failstd::endl;break;}else{for(int i0;ifdmax1;i){if(FD_ISSET(i,tempset)){if(isocketfd) //服务器文件描述符发生了变化意味着有连接请求{sockaddr_in clientAddr;memset(clientAddr,0,sizeof(clientAddr));socklen_t clientLensizeof(clientAddr);int clientfdaccept(i,(sockaddr*)clientAddr,clientLen);std::cout客户端IP地址:inet_ntoa(clientAddr.sin_addr)std::endl;if(-1clientfd){std::coutaccept fail!std::endl;}else{FD_SET(clientfd,fdset); //成功接收请求把客户端文件描述符信息注册到fdset里if(fdmaxclientfd)fdmaxclientfd;}}else //客户端文件描述符发生了变化意味着有读取{char buff[1024];int readlenread(i,buff,sizeof(buff));if(readlen0) //客户端EOF断开连接不再发送数据{FD_CLR(i,fdset); //把fdset里的客户端文件描述符给注销掉close(i); //关闭客户端套接字}else{std::cout客户端发来的消息:buffstd::endl;write(i,buff,readlen);}}}}}}close(socketfd);return 0;
}
Windows系统
要注意其fdset不是一个数组而是一个结构体要用“.”或“-”运算符来取里面的套接字文件描述符数组来进行处理。