socket 编程
网络通讯的本质目标就是进程间通信
socket 编程模型
unix 系统 socket 是一种特殊的文件,双向管道,一端是应用,一端是缓存区
socket 连接应用和协议。应用程序 传输数据,就创建一个 socket 。向 socket 中写数据相当于将数据发送给另一个应用。应用从 socket 中读取数据,相当于接收另一个程序发送的数据。
在服务端有两种 Socket:一个是用于监听的 Socket,另一种是每条连接各自对应的已连接 Socket。需要注意的是,监听 Socket 里并不是「存着一堆客户端的文件描述符等着被读出来」,内核为它维护的是半连接队列和全连接队列,accept() 每次从全连接队列里取出一个已完成握手的连接,返回一个新的文件描述符,也没法靠 read() 监听 Socket 来拿到客户端。
让一个线程能同时照看所有连接的是另一件事,即 I/O 多路复用:用 select/poll/epoll 同时监视这一批已有的文件描述符的可读可写状态,哪个就绪了再去读写对应的描述符。
主动式的 I/O 多路复用,对负责 I/O 的线程压力过大,因此通常会设计一个高效的中间数据结构作为 I/O 事件的观察者,线程通过订阅 I/O 事件被动响应,这就是响应式模型。在 Socket 编程中,最适合提供这种中间数据结构的就是操作系统的内核,事实上 epoll 模型也是在操作系统的内核中提供了红黑树结构。
I/O 复用手段 同步调用
select poll线性结构,每次调用都要把描述符集合从用户态拷进内核,并遍历全部被监视的描述符epoll红黑树加就绪队列,描述符集合常驻内核,只把就绪的那部分返回给用户
每次 select 操作会阻塞当前线程,在阻塞期间所有操作系统产生的每个消息,都会通过遍历的手段查看是否在 3 个集合当中 (用户传入)
poll : 用户不需要用 select 来设置自己的集合,而是可以通过系统的 API 直接拿到对应的消息,从而处理对应的文件描述符,性能角度分析差距不大。
epoll 将进程关注的文件描述符存入一棵二叉搜索树,通常是红黑树的实现。在这棵红黑树当中,Key 是 Socket 的编号,值是这个 Socket 关注的消息。因此,当内核发生了一个事件:比如 Socket 编号 1000 可以读取。这个时候,可以马上从红黑树中找到进程是否关注这个事件。
另外当有关注的事件发生时,epoll 会先放到一个就绪队列当中。当用户调用 epoll_wait 时候,就会从队列中返回一个消息。epoll_create 是一个构造函数,只用来创建红黑树和队列结构。
epoll_wait 会不会马上返回,取决于传入的 timeout:传 0 就立刻返回,传 -1 就一直阻塞到有事件为止;反过来 select/poll 把 timeout 设成 0 也同样立刻返回。所以不好凭这一点说 epoll 是非阻塞模型,三者都属于同步的多路复用(也就是上面写的「同步调用」)。它们真正的差别在于:epoll 直接从就绪列表里取结果,开销只和就绪的描述符数量相关,而 select/poll 每次都要遍历全部被监视的描述符(O(n)),并且描述符集合每次都得从用户态重新拷进内核。
传输层 负责 应用传输数据给应用。
端口号: 编号将应用区分开, 80 web 服务器, 22 ssh ,浏览器 临时分配端口号。 TCP 封包上携带了 端口号,接收方识别封包然后发送对应应用
网络层 ip 寻址,路由
数据链路层 为 网络层提供 链路级别传输的支持
物理层: 为 数据链路层 提供二进制传输服务
多路复用 : 一台机器可能只有一份网卡网线,而应用可能有很多。 多个信号,复用一个信道.提升吞吐量,信号相互隔离,并行传输。
应用层多路复用: eg:一个应用可能同时向服务器发送多个请求,多个请求可以 复用 一个 TCP 连接
传输层多路复用: 多个请求并行的发送请求,eg: 多个 TCP 链接复用一条线路,依赖网路层提供多路复用能力
网络层多路复用: 网络层天然支持多路复用
示例:
HTTP 协议, 浏览器打开时,会向服务器发送多个请求,多个请求混合在一起,复用相同连接,数据紧密切互相隔离。