嵌入式学习第32天(同时多个用户访问的服务器)

要想支持多个客户端同时访问,就必须解决两个问题

1.多个客户端

2.同时访问

单循环服务器(tcp)

docket

bind

listen

while(1)

{

accept

while(1)

recv

send

}

特点:实现简单,可以处理多个客户端

2.无法同时处理

多进程服务器

socket

bind

listen

while(1)

{

confd = accept

pit_t pid = fork();

if(pid == 0)

{

while(1)

{

recv

send

}

}

}

服务器:不断地处理客户端请求,但是accept//本身阻塞

通信过程中如果与客户端通信耗时,甚至是死循环,导致

整个服务器流程会卡在通信过程,没法处理新的客户端

注意:

1.将来要考虑回收资源

2.进程的切换和开销大

多线程服务器

void *do_hander (void * arg)

{

while(1)

{

recv;

send;

if()

{

close;

break;

}

}

}

socket

bind

litsten

while(1)

{

confd = accept

pthread_t tid;

ret=pthread_create()

pthread_detach();

}

注意:相比进程,效率确实提高了

但多线程回收资源,资源竞争;

IO多路复用

基本思想:尽可能的减少要创建的数量;将进程或者线程复用

IO多路复用的最大优势:

不需要创建和维护进程;系统开销小

IO模型

阻塞IO(有数据直接读取,没数据就一直等)

recv,read,fgets,scanf

特点:简单易用

非阻塞IO

读取数据

没数据时不阻塞,直接返回;但是他要轮询

说明:

一般相关的文件操作默认都是阻塞

可通过fcntl来设置对应的io模型

int fcntl(int fd, int cmd, ... /* arg */ );

修改文件的非阻塞属性:

int flag ;

flag = fcntl(fd,F_GETFL,0); ///获取fd文件的默认属性到flag变量中。

flag = flag | O_NONBLOCK; ///将变量的值调整并添加非阻塞属性

fcntl(fd,F_SETFL,flag); ///将新属性flag设置到fd对应的文件生效。

以上代码执行后的阻塞IO将变成非阻塞方式。

信号驱动IO

有信号来表示有数据可用;此时信号处理函数中运用

步骤:

1.为内核发送通知信号安装一个信号处理历程;如signal(SIGIO,handler)

2.设定文件描述符的属主,就是当文件描述符上可执行IO时会接受到

这个通知信号的进程或线fcntl(fd,F_SETOWN,pid);

3.设定O_NONBLOCK标志能非阻塞IO;

4.打开O_ASYNC标志信号使能信号驱动IO;

flags=fcntl(fd,F_GETTFL)

fcntl(fd,F_SETFL,flags|O_ASYNC|O_NONBLOCK);

问题:如果是多路IO无法处理;

多路复用IO

select

int select( int nfds,

fd_set *readfds,

fd_set *writefds,

fd_set *exceptfds,

struct timeval *timeout)

nfds:最大文件描述符+1;

readfds:存放关心读操作的那些文字描述符

*writefds:存放关心写操作的那些文字描述符

exceptfds,:存放关心出错的那些文字描述符

timeout:设置一个超时时间//NULL表示阻塞;//0表示非阻塞

注意:

  1. select 函数在一次就绪后,会将表中未就绪的文件描述符清除

select的调用一般要注意几点:

① readfds等是指针结果参数,会被函数修改,

所以一般会另外定义一个all read_fdset,

保持全部要监听读的句柄,将它的拷贝传递给select函数,

返回可读的句柄集合,类型fdset支持赋值运算符=;

② 要注意计算nfds,当新增监听句柄时比较容易修改,

当减少监听句柄时较麻烦些,

如果要精确修改需要遍历或者采用最大堆等数据结构维护这个句柄集,

以方便的找到第二大的句柄,或者干脆在减少监听句柄时不管nfds;

3 4 100 101 //4?

③timeout如果为NULL表示阻塞等,

如果timeout指向的时间为0,表示非阻塞,否则表示select的超时时间;

④ select返回-1表示错误,返回0表示超时时间到没有监听到的事件发生,

返回正数表示监听到的所有事件数(包括可读,可写,异常),

通常在处理事件时 会利用这个返回值来提高效率,

避免不必要的事件触发检查。

(比如总共只有一个事件,已经在可读集合中处理了一个事件,

则可写和异常就没必要再去遍历句柄集 判断是否发生事件了);

⑤ Linux的实现中select返回时会将timeout修改为剩余时间,

所以重复使用timeout需要注意。

//缺点:

select的缺点在于:

① 由于描述符集合set的限制,每个set最多只能监听FD_SETSIZE(在Linux上是1024)个句柄

(不同机器可能不一样);

② 返回的可读集合是个fdset类型,

需要对所有的监听读句柄一一进行FD_ISSET的测试来判断是否可读;

③ nfds的存在就是为了解决select的效率问题

(select遍历nfds个文件描述符,

判断每个描述符是否是自己关心的,对关心的描述符判断是否发生事件)。

但是解决不彻底,

比如: 如果只监听0和1000两个句柄,select需要遍历1001个句柄来检查事件。

//缺点:

(1).文件描述符数量受限

(2).底层监控是通过遍历实现的,效率不高

(3).返回值的表示的是就绪个数,

还需要应用层进一步处理

心得:

天高地炯,觉宇宙之无穷

兴尽悲来,识盈虚之有数

望长安于日下,目吴会于人间

地势极而南溟深,天柱高而北辰远

关山难越,谁悲失路之人

萍水相逢,尽是他乡之客

相关推荐
日拱一卒的小田2 小时前
ZYNQ学习笔记4-ZYNQ的SD卡控制器1
笔记·学习
国际云,接待3 小时前
华为云 EVS 性能诊断:用 CES、ECS QoS 与 fio 定位 IOPS 和吞吐瓶颈
服务器
我要见SA姐14 小时前
告别 Copilot?Codex 本地化部署指南
运维·数据库·机器学习·oracle·回归
拼图2094 小时前
Git常用语法
分布式·git·学习
Elastic 中国社区官方博客5 小时前
列式存储并不等同于列式数据库。Columnar 模式为 Elasticsearch 带来了什么
大数据·运维·数据库·elasticsearch·搜索引擎
fengkai45455 小时前
八、Docker详解4-5
运维·docker
Vcaker5 小时前
Linux学习29-kubernetes网络
学习
智恒百亿6 小时前
RTX 5090 八卡服务器 vs RTX PRO 6000 整机:AI 推理、微调与渲染选型部署指南
运维·服务器·rtx5090
ManageEngineITSM6 小时前
DevOps和ITIL是什么关系?是替代还是互补一文讲清
java·服务器·资产管理·变更管理
码少女7 小时前
Linux--多路转接之select
java·服务器·数据库