嵌入式学习第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).返回值的表示的是就绪个数,

还需要应用层进一步处理

心得:

天高地炯,觉宇宙之无穷

兴尽悲来,识盈虚之有数

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

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

关山难越,谁悲失路之人

萍水相逢,尽是他乡之客

相关推荐
龙腾-虎跃1 小时前
Linux 趣味实战|纯 Bash Shell 脚本实现俄罗斯方块终端小游戏(完整源码资源)
linux·运维·bash
阳光九叶草LXGZXJ8 小时前
达梦数据库-报错-15-列【XXX】长度超出定义
linux·运维·数据库·sql·学习
黄大刀10 小时前
DataPulse一款开箱即用的SCADA(四)-历史记录
运维·scada·动环
正在走向自律11 小时前
AI数据分析与可视化:从基础到应用实践
服务器·人工智能·python·机器学习·数据分析·pandas
mlidongfeng11 小时前
【学习】mKernel 分析
学习
OnlineProxy11 小时前
亚马逊多账号运营:如何在规避“关联封号”风险的同时实现电商规模化扩张
服务器·数据库·redis
归秋14212 小时前
2026 企业 AI 办公工具选型指南:从评估框架到产品适配
大数据·运维·人工智能
Android系统攻城狮12 小时前
Linux Gstreamer深度解析之gst_audio_encoder_set_frame_max调用流程与实战(六十一)
android·linux·运维·音视频·gstreamer音视频·音视频进阶·gstreamer音视频进阶
一只小小的芙厨13 小时前
【线性DP】
学习·算法·动态规划
91刘仁德13 小时前
IP协议详解:从IP协议头到网段划分、路由与NAT
linux·服务器·网络·网络协议·tcp/ip