强推Linux高性能服务器编程, 真的是后端开发技术提升, 沉淀自身不容错过的一本经典书籍

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介: 强推Linux高性能服务器编程, 真的是后端开发技术提升, 沉淀自身不容错过的一本经典书籍

第1章 TCP/IP协议

1.1 TCP/IP协议族体系结构以及主要协议

TCP/IP协议栈是一个四层协议, 由下而上分别是数据链路层, 网络层, 传输层, 应用层, 上层协议使用下层协议提供的服务.  下三层处在内核态中, 应用层处在用户空间中.

1.1.1 数据链路层

链路层功能: 屏蔽物理层的电气差异, 为上层提供统一服务接口


数据链路层实现了网卡接口的网络驱动程序, 处理数据在不同的物理媒介上的传输


不同的物理网络具有不同的电气特性,网络驱动程序隐藏了这些细节,为上层协议提供一个统一的接口。    下层协议为上层提供统一的接口服务, 隐藏屏蔽掉物理层的不同电气差别.


链路层常用协议: ARP协议 (地址解析协议), RARP协议(逆地址解析协议)


功能: ARP 将IP地址转换为物理MAC地址.    RARP将MAC地址转换为IP地址  

在网络层使用IP地址唯一标识一台主机, 在链路层使用MAC地址唯一标识一台主机

因此网络层必须先将目标机器的IP地址转化成其物理地址,才能使用数据链路层提供的服务

1.1.2 网络层

网络层功能:网络层实现数据包的选路和转发


通常使用众多分级的路由器来连接分散的主机或LAN (局域网), 往往路由器就作为局域网的网关. (借助海关来理解).   每一个路由器都相当于是一个中间结点.


网络层的任务就是选择这些中间节点(路由),以确定两台主机之间的通信路径.


网络层对 上层协议隐藏了网络拓扑连接的细节,使得在传输层和网络应用程序看来,通信的双方是直接相连的。     (为何要分层, 优势出来了, 不同层处理不同的通信细节问题, 分化分工, 使得问题简化, 下层协议为上层协议提供简单易用的接口, 隐藏下层处理的细节问题)


网络层重要协议:IP协议,  ICMP协议  (探路小兵, 主要用来检测连接的)


IP协议根据数据包的目的IP地址来决定如何投递它,如果数据包不能直接发送给目标主机,那么IP协议就为它寻找一个合适的下一跳(next hop)路由器,并将数据包交付给该路由器来转发


数据包是在网络环境中, 一跳一跳的传输. TTL数据包的最长生存时间, 单位也是hop, 跳过TTL还未到达目标主机, 就默认发送失败, 丢弃数据包了, 并且发送ICMP差错报文给源端.

ICMP报文分为差错报文 (回应网络错误) eg: 上述的数据包不可达(3), 重定向(5)


查询报文(ping查询目标是否可达, 网络是否连接)(8),  所以存在类型来区分他们.


ICMP报文还使用8位代码字段来进一步细分不同的条件


比如重定向报文使用代码值0表示对网络重定向,代码值1表示对主机重定向


ICMP报文使用16位校验和字段 对整个报文(包括头部和内容部分)进行循环冗余校验(Cyclic Redundancy Check,CRC),以检验报文在传输过程中是否损坏

1.1.3 传输层

传输层只管端到端的传输, 传输层只关心通信的起始端和目的端,而不在乎数据包的中转过程。

在传输层看来, 两端好似是直接通信的, 实际上中间的中转细节是由网络层处理的   (下层对上层隐藏细节,让上层协议变得尽可能看起来很简单.)

实线箭头表示TCP/IP协议族各层之间的实体通信 (数据包确实是沿着这些线路传递的),而水平的虚线箭头表示逻辑通信线路.


该图中还附带描述了不同物理网络的连接方法。可见,数据链路层(驱动程序)封装了物理网络的电气细节


传输层则为应用程序封装了一条端到端的逻辑通信链路,它负责数据的收发、链路的超时重连等


下层都是在为上层提供服务, 提供便捷, 隐藏细节.


传输层重点协议: TCP, UDP.


TCP是可靠传输协议, 面向连接, 基于流.


可靠来源: 超时重传, 排序号, 确认应答等可靠机制


使用TCP协议通信的双方必须先建立TCP连接, 并在内核中为该连接维持一些必要的数据结构,比如连接的状态、读写缓冲区,以及诸多定时器等.    (超时重传, 确认应答这些机制的实现必然携带着一些内核数据结构. )


当通信结束时,双方必须关闭连接以释放这些内核数据。 ( 连接关闭细节,内核数据结构的释放, 避免内存泄漏 )


UDP协议(User Datagram Protocol,用户数据报协议)则与TCP协议完全相反,它为应用层提供不可靠、无连接和基于数据报的服务


不可靠是因为什么?   因为如果数据在中途丢失,或者目的端通过数据校验发现数据错误而将其丢弃,则UDP协议只是简单地通知应用程序发送失败     (不会重传, 不会确认应答. 所以不可靠)


所以UDP传输的使用往往需要自己处理数据确认、超时重传等逻辑  (自定义UDP可靠传输协议.在应用层书写)


UDP是无连接的, 所以每一次传输数据都需要指定对端的地址 ip + port


基于数据报的服务,是相对基于流的服务而言的。每个UDP数据报都有一个长度,接收端必须以该长度为最小单位将其所有内容一次性读出,否则数据将被截断    


(数据报传输,每次都按照一个包裹传输, 包裹为最小传输单位, 大于包裹数据截断)

1.1.4 应用层

应用层负责处理应用程序的逻辑                                       (用户空间应用程序)


数据链路层、网络层和传输层负责处理网络通信细节       (内核数据结构, 稳定, 安全, 高效)


?为啥不把应用层服务也全部放入到内核态中                  (逻辑众多,会使内核变得非常庞大)


少数服务器程序是在内核中实现的?                                 (省去拷贝的代价, 提高效率,但是代码逻辑复杂, 不便于移植,书写应用)


应用层重点协议,应用程序列举


ping是应用程序,而不是协议,前面说过它利用ICMP报文检测网络连接,是调试网络环境的必备工具

DNS(Domain Name Service,域名服务)协议提供机器域名到IP 地址的转换,我们将在后面简要介绍DNS协议

应用层协议(或程序)可能跳过传输层直接使用网络层提供的服务,比如ping程序


应用层协议(或程序)通常既可以使 用TCP服务,又可以使用UDP服务,比如DNS协议。


我们可以通 过/etc/services文件查看所有知名的应用层协议,以及它们都能使用哪些传输层服务

1.2 封装

前文一直都在说上层协议使用下层协议提供的服务, 但是究竟是如何使用上的呢?  封装


应用程序数据在发送到物理网络上之前, 将沿着内核协议栈从上往下依次传递  (封装, 封包)


每层协议都将在上层数据的基础上加上自己的头部信息(有时还包括尾部信息),以实现该层的功能, 这个过程就称为封装

每一层都可以进行DDOS攻击.  洪水猛兽.  网络层(可以不断发送连接请求, 或者不断发送fin包.)


DDOS很有意思, 而且暂时没有完全的解决方案, 大致做法就是, 不断地发送无效请求, 占据服务器, 让真正需要使用服务器地人无法建立连接, 或者无法使用.    


解决方法:前面放一台大型地壁垒机器, 过滤无效请求.        (扯多了, 插曲)


经过TCP封装后的数据称为TCP报文段(TCP message segment), 或者简称TCP段。


TCP协议为通信双方维持一个连接,并且在内核中存储相关数据          (内核数据结构:属于内核协议栈)

深入内核协议栈数据结构去看这个封装过程.  


发送地时候是 tcp header + tcp 内核sendbuffer   接收地时候是   tcp header + tcp 内核recvbuffer


send + write数据, 是先写到内核协议栈的传输层的tcp sendbuffer中, 然后经有内核协议栈层层向下封装, 经由网卡接口驱动程序将其放到物理网络传输层 (网线, 电气传输)


经过UDP封装后的数据称为UDP数据报(UDP datagram)。UDP 对应用程序数据的封装与TCP类似。不同的是,UDP无须为应用层数据保存副本,因为它提供的服务是不可靠的.     (不保存副本在内核态意味着不可靠, 不考虑重传的问题)


当一个UDP数据报被成功发送之后,UDP内核缓冲区中的该数据报就被丢弃了。如果应用程序检测到该数据报未能被接收端正确接收,并打算重发这个数据报,则应用程序需要重新从用户空间将该数据报拷贝到UDP内核发送缓冲区中。


经过IP封装后的数据称为IP数据报(IP datagram)


经过数据链路层封装的数据称为帧(frame)。传输媒介不同,帧 的类型也不同

帧才是最终在物理网络上传送的字节序列

1.3 分用

当帧到达目的主机时,将沿着协议栈自底向上依次传递。各层协议依次处理帧中本层负责的头部数据,以获取所需的信息,并最终将 处理后的帧交给目标应用程序。这个过程称为分用

上层协议那么多, 所以分用的一个核心问题在于如何知道分用后应该交付给哪个上层协议.


掐去头部之后的身子应该交付给谁?


每一层的头部中都会存在这样一个字段可以标识上层协议进行交付


eg : IP协议, ARP协议, RAPR协议都使用帧传输数据,所以帧的头部需要提供某个字段(具体情况取决于帧的类型)来区分它们,帧给出的是帧头部的类型字段进行区分


eg : 以太网帧类型字段的值为0x800,则帧的数据部分为IP数据报


同样,因为ICMP协议、TCP协议和UDP协议都使用IP协议,所以 IP数据报的头部采用16位的协议(protocol)字段来区分它们。


TCP报文段和UDP数据报则通过其头部中的16位的端口号(port number)字段来区分上层应用程序。所有知名应用层协议使用的端口号都可在 /etc/services 文件中找到。


帧通过上述分用步骤后,最终将封装前的原始数据送至目标服务

1.5 ARP协议工作原理

主机向自己所在的网络广播一个ARP请求,该请求包含目标机器 的网络地址。此网络上的其他机器都将收到这个请求,但只有被请求 的目标机器会回应一个ARP应答,其中包含自己的物理地址


核心原理: 广播, 目标MAC ff:ff:ff:ff:ff:ff       同网段的所有主机广播, 对比发现ip等于目标ip的机器,填写自己的MAC物理地址并且发送ack应答包

1.5.1 以太网ARP请求/应答报文详解

硬件地址长度字段和协议地址长度字段,顾名思义,其单位是字节。对MAC地址来说,其长度为6;对IP(v4)地址来说,其长度为 4。


操作字段指出4种操作类型:ARP请求(值为1)、ARP应答(值 为2)、RARP请求(值为3)和RARP应答(值为4)。


最后4个字段指定通信双方的以太网地址和IP地址。发送端填充除目的端以太网地址外的其他3个字段,以构建ARP请求并发送之。接 收端发现该请求的目的端IP地址是自己,就把自己的以太网地址填进 去,然后交换两个目的端地址和两个发送端地址,以构建ARP应答并返回之(当然,如前所述,操作字段需要设置为2)。

1.5.2 ARP高速缓存的查看和修改

通常,ARP维护一个高速缓存,其中包含经常访问(比如网关地址)或最近访问的机器的IP地址到物理地址的映射。这样就避免了重复的ARP请求,提高了发送数据包的速度。


Linux下可以使用arp命令来查看和修改ARP高速缓存。


arp  -a 是查看ARP 缓存内容  

arp  -d  ip   是删除ARP缓存内容

arp  -s  ip  MAC  是添加  ARP缓存内容

1.5.3 使用tcpdump观察ARP通信过程所得结果如下

注意点:


ARP请求和应答是从以太网驱动程序发出的,而并非像图 中描述的那样从ARP模块直接发送到以太网上,所以我们将它们用虚 线表示,这主要是为了体现携带ARP数据的以太网帧和其他以太网帧 (比如携带IP数据报的以太网帧)的区别。


路由器也将接收到以太网帧1,因为该帧是一个广播帧。不 过很显然,路由器并没有回应其中的ARP请求,正如前文讨论的那样


相关文章
|
11天前
|
Shell Linux
Linux shell编程学习笔记30:打造彩色的选项菜单
Linux shell编程学习笔记30:打造彩色的选项菜单
|
11天前
|
Shell Linux
Linux shell编程学习笔记82:w命令——一览无余
Linux shell编程学习笔记82:w命令——一览无余
|
26天前
|
Linux Shell
Linux系统编程:掌握popen函数的使用
记得在使用完 `popen`打开的流后,总是使用 `pclose`来正确关闭它,并回收资源。这种做法符合良好的编程习惯,有助于保持程序的健壮性和稳定性。
23 6
|
15天前
|
Shell Linux Python
python执行linux系统命令的几种方法(python3经典编程案例)
文章介绍了多种使用Python执行Linux系统命令的方法,包括使用os模块的不同函数以及subprocess模块来调用shell命令并处理其输出。
15 0
|
Oracle 算法 搜索推荐
C/C++/Linux/PostgreSQL 高性能服务器开发技术博文分类汇总(持续更新...)
C/C++/Linux/PostgreSQL 高性能服务器开发技术博文分类汇总(持续更新...)
235 0
C/C++/Linux/PostgreSQL 高性能服务器开发技术博文分类汇总(持续更新...)
|
3天前
|
Linux
Linux的cp命令如何使用?
Linux的cp命令如何使用?
11 5
|
7天前
|
Linux
Linux常用命令包括
Linux常用命令包括
16 5
|
7天前
|
Linux
Linux命令
Linux命令
18 5
|
3天前
|
安全 Linux Shell
常见的 Linux 命令大全(表格形式)
常见的 Linux 命令大全(表格形式)
|
11天前
|
Linux Python Perl
Linux命令删除文件里的字符串
Linux命令删除文件里的字符串
26 7