尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
【Linux网络】网络基础下篇:数据到底怎么从进程 A 到进程 B?从 MAC、IP、端口到 Socket 一次理清
个人主页爱和冰阔乐专栏传送门《数据结构与算法》 、C、 《Linux操作系统》学习方向C方向学习爱好者⭐人生格言得知坦然 失之淡然博主简介文章目录前言一、局域网通信的基本过程1.1 认识 MAC 地址1.2 以太网是共享资源1.3 局域网内怎样找到目标主机二、数据封装、解包与分用2.1 报头和有效载荷2.2 封装为什么是自顶向下2.3 接收端为什么要解包2.4 分用数据应该交给谁三、跨网络传输IP 地址与 MAC 地址3.1 为什么只靠 MAC 地址不够3.2 IP 是长期目标MAC 是下一站3.3 路由器怎样连接不同网络3.4 IP 层屏蔽底层网络差异四、网络通信为什么最终是进程通信4.1 IP 地址只能找到主机4.2 端口号负责标识进程4.3 端口号与进程 ID 的区别4.4 Socket 是什么五、TCP 与 UDP 的直观认识5.1 TCP5.2 UDP六、网络字节序6.1 大端与小端6.2 常用转换函数七、Socket 编程中的核心接口7.1 socket创建通信端点7.2 bind绑定本地地址7.3 listen把 TCP Socket 设为监听状态7.4 accept获取已经建立的连接7.5 connect客户端主动连接服务器八、为什么接口统一使用 sockaddr8.1 不同协议的地址结构不同8.2 sockaddr_in 的主要字段8.3 字符串 IP 与二进制地址九、把完整流程串起来总结前言上一篇建立了协议分层和 TCP/IP 模型的整体认识本篇继续追踪一份数据从发送主机到接收主机的完整过程。我们先看局域网中的数据怎样通过 MAC 地址找到目标网卡再看数据跨网络时 IP 地址和路由器分别做什么。最后把网络编程前经常混在一起的几个概念讲清楚端口号、进程 ID、Socket、TCP/UDP、网络字节序以及sockaddr_in地址结构。这些内容看起来很多其实围绕的是同一个问题数据怎样从一台主机中的某个进程准确交付到另一台主机中的某个进程。一、局域网通信的基本过程1.1 认识 MAC 地址MAC 地址用于局域网内部识别网络接口。常见 MAC 地址长度为 48 位一般写成六组十六进制数例如08:00:27:03:fb:19物理网卡通常在出厂时写入 MAC 地址。虚拟机、容器和部分网卡也允许软件配置因此学习时更准确的说法是MAC 地址用于链路层标识网络接口通常具有唯一性。在 Windows 中可以使用下面的命令查看网卡信息ipconfig /allIP 地址回答“最终去哪里”MAC 地址回答“当前这一跳交给谁”。两者都和地址有关但工作的范围不同。1.2 以太网是共享资源早期共享式以太网中多台主机使用同一传输介质。某一时刻如果有多台主机同时发送数据信号会互相干扰形成数据碰撞。这和教室里多人同时说话很像。一个人讲话时大家能听清多人同时喊接收者就难以分辨。共享介质因此需要碰撞检测和避免机制。从并发角度看以太网传输介质也是一种共享资源。多台主机都想发送数据但同一时刻只能让一方成功占用。交换机可以把大的碰撞域拆开让不同端口之间独立转发现代交换式以太网已经大幅减少了传统共享介质中的碰撞问题。无线局域网虽然介质不同但同样存在多个设备竞争信道的问题。设备越多冲突和等待越明显这也是高密度环境下无线网络可能变慢的原因之一。1.3 局域网内怎样找到目标主机同一局域网中的数据帧会携带源 MAC 地址和目的 MAC 地址。网卡收到帧后会判断目的 MAC 是否与自己匹配是发给自己的继续向上交付不是发给自己的通常直接丢弃广播帧则由局域网内符合条件的主机接收处理。站在用户角度主机 A 像是在直接给主机 B 发消息。站在系统角度应用数据先从 A 的协议栈自顶向下交给网卡经过局域网到达 B 的网卡再由 B 的协议栈自底向上交给应用程序。主机之间通信就是两套协议栈按照相同规则处理数据。局域网通信时数据先穿过本机协议栈再由对端协议栈逐层接收应用程序不会直接控制远端网卡。二、数据封装、解包与分用2.1 报头和有效载荷用户只关心自己要发送的内容但网络中的每层协议还需要携带控制信息。当前层添加的控制信息称为报头报头之外的数据称为有效载荷。可以把它看成寄快递。商品是我们关心的内容包装和快递单记录运输所需的信息。没有这些信息包裹无法被正确处理。对某一层协议来说完整报文 当前层报头 当前层有效载荷应用层数据交给传输层后传输层添加自己的报头传输层报文交给网络层后网络层继续添加 IP 报头到数据链路层时还会添加链路层首部和尾部。2.2 封装为什么是自顶向下发送动作由应用程序发起而真正把比特送上介质的是底层网卡。应用程序不能直接控制远端网卡因此数据必须穿过本机协议栈逐层向下交付。数据每经过一层就会增加该层需要的报头。这个过程称为封装。发送端逐层添加报头叫封装接收端逐层分离报头和有效载荷叫解包。不同层对完整报文的称呼也不同应用层通常称为应用数据或消息传输层中TCP 报文常称为段网络层称为 IP 数据报数据链路层称为帧到物理层后表现为比特流或信号。2.3 接收端为什么要解包目标网卡收到数据帧后链路层先读取自己的报头判断帧是否应该接收。确认无误后链路层去掉自己的首部和尾部把剩余有效载荷交给网络层。网络层继续解析 IP 报头再把有效载荷交给传输层传输层解析端口等信息最终把数据交给应用进程。逐层把报头和有效载荷分开的过程叫作解包。一层协议要完成解包报文格式中就必须有办法确定报头长度和有效载荷边界。固定长度报头可以直接按字节数切分可变长度报头则需要专门字段记录长度。2.4 分用数据应该交给谁每一层的上方都可能存在多个协议。例如网络层上方可能是 TCP也可能是 UDP传输层上方又有很多应用进程。协议在解包之后还要根据报头中的字段判断有效载荷应该交给谁这个过程称为分用。后面学习任何协议都可以先问两个问题这个协议怎样区分报头和有效载荷这个协议怎样把有效载荷交给正确的上层对象把这两个问题弄清楚协议的报文格式就不再只是需要背诵的字段表。三、跨网络传输IP 地址与 MAC 地址3.1 为什么只靠 MAC 地址不够MAC 地址适合局域网内寻址但互联网由大量不同局域网组成。数据从一个网络到另一个网络需要一种能描述整体网络位置的地址这就是 IP 地址。IPv4 地址长度为 32 位通常使用点分十进制表示。192.168.1.10四个十进制数字分别表示一个字节每个数字的范围是 0255。公网 IPv4 地址在互联网范围内需要保持唯一。由于 IPv4 地址数量有限现实网络大量使用NAT网络地址转换把多个内网地址映射到一个或少量公网地址。内网 IP 可以在不同局域网中重复公网侧则通过转换后的地址完成通信。3.2 IP 是长期目标MAC 是下一站跨网络发送数据时目的 IP 表示最终目标目的 MAC 表示当前链路上的下一跳。例如主机 A 和主机 B 不在同一局域网。A 无法直接把帧交给 B 的网卡它会先把帧交给本地路由器IP 报头中的目的 IP 仍然是主机 B当前以太网帧中的目的 MAC 是本地路由器网卡。路由器收到帧后去掉原来的链路层报头查看目的 IP 并选择下一跳。准备从另一个接口发出时路由器会重新封装链路层报头因此 MAC 地址会随链路变化。可以先记住这个结论在不考虑 NAT 等情况时源 IP 和目的 IP 在转发过程中保持不变每经过一个新的链路源 MAC 和目的 MAC 都要重新填写。3.3 路由器怎样连接不同网络路由器至少要有两个网络接口每个接口接入一个网络。不同接口具有各自的 IP 和 MAC 地址。主机发送数据前会根据目的 IP 和本机网络配置判断目标是否在同一子网在同一子网直接寻找目标主机的链路层地址不在同一子网把数据交给默认网关由路由器继续转发。路由器在接收接口上解开链路层帧保留其中的 IP 数据报根据路由表选出发送接口和下一跳后再为新的链路重新封装数据帧。路由器的转发过程可以概括为解开旧链路封装依据目的 IP 路由再建立新链路封装。它不会把收到的以太网帧原样搬到另一个网络。3.4 IP 层屏蔽底层网络差异路由器两侧不一定使用相同的链路层技术。一侧可以是以太网另一侧也可以是其他网络。只要它们都能承载 IP 数据报上层就不必关心底层链路的具体差异。这就是 IP 层的重要价值它在各种底层网络之上提供统一的网络层视图。对传输层来说下面无论经过以太网、无线局域网还是其他链路看到的都是 IP 网络。IP 地址和 MAC 地址不能简单互相替代。把全局寻址与局部链路寻址分开可以让网络层和不同链路技术各自演进避免所有网卡和链路协议都被某一种网络层地址绑定。目的 IP 指向最终主机目的 MAC 指向当前链路的下一跳。四、网络通信为什么最终是进程通信4.1 IP 地址只能找到主机一台服务器上可能同时运行 Web 服务、数据库、SSH 服务和其他程序。数据到达主机以后如果操作系统只知道目标 IP还无法判断数据属于哪个进程。网络通信的两个端点是进程。网卡和协议栈负责传输最终仍要让发送进程和接收进程交换数据。4.2 端口号负责标识进程传输层使用端口号区分主机中的通信端点。端口号是一个 16 位无符号整数范围为 065535。数据到达传输层后操作系统根据报头中的目标端口号把数据放入对应 Socket 的接收队列再唤醒等待数据的进程。常见端口范围可以这样理解01023知名端口通常分配给常用服务例如 SSH 默认使用 22HTTP 默认使用 80102449151注册端口很多应用服务会在这个范围选择固定端口4915265535动态或临时端口客户端建立连接时常由操作系统临时分配。不同系统对临时端口范围的具体配置可能不同上面的划分用于建立基本认识。4.3 端口号与进程 ID 的区别进程 ID 和端口号都能在某种意义上标识进程但它们服务于不同场景。PID 用于操作系统内部管理进程端口号用于网络协议栈定位通信端点PID 由操作系统的进程管理模块分配端口号由网络程序主动绑定或由内核临时分配。不能直接用 PID 替代端口号。网络报文要在不同操作系统之间传输而远端并不认识本机的进程 IDPID 生命周期也短数值会被重复使用。端口号是 TCP/UDP 协议的一部分通信双方都能按照相同规则解析。通常情况下一个确定的 IP、传输层协议和端口组合只能由一个监听端点占用。不同协议可以在同一主机上使用相同数字端口例如 TCP 8080 和 UDP 8080 属于不同的通信端点。IP 地址负责找到主机端口号负责找到主机中的通信端点。两者缺少任何一个都不能完成准确的进程投递。4.4 Socket 是什么从网络地址角度看IP port可以确定一个通信端点。Socket 则是操作系统提供给进程的网络通信对象和编程接口。一个 TCP 连接通常由下面的四元组唯一描述源 IP 源端口 目的 IP 目的端口服务器可以在一个固定端口上接受多个客户端连接因为每个客户端的源 IP 和源端口不同最终形成的四元组也不同。五、TCP 与 UDP 的直观认识5.1 TCPTCP 是面向连接的传输层协议。通信前要先建立连接通信过程中会进行确认、重传、排序、流量控制和拥塞控制向应用层提供可靠的字节流。“可靠”并不代表数据永远不会丢。TCP 会发现丢失、重复和乱序并通过协议机制处理。应用程序看到的是一条连续字节流不需要自己为每个 IP 数据报处理重传。TCP 适合对完整性和顺序要求高的场景例如网页传输、远程登录和文件传输。5.2 UDPUDP 是无连接的传输层协议。发送数据前不建立连接也不保证数据一定到达、不重复或按顺序到达。它的报头更简单发送开销较小同时保留消息边界。实时音视频、局域网发现、DNS 查询等场景常使用 UDP应用层可以根据需要自己补充可靠性机制。初学时可以先这样区分TCP 更关注可靠、有序和连接状态UDP 更关注简单、直接和较低开销。具体选择不能只看“谁更快”还要看业务是否允许丢包、是否需要顺序、是否需要广播或多播以及应用层愿意承担多少控制逻辑。六、网络字节序6.1 大端与小端多字节整数在内存中有两种常见存放方式大端序高位字节放在低地址小端序低位字节放在低地址。不同主机可能采用不同的主机字节序。如果发送方直接把内存中的整数原样发出接收方按照另一种顺序解释就会得到错误数值。为了让不同主机能够统一解析网络协议规定使用大端序也叫网络字节序。程序在填写端口和地址字段时需要在主机字节序与网络字节序之间转换。网络字节序统一采用大端序主机中的整数进入网络报文前必须按接口要求完成转换。6.2 常用转换函数Linux 中常用四个函数#includearpa/inet.huint32_thtonl(uint32_thostlong);uint16_thtons(uint16_thostshort);uint32_tntohl(uint32_tnetlong);uint16_tntohs(uint16_tnetshort);函数名可以直接拆开记hhost主机nnetwork网络llong32 位整数sshort16 位整数。例如端口号是 16 位整数填写sockaddr_in时通常使用htonsserver.sin_porthtons(8080);从网络报文中取出端口号后如果需要按主机整数使用可以调用ntohs。七、Socket 编程中的核心接口7.1 socket创建通信端点#includesys/types.h#includesys/socket.hintsocket(intdomain,inttype,intprotocol);常见参数组合intsockfdsocket(AF_INET,SOCK_STREAM,0);// IPv4 TCPintsockfdsocket(AF_INET,SOCK_DGRAM,0);// IPv4 UDP调用成功返回文件描述符失败返回-1。Linux 把 Socket 也纳入文件描述符体系后续可以使用read、write也可以使用网络专用的send、recv等接口。7.2 bind绑定本地地址intbind(intsockfd,conststructsockaddr*addr,socklen_taddrlen);服务器通常需要把 Socket 绑定到固定 IP 和端口。客户端一般不必手动绑定端口连接时由内核分配临时端口即可。7.3 listen把 TCP Socket 设为监听状态intlisten(intsockfd,intbacklog);listen只用于面向连接的 TCP Socket。调用后Socket 开始接收连接请求。backlog与等待连接队列有关但它不是简单等于“最多只能连接多少客户端”具体语义还受到内核实现和系统配置影响。7.4 accept获取已经建立的连接intaccept(intsockfd,structsockaddr*addr,socklen_t*addrlen);监听 Socket 负责等待连接accept成功后会返回一个新的文件描述符专门用于和某个客户端通信。这里要区分两个描述符监听描述符继续等待新的客户端已连接描述符负责当前客户端的数据收发。服务器能够同时服务多个客户端正是因为每次成功accept都会得到独立的连接 Socket。7.5 connect客户端主动连接服务器intconnect(intsockfd,conststructsockaddr*addr,socklen_taddrlen);TCP 客户端创建 Socket 后使用connect指定服务器 IP 和端口。连接建立成功后客户端就可以通过该描述符收发数据。UDP 没有建立连接的握手过程但也允许调用connect固定默认对端。此时并不会变成 TCP只是内核记录默认目标并对收发接口做相应限制。八、为什么接口统一使用 sockaddr8.1 不同协议的地址结构不同IPv4 地址需要保存 IP 和端口本地 Unix 域 Socket 需要保存文件路径。它们的地址格式不同但bind、connect等函数希望使用统一接口。为了解决这个问题Socket API 使用通用结构struct sockaddr作为函数参数再通过开头的地址族字段判断实际类型。写 IPv4 程序时我们实际准备的是struct sockaddr_in调用函数时再强制转换成struct sockaddr *。8.2 sockaddr_in 的主要字段#includenetinet/in.hstructsockaddr_in{sa_family_tsin_family;in_port_tsin_port;structin_addrsin_addr;};主要字段含义如下sin_family地址族IPv4 填写AF_INETsin_port16 位端口号必须使用网络字节序sin_addrIPv4 地址内部保存 32 位地址值。准备服务器地址时可以这样写structsockaddr_inlocal;memset(local,0,sizeof(local));local.sin_familyAF_INET;local.sin_porthtons(8080);local.sin_addr.s_addrhtonl(INADDR_ANY);INADDR_ANY表示绑定本机所有可用 IPv4 地址。服务器可能有环回地址、局域网地址或其他网卡地址使用它以后这些本地地址上的 8080 端口都可以接收连接。调用bind时需要转换指针类型intretbind(sockfd,(conststructsockaddr*)local,sizeof(local));这种转换看起来麻烦但它换来了接口统一。IPv4、IPv6 和 Unix 域 Socket 可以复用同一组系统调用。8.3 字符串 IP 与二进制地址人习惯使用192.168.1.10这样的字符串协议栈内部使用的是二进制地址。可以使用inet_pton完成转换#includearpa/inet.hstructsockaddr_inserver;memset(server,0,sizeof(server));server.sin_familyAF_INET;server.sin_porthtons(8080);intretinet_pton(AF_INET,192.168.1.10,server.sin_addr);返回值含义返回1转换成功返回0字符串不是合法地址返回-1地址族不受支持或发生错误。反向转换可以使用inet_ntopcharip[INET_ADDRSTRLEN];inet_ntop(AF_INET,server.sin_addr,ip,sizeof(ip));相比旧接口inet_pton和inet_ntop同时支持 IPv4、IPv6含义也更清楚。九、把完整流程串起来前面的概念可以放进一次 TCP 访问中再看一遍。假设客户端进程要访问另一台主机上的 TCP 服务器客户端调用socket创建通信端点客户端准备服务器 IP 和端口调用connect应用数据交给 TCPTCP 添加源端口和目的端口等信息IP 层添加源 IP 和目的 IP并根据路由决定下一跳链路层填写当前链路的源 MAC 和目的 MAC网卡把数据帧发送到网络路由器逐跳转发每一跳都会重新建立链路层封装服务器网卡收到数据后协议栈逐层解包和分用TCP 根据目标端口找到监听端点或已建立连接服务器进程通过accept获得连接再从 Socket 中读取应用数据。这里同时出现了三类定位信息MAC 地址定位当前链路中的下一跳IP 地址定位网络中的目标主机端口号定位主机中的通信进程。三者解决的问题不同组合起来才完成从一个进程到另一个进程的准确交付。总结本文的重点如下局域网与跨网络寻址MAC 地址服务于当前链路IP 地址服务于跨网络寻址和路由数据处理方向发送端自顶向下封装接收端自底向上解包并分用IP 与 MAC 的变化跨路由转发时IP 提供长期方向MAC 地址随当前链路变化端口号的作用网络通信最终是进程间通信端口号负责把数据交给正确通信端点TCP 与 UDPTCP 提供可靠字节流UDP 提供更轻量的数据报服务网络字节序网络统一使用大端字节序端口常用htons和ntohs转换Socket 基本接口socket、bind、listen、accept、connect构成 TCP 网络编程的基本接口IPv4 地址结构程序使用sockaddr_in保存地址调用通用接口时转换成sockaddr *。把这些基础概念理顺以后再开始写 TCP 或 UDP 代码就能知道每个结构和接口为什么存在而不是只照着模板填写参数。往期资源分享【Linux网络】网络为什么要分层从协议、OSI 到 TCP/IP一次搭起网络通信整体框架【Linux】线程安全进阶单例模式、可重入函数与死锁排查【Linux】手写日志与固定线程池任务队列、工作线程和安全退出
RELATED

相关推荐

【基于 Swoole+Hyperf 的微服务实战】第十二周·周四:安全模块:IP 黑白名单、SQL 注入/XSS 检测插件、请求体大小限制

【基于 Swoole+Hyperf 的微服务实战】第十二周·周四:安全模块:IP 黑白名单、SQL 注入/XSS 检测插件、请求体大小限制

【基于 SwooleHyperf 的微服务实战】第十二周周四:安全模块:IP 黑白名单、SQL 注入/XSS 检测插件、请求体大小限制今天我们进入第十二周周四,主题是 安全模块:IP 黑白名单、SQL 注入/XSS 检测插件、请求体大小限制。在前面几天&am…

📅 2026/10/26 1:54:07
部署实战(三):ICP备案通过后,从HTTPS证书签发到微信小程序域名配置

部署实战(三):ICP备案通过后,从HTTPS证书签发到微信小程序域名配置

部署实战系列 📌 部署实战(一):2核2G轻量服务器 Docker 部署 Java 全栈及小程序上线避坑指南 部署实战(一):2核2G轻量服务器Docker部署Java全栈及小程序上线避坑指南-CSDN博客 📌 部…

📅 2026/10/26 1:54:07
前端教学5:视口 vs 文档 —— 滚动改变不了视口大小

前端教学5:视口 vs 文档 —— 滚动改变不了视口大小

<!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>案例四&#xff1a;视口 vs 文档 —— 滚动改变不…

📅 2026/10/26 1:54:07
MORE NEWS

更多资讯

📰

AI生成代码仅需几秒,为何验证要半个月?嵌入式安全开发全解析

“AI生成代码几秒钟&#xff0c;测试验证和路试可能要半月”——这句话一出来&#xff0c;懂的人都懂。前阵子我们某个电控功能迭代&#xff0c;让AI辅助生成了一段控制器代码&#xff0c;从按下回车到代码落地&#xff0c;确实只花了几秒钟。但后续的代码评审、静态分析、单元…

📰

AI智能体开发框架核心技术解析与实践指南

1. AI智能体开发框架概述AI智能体&#xff08;Agent&#xff09;开发框架正在成为人工智能领域的新热点。作为一名长期从事AI系统开发的工程师&#xff0c;我见证了从传统规则引擎到现代智能体的技术演进过程。当前主流的智能体框架已经能够支持复杂决策、环境交互和持续学习等…

📰

AI智能体开发框架解析与应用实践

1. AI智能体开发框架概述AI智能体&#xff08;Agent&#xff09;开发框架正在成为当前人工智能领域最热门的技术方向之一。作为一个长期从事AI系统开发的从业者&#xff0c;我见证了从传统规则系统到现代智能体的演进过程。如今的AI智能体已经不再是简单的任务执行者&#xff0…

📰

Linux kernel日志解析:定位底层故障的核心技术

1. 什么是kernel日志&#xff1a;系统底层的“黑匣子”与运维人员的第一手证据你有没有遇到过服务器突然卡死、服务莫名中断、磁盘IO飙升到99%却查不到哪个进程在作怪&#xff1f;或者容器反复重启&#xff0c;kubectl get pods只显示CrashLoopBackOff&#xff0c;但kubectl lo…

📰

Linux kernel日志原理与实战:从dmesg到故障根因分析

1. 什么是kernel日志&#xff1a;系统底层的“黑匣子”与运维人员的第一手证据“kernel日志”这四个字&#xff0c;听起来像Linux教科书里的术语&#xff0c;但对每天盯着服务器面板、排查半夜告警、调试驱动异常的从业者来说&#xff0c;它不是概念&#xff0c;而是现场——是…

📰

架构图之外:常见软件架构模式的依赖约束与选型

接手一个项目的第二天&#xff0c;我在代码仓库里看到三个包互相引用&#xff1a;订单相关的包调用库存相关的包&#xff0c;库存相关的包反过来又调用订单相关的包&#xff0c;中间还夹着一个名字里带 common 的包被两边同时依赖。项目文档里那张架构图画得很干净&#xff0c;…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬