1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > 常见的几种服务器/客服端模型

常见的几种服务器/客服端模型

时间:2024-05-24 04:26:08

相关推荐

常见的几种服务器/客服端模型

其实常见的几种服务器/客服端模型都可以,用餐厅,服务员,客人来解释。

餐厅:代表服务器顾客:代表客服端服务员:代表处理客人请求的事件菜单:菜单方便顾客了解菜品和价格,客人读了菜单,然后点菜,相当于服务器发给客服端的数据记账本:服务员记录顾客点了那些菜,方便后厨做菜和服务员收钱,相当于客服端给服务器发送数据

大致流程:

顾客进入餐厅(前提是餐厅门开了且正在营业∩﹏∩),服务员就前来招待,顾客会先看一下菜单,然后点餐,服务员在记账本记下菜品然后交给后厨做菜(现实中顾客对服务员的请求不光是点菜,有时候可能会问厕所在哪∩︿∩)菜做好了,服务员把菜端上来,然后顾客开吃。顾客吃完了,招呼一声服务员结账,顾客结完账没什么事就离开餐厅。

下面就以具体的模型分情况介绍。

======================================================================================

单线程循环模型

======================================================================================

这个餐厅只有一个服务员,这个服务要处理所有顾客的点菜,结账等请求,如果当前顾客的请求事件一直没有处理完,其他需要处理请求的顾客就会一直等待,有些顾客不愿等,就会到别家餐厅吃饭。

简单代码:

listen(serverfd, listen_num);while(1){connfd = accept(serverfd);read(connfd);write(connfd);close(connfd);}

优点:由于只请了一个服务员,所以节省了人工费缺点:当顾客少时,服务员能即使处理请求,当顾客多时,服务员就忙不过来了,也会损失一部分顾客。

实际的服务器/客服端

优点:

简单、易于实现没有同步、加锁这些麻烦事,也没有这些开销

缺点:

不能同时处理过多的客服端请求没有利用多核cpu的优势

餐厅老板发现,顾客一多,服务员就忙不过来,干脆就每来一个顾客,就给他分配一个服务员,这个服务员就专门处理一个顾客的请求。

然后就变成下面的情况:

一个人来就餐,一个服务员去服务,然后客人会看菜单,点菜。 服务员将菜单给后厨。

二个人来就餐,二个服务员去服务……

五个人来就餐,五个服务员去服务……

而这种情况有分多进程和多线程

======================================================================================

多进程模型

======================================================================================

由于每个菜单和记账本都属于餐厅的资源(服务器的资源),而多进程就是给每一个服务员都分配一个菜单和记账本,服务员之间各用各的,没有竞争关系。

简单代码:

listen(serverfd, listen_num);while(1){connfd = accept(serverfd);if ((child_pid = frok()) == 0) {close(serverfd);read(connfd);write(connfd);close(connfd);}close(connfd);}

优点:可以同时满足多个顾客的请求。

缺点:成本高(每个服务员都有菜单和记账本),一个服务员还好,如果一百个服务员,那餐厅直接亏本。

======================================================================================

多线程模型

======================================================================================

老板发现给每个服务员都分配一个菜单和记账本成本太高,于是只准备一个菜单和记账本,如果一个服务员把菜单和记账本拿走了,只能等她把菜单和记账本归还后才能使用(这里相当于互斥锁),这样既可以解决同时服务多个顾客的问题,又可以不让成本太高。

简单代码:

listen(serverfd, listen_num);while(1){connfd = accept(serverfd);pthread_create(&tid, NULL, &doit, (void *)connfd);}void *doit(void *arg){pthread_detach(pthread_self());read(connfd);write(connfd);close(connfd);}

优点:可以解决同时服务多个顾客的问题,又可以不让成本太高。缺点:成本还是很高,而且还进行互斥,增加了管理的麻烦。

具体代码可参考我的博客:/qq_40732350/article/details/89043038

======================================================================================

进程池和线程池模型

======================================================================================

老板又发现,多线程和多进程分配的服务员太多了,成本太高了,而且每次都是顾客进店后才让一个服务员准备

服务员准备的过程:穿上工作服,拿好菜单和记账本等等。

这样再时间上太慢,不能及时的服务顾客。

于是老板在开张时一次性安排10服务员,这10个服务员把所有准备工作都好然后迎接顾客,如果顾客太多,10个服务员不够,那就要靠餐厅的协调能力,而且得让顾客等一会儿。

代码就不写了,参考下面的图

优点:成本低,响应时间快缺点:成本比单线程高,需要系统来协调。

具体代码可参考我的博客:/qq_36221862/article/details/73694504

======================================================================================

I/O多路复用模型

======================================================================================

老板又发现(这个老板老是发现∩▂∩)多线程和多进程的成本都很高,而且有的顾客有特别磨叽,半天也没点好菜,耽误了太多时间。

于是就用下面的办法。

餐厅只分配一个服务员,在顾客点菜时,服务员把菜单放在桌子上,马上离开去服务其他顾客,等顾客点完了通知一下服务员,然后服务员再来处理。

由于Linux中多了复用有两种方式:select 和 epoll

select

while(1){select(fd_max+1, &cpy_reads, 0, 0, &timeout))== -1)for(i=0; i<fd_max+1; i++) {if(FD_ISSET(i, &cpy_reads)) {if(i==serv_sock) { //有一个新顾客来了adr_sz=sizeof(clnt_adr);clnt_sock=accept(serv_sock, (struct sockaddr*)&clnt_adr, &adr_sz);FD_SET(clnt_sock, &reads);if(fd_max<clnt_sock) fd_max=clnt_sock;}else{//顾客发送请求了str_len=read(i, buf, BUF_SIZE);if(str_len==0) { //顾客走了FD_CLR(i, &reads);close(i);}else{write(i , buf, str_len); //给顾客反馈}}}}}

epoll

while(1){event_cnt=epoll_wait(epfd, ep_events, EPOLL_SIZE, -1);for(i=0; i<event_cnt; i++ ) {if(ep_events[i].data.fd==serv_sock) { //有一个新顾客来了dr_sz=sizeof(clnt_adr);clnt_sock=accept(serv_sock, (struct sockaddr*)&clnt_adr, &adr_sz);event.events=EPOLLIN;event.data.fd=clnt_sock;epoll_ctl(epfd, EPOLL_CTL_ADD, clnt_sock, &event);}else{str_len=read(ep_events[i].data.fd, buf, BUF_SIZE);//顾客发送请求了if (str_len==0) { //顾客走了epoll_ctl(epfd, EPOLL_CTL_DEL, ep_events[i].data.fd, NULL);close(ep_events[i].data.fd);}else{write(ep_events[i].data.fd, buf, str_len); //给顾客反馈}}}}

epoll又分 边缘触发 和水平触发

水平触发方式:如果文件描述符已经就绪可以非阻塞的执行IO操作了,此时会触发通知。允许在任意时候重复检测IO的状态,没有必要每次描述符就绪后尽可能多的执行IO,select,poll就属于水平触发事件。只要满足要求就触发一个事件。边缘触发方式:如果文件描述符自上次状态改变后有新的IO活动到来,此时会触发通知。在收到一个IO事件通知尽可能多的执行IO操作,因为如果再一次通知中没有执行完IO那么就需要等到下一次新的IO活动到来才能获取就绪的描述符。信号驱动式IO就属于边缘触发。每当状态改变就触发一个事件。优点:成本低,不用加互斥条件,可以最大限度的利用CPU缺点:不能同时服务多个请求

可以参考我的博客:/QQ2558030393/article/details/90896317

小结:每种模型都有它使用的场景,适合的才是最好的。

参考:

/sOS-589117.htm

/developer/article/1376352

/yexiangCSDN/article/details/85988776

/qq_36221862/article/details/73694504

/QQ2558030393/article/details/91360750

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。