【Linux网络】网络基础_协议、网络传输基本流程
hello~ 很高兴见到大家! 这次带来的是Linux网络中关于网络基础这部分的一些知识点,如果对你有所帮助的话,可否留下你宝贵的三连呢?
个 人 主 页: 默|笙

文章目录
一、OSI七层模型和TCP/IP五层(或四层)模型
1.1 通过网络通信所面临的问题

- 想要依托网络完成两台主机间通信,需要解决传输距离变长引发的各类问题:
- 想要远距离传输数据,先要实现相邻主机间的通信;只要打通近距离通信,远距离传输便可分段接力完成,毕竟远距离链路本就是由多段近距离链路拼接而成。—— 解决物理传输链路的问题。
- 链路搭建完成后,还需要寻址定位目标主机;即便有路,若无行进路线指引也无法送达,需要在多条链路里筛选出合适的传输路径。—— 实现目标主机寻址与路由选择。
- 数据传输途中发生丢失如何处理也是一大难题,数据丢失后需要触发重传机制补发数据。
- 接收数据并非通信最终目的,上层应用需要解析、使用报文数据,如何交付并解析数据同样需要配套方案。
- 对于计算机内部各硬件配件间的通信,基本不存在上述网络传输问题,原因是元器件传输距离极短,通信在主板与芯片内部即可完成。
1.2 解决方法—TCP/IP协议

- 实现网络通信需要攻克前述四项问题,OSI 七层模型便是从逻辑层面针对性给出的解决方案。不过实际落地的协议体系和 OSI 逻辑模型存在区别,工程中将 OSI 的应用层、表示层、会话层合并为单一应用层,七层架构简化为五层;而我们不探究物理层,仅学习其上四层:数据链路层、网络层、传输层、应用层。
- 四层分层恰好对应解决之前的四类问题:数据链路层负责相邻主机间的数据传输,网络层实现主机寻址与路由选择,传输层处理数据丢包重传问题,应用层负责数据解析与上层业务使用。
- 四层协议里的核心是传输层与网络层,这两层都在操作系统里面(通常由C/C++语言编写),传输层最具代表性的协议为 TCP,网络层的核心协议是 IP,这套协议体系因此被称作 TCP/IP 协议簇。TCP/IP协议簇之所以能够分层,是因为要解决的问题是分层的。
- 协议本质可以理解为预先定义好的数据结构体,通信双方依照统一的结构体封装数据,以此规范数据通信格式。两台不同主机使用同一套网络协议,确保双方都能识别这套数据结构,这是主机之间正常通信的基础。
二、网络传输基本流程
2.1 局域网(LAN)通信
1. 了解Mac地址(物理/硬件地址)

- 如果两台主机在同一个局域网里面,它们能否直接进行通信?答案是可以的。它就好比一个教室里面老师出声让某个人站起来,老师就是发出信息的那个主机,目的主机就是要站起来的那个学生,教室里面的所有学生就是这个局域网里面所有的主机,教室里所有学生都能接收到教师的这条喊话信息,但是只有被叫到名字的同学会起身回应,其他同学虽然听到了消息,但是不会做出任何反应。这就是模拟局域网主机之间的通信过程。
- 在这片局域网里面,每个主机配有两种标识:IP 相当于主机名字,而Mac 是主机独有的硬件编号,这个硬件编号在网卡出厂时就已经设置好,它通常具有唯一性,这个 Mac 地址依靠 Mac 地址在局域网内完成数据寻址。
- 局域网:按地理覆盖范围划分的网络类型(概念:范围),以太网:数据链路层的组网技术标准(概念:协议规范)—>以太网是用来搭建局域网最常用的工具,但工具≠成品。
2. 数据的封装和解包

- 要完成数据的传输工作,就必须进行数据的封装和解封装工作。封装时,上层有效载荷向下递交,下层通常在数据前端添加本层协议报头,部分协议如以太网还会在尾部追加校验字段,封装完毕组成新载荷继续向下传输;解封装的时候数据逐层向上,每层剔除自身对应的头部与尾部字段,直到将原始数据传递给接收方。
- 我们上面提到过协议的本质就是一个结构体,而这个协议报头的本质就是一个结构体变量,它里面存储的各类字段信息,用来解决网络通信过程中寻址、校验等各类通信问题。
- 解封装过程中需要确定剔除头部的字节长度,以及判断剥离头部后的数据该交付给上层哪个协议,相关信息都存放在协议报头中;部分协议首部长度固定,可变长度的协议依靠报头内的长度字段确定头部大小,依靠报头中的协议类型字段区分上层协议。
- 同层级的对等协议实体,逻辑上认为彼此在直接通信,实际数据需要经过下层逐层封装、物理链路传输、接收端逐层解封装才能抵达对端同层。
- 报文 = 报头 + 有效载荷。
2.2 跨网络传输
1. 理解源ip地址和目的ip地址

- IP 地址是什么?它和Mac地址之间有什么关系?而且为什么会用到两个地址?
- IP 地址:标记全网两端最终源主机、最终目的主机(端到端),负责跨网段、跨地域寻址,全球 / 局域网逻辑编号,可修改。IPv4 格式:192.168.1.100,由网络位 + 主机位组成,路由依靠 IP 查表跨地区转发。
- MAC 地址:标记同一网段内相邻两个直连设备(一跳之间),仅在单个局域网链路内生效,烧录在网卡硬件,出厂固定、一般不随网络环境变更。
- 这个也可以打一个恰当的比方,如果我要从黑龙江到陕西,出发点是黑龙江,目的地是陕西。但是我不会一下子直接到陕西,而是先从黑龙江到吉林,再从吉林到辽宁……,IP 地址用来标记原始出发点黑龙江和最终总目的地陕西,全程不会发生变化;而 Mac 地址则只记录当前路段的出发站点与紧邻下一站目的地,每换乘一段路程,两端 Mac 地址就随之更改(解决局域网之间转发的问题)。
2. 传输过程

- 那么数据究竟是如何通过网络传输的呢?首先上层数据向下递交至网络层,封装头部,填入源 IP、目的 IP;再往下交到数据链路层,封装二层帧头,填入源 MAC 与目的 MAC,依靠二层地址实现相邻设备间的数据投递。主机首先对比本机 IP 与目标 IP,判断目标主机是否处在同一个局域网:如果目的 IP 属于本局域网网段,主机通过 ARP 获取对方 MAC,封装帧后经交换机直接转发至目标设备;如果目的 IP 不在本地局域网,报文就会转发给网关(路由器内网接口),交由路由器处理。
- 而路由离不开路由器,数据跨网段传输离不开核心网络设备 —— 路由器。路由器收到数据报文后,先剥离三层 IP 报文外的数据链路层头部(解封装),读取 IP 首部中的目标 IP 地址完成路由查表选路,再根据出接口的链路类型,重新封装对应数据链路层帧头,从选定端口转发至下一跳设备。路由器就如同跨区域快递中转站,能够对封装好的 “快递包裹”(数据报文)做链路层格式改造。不同物理网络采用的数据链路标准互不相同:以太网设备的网卡驱动仅能识别以太网帧首部,令牌环网络的硬件驱动只兼容令牌环帧结构,二者链路帧格式、寻址方式、封装规则完全不一样,原始报文无法在异构链路中直接转发。路由器依托三层 IP 协议屏蔽下层链路差异,通过拆旧链路头、封装新链路头的格式转换,打通以太网、令牌环等不同类型局域网之间的数据通路,最终实现跨网段终端的正常网络通信。----隔离物理链路差异,这正是数据链路层的设计初衷。

- 所以真正的跨网段网络传输正如上图所示:源 IP 与目的 IP 全程保持不变,每经过一跳路由 MAC 地址都会重新封装变更,依靠不断更新的 MAC 地址将数据递交给相邻路由器,一步步转发,最终送达目的 IP 对应的主机。
三、Socket编程预备
3.1 认识端口号(port)
-
让目标主机收到报文并非传输的最终目的,最终目的是对应应用程序接收并使用数据。一台主机往往同时运行 QQ、浏览器、微信等多个进程,操作系统依靠端口号区分报文,把收到的数据精准交付给对应进程。

-
端口号是占用 2 字节(16 位)的无符号整数,用于在主机内唯一标识应用进程,该字段封装在传输层头部;数据抵达目标主机后,逐层向上解封装,传输层剥离头部,操作系统便可提取端口号完成数据分发。
-
网络通信选用端口而非操作系统 PID 标识进程,核心原因是解耦:PID 由操作系统动态分配,进程重启、设备重启后 PID 数值都会发生改变,无法固定用来做网络寻址;端口可由应用程序固定绑定,让网络协议与系统进程管理相互独立,降低耦合。除此之外,并非所有进程都具备网络收发数据的需求,只用端口专门管理联网进程,资源分配更高效。
-
所以IP 地址 + 端口号(port)组成套接字(Socket),就能精准定位全网唯一的目标进程。
-
端口号作为哈希表的键,对应的进程 PCB 作为值保存在哈希表中,依靠该哈希表即可由端口查询到所属进程。
-
找到了进程,那么如何将从网络得到的数据交给这个进程呢?找到进程后,操作系统会将网络收到的数据放进该进程所打开套接字对应的内核缓冲区中。
端口号 0~1023:公认知名端口,HTTP、FTP、SSH 等常用应用层协议占用该区间固定端口,由国际机构统一规定,是服务器默认监听端口。
端口号1024~65535:动态 / 私有端口该区间多用于客户端,客户端发起网络连接时,由操作系统从中随机分配临时端口。
3.2 网络字节序
- 内存中的多字节数据在存储时,相对于内存高低地址存在大端、小端之分。例如存储十六进制整数0x1234,该数据占用两个字节,0x12为高字节、0x34为低字节:小端模式下,低地址存低字节、高地址存高字节;大端模式与之相反,低地址存高字节、高地址存低字节。
- 早期计算机系统研发阶段,业界没有统一规定多字节数据的存储字节序,因此不同处理器架构分别采用大端或小端存储格式。而主机通过网络发送报文时,默认依照内存低地址→高地址顺序依次发送字节,先发低地址存储的数据;接收方收到字节后,同样从内存低地址开始依次存放。若收发两端字节序不一致,解析多字节数值就会出现数据错乱,因此只有同字节序设备才能直接正常通信。
- 为规避跨平台数据错乱问题,TCP/IP 协议统一规定:网络传输固定使用大端序(网络字节序)。发送端若本机为小端主机,需要先把主机小端数据转换为大端再发送;小端主机接收网络数据后,再将大端格式的数据转回本机小端格式,保证数据正确解析。
3.3 socket编程接口
// 创建 socket ⽂件描述符 (TCP/UDP, 客⼾端 + 服务器)
int socket(int domain, int type, int protocol);
// 绑定端⼝号 (TCP/UDP, 服务器)
int bind(int socket, const struct sockaddr *address, socklen_t address_len);
// 开始监听socket (TCP, 服务器)
int listen(int socket, int backlog);
// 接收请求 (TCP, 服务器)
int accept(int socket, struct sockaddr* address, socklen_t* address_len);
// 建⽴连接 (TCP, 客⼾端)
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
- 传输层、网络层内核协议栈隶属于操作系统内核,应用程序若要和操作系统内核交互,需要调用操作系统提供的系统调用接口,网络编程同样遵循该规则。

- 网络编程的设计者希望同时支持网络通信与本地进程间通信,因此设计了统一的地址结构体系。
- 首先要了解 struct sockaddr 结构体,它可以被理解为一个通用基类,用于给系统调用(如 bind、connect)提供统一的参数类型。在此基础上又派生出两个专用结构体(类似继承类):struct sockaddr_in 和 struct sockaddr_un。其中,sockaddr_in 用于网络通信(存放 IP + 端口),sockaddr_un 用于本地(UNIX 域)进程间通信。在实际使用时,都会将这两个结构体强制类型转换为 struct sockaddr* 再传入内核接口。
今天的分享就到此结束啦,如果对读者朋友们有所帮助的话,可否留下宝贵的三连呢~~
让我们共同努力, 一起走下去!
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)