TCP/IP 网络模型有哪几层?
1.应用层
为用户提供应用功能
2.传输层
负责为应用层提供网络支持
使用TCP和UDP
当传输层的数据包大小超过 MSS(TCP 最大报文段长度) ,就要将数据包分块,这样即使中途有一个分块丢失或损坏了,只需要重新发送这一个分块,而不用重新发送整个数据包。在 TCP 协议中,我们把每个分块称为一个 TCP 段(TCP Segment)。
传输层的报文中会携带端口号,因此接收方可以识别出该报文是发送给哪个应用。
3.网络层
IP协议
IP 协议会将传输层的报文作为数据部分,再加上 IP 包头组装成 IP 报文,如果 IP 报文大小超过 MTU(以太网中一般为 1500 字节)就会再次进行分片,得到一个即将发送到网络的 IP 报文。
IP 地址分成两种意义:
一个是网络号,负责标识该 IP 地址是属于哪个「子网」的;
一个是主机号,负责标识同一「子网」下的不同主机;
10.100.122.0/24,后面的/24表示就是 255.255.255.0 子网掩码,255.255.255.0 二进制是「11111111-11111111-11111111-00000000」,一共是 24 个1,为了简化子网掩码的表示,用/24代替255.255.255.0。
将 10.100.122.2 和 255.255.255.0 进行按位与运算,就可以得到网络号
将 255.255.255.0 取反后与IP地址进行进行按位与运算,就可以得到主机号。
除了寻址能力, IP 协议还有另一个重要的能力就是路由。
IP 协议的寻址作用是告诉我们去往下一个目的地该朝哪个方向走,路由则是根据「下一个目的地」选择路径。寻址更像在导航,路由更像在操作方向盘。
相当于说IP寻址是找寻目的地具体位置,路由是到达目的地走哪条路
4.网络接口层
在 IP 头部的前面加上 MAC 头部,并封装成数据帧(Data frame)发送到网络上。
以太网在判断网络包目的地时和 IP 的方式不同,因此必须采用相匹配的方式才能在以太网中将包发往目的地,而 MAC 头部就是干这个用的,所以,在以太网进行通讯要用到 MAC 地址。
MAC 头部是以太网使用的头部,它包含了接收方和发送方的 MAC 地址等信息,我们可以通过 ARP 协议获取对方的 MAC 地址。
所以说,网络接口层主要为网络层提供「链路级别」传输的服务,负责在以太网、WiFi 这样的底层网络上发送原始数据包,工作在网卡这个层次,使用 MAC 地址来标识网络上的设备。
键入网址到网页显示,期间发生了什么?
浏览器先对域名进行解析,
再通过
浏览器缓存 -> 操作系统缓存 -> hosts文件 -> 本地DNS服务器 -> 根DNS服务器 ->.com顶级域名服务器 -> 权威域名服务器 -> IP地址
寻找到IP地址。
本地 DNS 再将 IP 地址返回客户端,客户端和目标通过三次握手建立连接。
通过 DNS 获取到 IP 后,就可以把 HTTP 的传输工作交给操作系统中的协议栈。
随后HTTP报文需要依次组装TCP头部,IP头部和MAC头部来形成网络包。
之后需要使用网卡,网络包只是存放在内存中的一串二进制数字信息,没有办法直接发送给对方。因此需要将数字信息转换为电信号,才能在网线上传输。网卡驱动获取网络包之后,会将其复制到网卡内的缓存区中,接着会在其开头加上报头和起始帧分界符,在末尾加上用于检测错误的帧校验序列。最后网卡会将包转为电信号,通过网线发送出去。
接着经过交换机,交换机根据 MAC 地址表查找 MAC 地址,然后将信号发送到相应的端口。再经过路由器(MAC 头部的作用就是将包送达路由器,此后MAC头部被丢弃,然后组装上新的MAC头部),接下来,路由器会根据 MAC 头部后方的 IP 头部中的内容进行包的转发操作,转发会查询路由表。
发送后会经过一些交换机和路由器最后到达终点。在此过程中,源 IP 和目标 IP 始终是不会变的,一直变化的是 MAC 地址,因为需要 MAC 地址在以太网内进行两个设备之间的包传输。
数据包抵达了服务器后,服务器开始拆包。
然后把客户端需要的页面封装在 HTTP 响应报文里。
客户端收到的数据包后同样开始拆包,得到 HTTP 响应报文后,交给浏览器去渲染页面,网页就可以正常显示了。
最后,客户端向服务器发起了 TCP 四次挥手,双方的连接断开。
Linux 系统是如何收发网络包的?
1.Linux收包:网卡收到网络包之后会通过 DMA 技术将网络包写入RingBuffer环形缓冲区,接着网卡向 CPU 发起硬件中断,当 CPU 收到硬件中断请求后,根据中断表,调用已经注册的中断处理函数。硬件中断函数先暂时屏蔽中断(下一次就直接写入内存而不通知CPU),再发起软中断,软中断调用ksoftirqd 线程从 Ring Buffer 中获取一个数据帧,用sk_buffer(socket_buffer,一种数据类型)表示,然后由网络协议栈处理,先进入网络接口层,去掉帧头和帧尾,再进入网络层,去掉IP头部,接着进入传输层去掉TCP 头或 UDP 头,根据四元组「源 IP、目的 IP、源端口、目的端口」 作为标识,找出对应的 Socket,并把数据放到 Socket 的接收缓冲区。最后,应用层程序调用 Socket 接口,将缓冲区的数据「拷贝」到应用层的缓冲区,然后唤醒用户进程。
2.Linux发包:应用程序会调用 Socket 发送数据包的接口,内核会申请一个 sk_buff 内存,将用户待发送的数据拷贝到 sk_buff 内存,并将其加入到发送缓冲区,然后网络协议栈从 Socket 发送缓冲区中取出 sk_buff,先进入传输层,如果使用的是 TCP 协议发送数据,会先拷贝一个sk_buff 副本 ,然后为副本添加TCP头部,本来的sk_buff就留在传输层,接着sk_buff副本在网络层添加上IP头部,然后在网络接口层添加上帧头(帧头包括了MAC头部)和帧尾,最后将 sk_buff 放到网卡的发送队列中。随后会触发「软中断」告诉网卡驱动程序,驱动程序会从发送队列中读取 sk_buff,将这个 sk_buff 挂到 Ring Buffer 中,接着将 sk_buff 数据映射到网卡可访问的内存 DMA 区域,最后触发真实的发送。当数据发送完成以后,网卡会触发一个硬中断来释放内存,主要是释放 sk_buff 内存和清理 Ring Buffer 内存。
发送网络数据的时候,涉及几次内存拷贝操作?
两次或三次。
第一次:调用发送数据的系统调用的时候,内核会申请一个内核态的 sk_buff 内存,将用户待发送的数据拷贝到 sk_buff 内存,并将其加入到发送缓冲区。
第二次:在使用 TCP 传输协议的情况下,从传输层进入网络层的时候,每一个 sk_buff 都会被克隆一个新的副本出来。副本 sk_buff 会被送往网络层,等它发送完的时候就会释放掉,然后原始的 sk_buff 还保留在传输层,等收到这个数据包的 ACK 时,才会释放原始的 sk_buff 。
第三次:当 IP 层发现 sk_buff 大于 MTU 时才需要进行。会再申请额外的 sk_buff,并将原来的 sk_buff 拷贝为多个小的 sk_buff。