【网络】代理服务器收尾及高级IO
全是通俗易懂的讲解,如果你本节之前的知识都掌握清楚,那就速速来看我的笔记吧~
自己写自己的八股!让未来的自己看懂! (全文手敲,受益良多)
本文主要带你了解什么是高级IO,以及常见的多路复用 接口。具体代码使用在下一篇文章
续上文收尾:
我们要访问目标服务器,一定会经过运营商。报文内容都会经过运营商,所以运营商可以对这些报文做分析,对非法的请求(访问外网)做拦截。有那么一种服务器,在特殊地区,但是可以访问外网,我们可以通过访问这个服务器,进而访问外网,规避运营商的检测。
常规情况下,http默认携带的是非http的其他数据。HTTP 隧道技术是一种通过 HTTP 协议来传输非 HTTP 数据的方法,它允许在 HTTP 连接上封装其他协议(如 TCP、UDP 等)的数据,从而突破网络限制或绕过防火墙的某些规则,实现特定应用的通信。
NAT和代理服务器
路由器往往都具备 NAT 设备的功能, 通过 NAT 设备进行中转, 完成子网设备和其他子
网设备的通信过程.
代理服务器看起来和 NAT 设备有一点像. 客户端向代理服务器发送请求, 代理服务器将
请求转发给真正要请求的服务器; 服务器返回结果后, 代理服务器又把结果回传给客户端.
那么 NAT 和代理服务器的区别有哪些呢?
- 从应用上讲, NAT 设备是网络基础设备之一, 解决的是 IP 不足的问题. 代理服务器则是更贴近具体应用, 比如通过代理服务器进行翻墙, 另外像迅游这样的加速器,也是使用代理服务器.
- 从底层实现上讲, NAT 是工作在网络层, 直接对 IP 地址进行替换. 代理服务器往往工作在应用层.
- 从使用范围上讲, NAT 一般在局域网的出口部署, 代理服务器可以在局域网做,也可以在广域网做, 也可以跨网.
- 从部署位置上看, NAT 一般集成在防火墙, 路由器等硬件设备上, 代理服务器则是一个软件程序, 需要部署在服务器上.
代理服务器是一种应用比较广的技术.
• 翻墙: 广域网中的代理.
• 负载均衡: 局域网中的代理.
高级IO
IO:输入Input输出Output。我们以读写为例:如果我们read,但是接受缓冲区没有数据,就会阻塞地等,write也一样。
- 应用层read&&write的时候,本质就是把数据从应用层给OS——本质就是拷贝函数
- IO=等待+拷贝。在等read时接受缓冲区有没有数据,write时发送缓冲区有没有空余位置。所以要进行拷贝,必须判断条件(读写事件)成立。
什么叫做高效IO呢?
单位时间内,IO过程中,等的比重越小,IO效率越高。几乎所有提高IO效率的策略,本质上就是让等的比重越小。
五种IO模型
- 阻塞式IO(read,write就是这种)
- 非阻塞式IO,非阻塞轮询
- 信号驱动式IO
- 多路复用,多路转接
- 异步IO
前4种IO称为同步IO
阻塞式IO和非阻塞IO有什么区别?
答:IO=等待+拷贝。等的方式不同而已,非阻塞IO可以在等的时候(非阻塞轮询)做其他事情。IO上效率没区别。
同步IO和异步IO有什么区别?
答:二者本质是有没有参与IO,只要参与等或拷贝,就参与了IO。同步参与了IO,异步只是发起IO,没有参与IO,拿起结果就行。
同步IO和线程同步有关系吗?
答:没有关系。同步IO是IO上的,线程同步是2个线程谁先谁后。
阻塞IO:
非阻塞IO:
信号驱动IO:
多路转接IO:
异步IO:
非阻塞IO
fcntl
一个文件描述符, 默认都是阻塞 IO.
函数原型如下.
#include <unistd.h>#include <fcntl.h>int fcntl(int fd, int cmd, ... /* arg */ );
传入的 cmd 的值不同, 后面追加的参数也不相同.
fcntl 函数有 5 种功能:
• 复制一个现有的描述符(cmd=F_DUPFD).
• 获得/设置文件描述符标记(cmd=F_GETFD 或 F_SETFD).
• 获得/设置文件状态标记(cmd=F_GETFL 或 F_SETFL).
• 获得/设置异步 I/O 所有权(cmd=F_GETOWN或 F_SETOWN).
• 获得/设置记录锁(cmd=F_GETLK,F_SETLK 或 F_SETLKW).
我们此处只是用第三种功能, 获取/设置文件状态标记, 就可以将一个文件描述符设置为非阻塞.
实现函数 SetNoBlock
基于 fcntl, 我们实现一个 SetNoBlock 函数, 将文件描述符设置为非阻塞.
void SetNoBlock(int fd)
{int fl = fcntl(fd, F_GETFL);if (fl < 0) {perror("fcntl");return;}fcntl(fd, F_SETFL, fl | O_NONBLOCK);}
• 使用 F_GETFL 将当前的文件描述符的属性取出来(这是一个位图).
• 然后再使用 F_SETFL 将文件描述符设置回去. 设置回去的同时, 加上一个O_NONBLOCK 参数.
阻塞式正常读: 非阻塞:
一旦我们设置为非阻塞,如果底层fd数据没有就绪,recv/read/write/send,返回值会以出错的形式返回。A.真的出错了,B.底层没有就绪。通过errno区分!!!
多路转接——select
select只负责等,一次等待多个fd。
返回值:大于0:有n个fd就绪了,等于0:超时,没有错误,但是没有fd就绪,小于0:等待出错了。
nfds:n个fd里面最大的fd+1。
timeout是输入输出型参数:
fd_set:内核提供的数据类型,它是位图。
Fd_set *readfds 是输入输出型参数,
输入时:用户告诉内核,我给你一个或者多个fd,你要帮我关心fd上面的读事件,如果就绪了,要告诉我。
输出时:内核告诉用户,你让我关心的多个fd中,有哪些已经就位了,你快去读吧。
比特位的位置,表示文件描述符的编号,比特位的内容表示是否需要内核关心,上面的读事件已经就绪了。
结论:fd_set是一张位图,让用户,内核之间传递fd是否就绪的信息的.注定了,使用select的时候,一定有大量的位图操作。
代码:
数据获取上来了,不可以直接读取。目前代码是单进程,建立连接的时候,不一定发消息了,所以read可能会阻塞住,必须要让select处理,将新连接交给select。
Select缺点
- 等待的fd是有上线的,因为位图有上线
- 输入输出型参数比较多,数据拷贝的频率比较高
- 输入输出型参数比较多,每次都要对关心的fd进行事件重置
- 使用第三方数组管理用户的fd,用户层需要多次遍历。内核中检测fd事件就绪,也要遍历
为了解决这些缺点,我们有另一个多路转接的方案
Poll
poll:只负责等待,
将输入输出事件进行了分离!!
Poll也有在用户层,内核方面的遍历效率问题。为了解决我们有epoll
本文主要是概念,篇幅不多,下篇文章才是重头戏!
下一章我将详细介绍,select,poll,epoll的使用~期待你的关注👉 【A charmer】