Linux服务器项目-9
1. I/O多路复用(IO多路转接)
这里的输入输出指的是文件和内存间的输入和输出;传统意义上从文件到内存为输入,从内存到文件为输出。
I/O 多路复用使得程序能同时监听多个文件描述符,能够提高程序的性能,Linux 下实现 I/O 多路复用的系统调用主要有 select、poll 和 epoll。
为了解决一个服务器只能和一个客户端通信的问题,使用了多进程或者多线程的方式。产生缺点的原因是blocking。
IO多路转接技术:
select/poll代收员比较懒,她只会告诉你有几个快递到了,但是哪个快递,你需要挨个遍历一遍。
epoll代收快递员很勤快,她不仅会告诉你有几个快递到了,还会告诉你是哪个快递公司的快递
select,poll,epoll本质上是通过内核实现的,与read和recv的原理不同,如果某个fd有数据,就有将标志位置于1,否则置于0。==实际上就是通过内核检测fd是否有数据,汇总后再告诉上层应用==,上层应用不用依次检测文件描述符判断数据。
select
主旨思想:
首先要构造一个关于文件描述符的列表,将要监听的文件描述符添加到该列表中。
调用一个系统函数,监听该列表中的文件描述符,直到这些描述符中的一个或者多个进行I/O操作时,该函数才返回。
a.这个函数是阻塞
b.函数对文件描述符的检测的操作是由内核完成的
在返回时,它会告诉进程有多少(哪些)描述符要进行I/O操作。
1 | // sizeof(fd_set) = 128字节 1024位 |
假如只有fd=3和4有数据
通过select实现多路复用技术:
1 |
|
select用数组的序号作为不同进程的文件描述符,这一点很不方便,而且有很多空间浪费,poll函数通过定义了一个包含fd以及其读写事件的结构体,解决了这一问题。
1 |
|
1 |
|
poll改进了select的第三个和第四个缺点。但是还存在缺点就是内核反馈回来的fd信息必须通过程序全部遍历一遍才可得出,开销很高,(对应缺点2);而epoll函数可以解决这个问题。
epoll
效率比select和poll的效率高,主要原因是不需要用户态到内核态的转换,此外通过rdlist向用户态拷贝的只有就绪态的文件描述符,而不是全部的文件描述符。
1 |
|
epoll实现IO多路复用:
1 |
|
Epoll 的工作模式:
LT 模式 (水平触发)默认
假设委托内核检测读事件 -> 检测fd的读缓冲区读缓冲区有数据 - > epoll检测到了会给用户通知 a.用户不读数据,数据一直在缓冲区,epoll 会一直通知 b.用户只读了一部分数据,epoll会通知 c.缓冲区的数据读完了,不通知
LT(level - triggered)是缺省的工作方式,并且同时支持 block 和 no-block socket。在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的 fd 进行 IO 操作。如果你不作任何操作,内核还是会继续通知你的。
ET 模式(边沿触发)
假设委托内核检测读事件 -> 检测fd的读缓冲区
读缓冲区有数据 - > epoll检测到了会给用户通知
a.用户不读数据,数据一致在缓冲区中,epoll下次检测的时候就不通知了
b.用户只读了一部分数据,epoll不通知
c.缓冲区的数据读完了,不通知
ET(edge - triggered)是高速工作方式,只支持 no-block socket。在这种模式下,当描述符从未就绪变为就绪时,内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知,直到你做了某些操作导致那个文件描述符不再为就绪状态了。但是请注意,如果一直不对这个 fd 作 IO 操作(从而导致它再次变成未就绪),内核不会发送更多的通知(only once)。
ET 模式在很大程度上减少了 epoll 事件被重复触发的次数,因此效率要比 LT 模式高。epoll工作在 ET 模式的时候,必须使用非阻塞套接口,以避免由于一个文件句柄的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死。
1 | struct epoll_event { |
UDP
1 |
|
TCP创建的是SOCK_STREAM
,UDP创建的是SOCK_DGRAM
。
UDP通信的实现:
1 | //server端 |
广播
向子网中多台计算机发送消息,并且子网中所有的计算机都可以接收到发送方发送的消息,每个广播消息都包含一个特殊的IP地址,这个IP中子网内主机标志部分的二进制全部为1。UDP流程
a.只能在局域网中使用。
b.客户端需要绑定服务器广播使用的端口,才可以接收到广播消息。
1 | // 设置广播属性的函数 |
广播的案例:
1 | //服务端 |
组播(多播)
单播地址标识单个 IP 接口,广播地址标识某个子网的所有 IP 接口,多播地址标识一组IP 接口。单播和广播是寻址方案的两个极端(要么单个要么全部),多播则意在两者之间提供一种折中方案。多播数据报只应该由对它感兴趣的接口接收,也就是说由运行相应多播会话应用系统的主机上的接口接收。另外,广播一般局限于局域网内使用,而多播则既可以用于局域网,也可以跨广域网使用。UDP流程
a.组播既可以用于局域网,也可以用于广域网
b.客户端需要加入多播组,才能接收到多播的数据
组播地址
IP 多播通信必须依赖于 IP 多播地址,在 IPv4 中它的范围从 224.0.0.0 到 239.255.255.255 ,并被划分为局部链接多播地址、预留多播地址和管理权限多播地址三类:
1 | int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen); |
本地套接字
本地套接字的作用:本地的进程间通信
- 有关系的进程间的通信
- 没有关系的进程间的通信
本地套接字实现流程和网络套接字类似,一般呢采用TCP的通信流程。
1 | // 本地套接字通信的流程 - tcp |
1 | // 头文件: sys/un.h |
程序
1 | //服务端 |