1、NAT
NAT (网络地址转换)技术主要解决 IPv4 地址不够用的问题,是路由器的一个重要功能。它在网络通信过程中将一个IP地址空间的地址转换为另一个IP地址空间的地址,即私有地址和公有地址之间的转换。
NAT 转换过程:
如果局域网内有多个主机都访问同一个外网服务器,那么对于服务器返回的数据中,目的 IP 都是相同的,因此NAPT需要用 IP+port 的方式来映射唯一的一台主机。
但是由于 NAT 依赖这个转换表,所以有诸多限制:
代理服务器是一种中间服务器,充当客户端与互联网之间的桥梁。
| 工作过程: 当客户在浏览器中设置好代理服务器后,使用浏览器访问所有WWW站点的请求都不会直接发给目的主机,而是先发给代理服务器。代理服务器接受客户的请求后,会向目的主机发出请求,并接受目的主机的数据,存于代理服务器的硬盘中,然后再由代理服务器将客户要求的数据发给客户。如果代理服务器已经缓存了客户所需的数据,它会直接将这些数据返回给客户,而无需再次向目的主机请求。
| 正向代理 & 反向代理: 正向代理是指客户端通过代理服务器来访问外部网络资源,即正向代理代理的是客户端,帮助客户端访问其无法直接访问的服务器资源;反向代理是指客户端不知道目标服务器的存在,代理服务器位于客户端和目标服务器之间,接收来自客户端的请求并将其转发给内部网络上的真实服务器进行处理,即反向代理代理的是服务器,将外部网络连接请求转发给内部网络上的服务器。
| 原理: 内网穿透的工作原理主要基于 NAT 技术的逆过程。NAT技术将内部网络的私有IP地址转换为外部网络的公网IP地址,实现内部网络与外部网络的通信。但是NAT设备会自动屏蔽非内网主机主动发起的连接,这使得从外网发往内网的数据包被NAT设备丢弃。内网穿透技术通过特定的方法,如端口映射、反向代理等,绕过这一限制,实现内外网之间的通信。
| 方法:
| 应用:
注意事项: 内网穿透涉及到将内网设备暴露在外部网络环境中,存在一定的安全风险,要保证安全性、稳定性、兼容性、且要严格遵守法律法规。
DNS 是一整套从域名映射到 IP 的系统。
TCP/IP 中使用 IP 地址和端口号来确定网络上的一台主机的一个程序,但是 IP 地址不 方便记忆,于是就有了一种叫主机名的东西,是一个字符串,并且使用 hosts 文件来描述主机名和 IP 地址的关系。
| 域名解析过程:
ICMP: 一个新搭建好的网络,往往需要先进行一个简单的测试,来验证网络是否畅通。但是 IP协议并不提供可靠传输,如果丢包了,IP 协议并不能通知传输层是否丢包以及丢包的原因。ICMP 协议是一个网络层协议,主要解决的就是这个问题。它的功能是:
ping 命令基于 ICMP,是在网络层,而端口号是传输层的内容,在 ICMP 中根本就不关注端口号这样的信息。
IO = 等 + 拷贝。
即在任何 IO 过程中,都包含两个步骤,第一是等待,第二是拷贝。拷贝效率受硬件的限制,不容易提高;而且在实际的应用场景中,等待消耗的时间往往都远远高于拷贝的时间,所以要想提高 IO 的效率,最核心的办法就是让等待的时间尽量少。
高效的IO就是:单位时间内,“等” 的时间比重越低,IO效率越高。
recvfrom
。recvfrom
,若数据未就绪,返回EWOULDBLOCK
。recvfrom
,直到数据就绪。select
/poll
/epoll
监控多个描述符,任一就绪时通知应用。select
,阻塞等待描述符就绪。select
返回。recvfrom
读取数据(复制阶段可能阻塞)。epoll
),但编程复杂度较高。SIGIO
)通知数据就绪,应用异步处理。recvfrom
读取数据(复制阶段可能短暂阻塞)。aio_read
,指定缓冲区并立即返回。io_uring
或Windows的IOCP)。理解这五种模型有助于根据需求选择合适的I/O策略,优化系统性能与资源利用率。
| 同步 vs 异步:
| fcntl:
fcntl
函数原型:
#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
)cmd=F_GETOWN
或 F_SETOWN
)cmd=F_GETLK
,F_SETLK
或 F_SETLKW
)阻塞式IO:
我们可以用第三种功能将一个文件描述符设置为非阻塞:
#include <iostream>
#include <string>
#include <unistd.h>
#include <fcntl.h>
using namespace std;
void SetNonBlock(int fd)
{
int fl = fcntl(fd, F_GETFL);
if (fl < 0)
{
perror("fcntl");
return;
}
fcntl(fd, F_SETFL, fl | O_NONBLOCK);
}
int main()
{
string tips = "Please Enter# ";
char buffer[1024];
SetNonBlock(0);
while (true)
{
write(0, tips.c_str(), tips.size());
ssize_t n = read(0, buffer, sizeof(buffer) - 1);
if (n > 0)
{
buffer[n] = 0;
cout << "echo# " << buffer << endl;
}
else if (n == 0)
{
cout << "read file end!" << endl;
break;
}
else
{
cout << "read error: " << n << endl;
}
sleep(1);
}
return 0;
}
从上面的测试中我们可以得出:
但是我们还知道,read
函数读取错误也会返回-1,那怎么区分究竟是读取错误还是数据未就绪呢?通过errno
查找更详细的错误原因。