同步阻塞,同步非阻塞,select,poll,epoll
场景;老师让学生做作业,做完收作业
同步阻塞:逐个收,收完A,收B,然后再收C,如果前面有人没做完,等他写完收他的,然后再收后面的人的作业
同步非阻塞:如果有一个学生没做完,就跳过这个人,先收下一个
select:有人写完了,就会摇一下铃,但是你不知道是谁写完摇的铃,所以需要一个个的去询问
server监听100个客户端的连接,有客户端发送消息write就去循环遍历这一百个连接,找到发消息的1个或者多个客户端,然后server执行read操作,select多路复用最大的问题就是要轮询查找,连接数太多的时候就太浪费时间了
epoll:有人写完了,会主动把作业交给老师 就会摇一下铃,你知道是谁摇的,直接去收他的作业
监听100个客户端连接,如果有客户端发送消息,那注册在这个客户端上的回调函数就会被执行,server就不需要去遍历轮询
先理解socket套接字:客户端发送数据需要一个出口,服务端接收数据需要一个入口,这两个口子就是socket
再理解文件描述符Fd :资源的索引,linux内核通过Fd来访问和管理资源
当你新建一个socket的时候,就可以通过对这个socket的fd进行操作来管理这个socket连接
socket通信的过程:
linux操作系统底层会分为用户空间和内核空间,当进程或者线程运行在内核空间的时候,就处于内核态,运行在用户空间就处于用户态,在用户态下只能执行一些相对安全的cpu指令,一些比较危险的特权指令只能在内核空间下执行,这样是为了安全,应用程序瞎搞,把整个操作系统搞挂了
但是调用socket的一些相关操作都是需要在内核态下才能完成的,用户态无法直接操作socket,于是操作系统内核提供read()函数和write()函数供处于用户态的程序使用,这些函数就称为系统调用函数,当我们调用系统调用函数的时候,系统会切换到内核态,由内核态来执行相应的socket操作
调用read()函数的时候,先将数据从网卡拷贝到内核空间(也叫sockt缓冲区),然后再从内核空间拷贝到用户空间
write()函数则刚好相反,先从用户空间拷贝到内核空间,然后再从内核空间拷贝到网卡上
假设服务端和四个客户端建立了socket连接,所以现在有四个Fd,当我们调用read函数进行数据读取时,
服务器端实现并发有两种方式:多进程处理并发,多线程处理并发
如果服务器端的程序只有一个进程或者一个线程,如何来实现并发呢,就需要使用I/O多路并发
I/O复用这种方式有三种解决方案:select,poll,epoll
select在windows,macos,linux上都可以使用,而poll和epoll只能在linux上使用
select,poll底层用的是线性表,epoll底层使用的是红黑树,所以epoll效率最高
着重要掌握的是select和epoll