Linux网络基础 网络基础零、计算机网络背景0.1 网络是怎么来的计算机是人的工具人要协同工作注定了网络的产生是必然的。回顾网络发展的三个阶段独立模式计算机之间相互独立数据交换只能靠软盘、U 盘这类物理介质网络互联多台计算机通过交换机和路由器连接在一起完成数据共享局域网LAN→ 广域网WAN局域网和广域网只是相对概念——可以把有中国特色的广域网也看作一个比较大的局域网0.2 什么是协议协议是一种约定。计算机之间的传输媒介是光信号和电信号通过频率和强弱来表示 0 和 1 这样的信息。要想传递各种不同的信息就需要约定好双方的数据格式。思考只要通信的两台主机约定好协议就可以了吗定好协议但你用频率表示 01我用强弱表示 01——就好比我用中国话你用葡萄牙语。虽然大家可能都遵守说话这一基本通信规则但语言不同即使订好了协议也无法正常通信。所以完善的协议需要更多更细致的规定并让所有参与方都遵守。计算机生产厂商有很多操作系统有很多网络硬件设备还是有很多。如何让这些不同厂商生产的计算机能够相互顺畅通信就需要有人站出来约定一个共同的标准大家都来遵守——这就是网络协议。一、协议分层两个主机通过网络通信会产生什么问题把这些问题逐个拆解相邻设备怎么传递数据— 数据得在相邻设备之间传递这是最底层的事网卡怎么把信号发到网线上、怎么检测有新帧到达。怎么找到目标主机— 目标主机定位和路径选择问题。互联网上那么多主机数据怎么知道该走哪条路才能到达目的地数据丢了怎么办— 传输过程中数据出错、丢失、乱序怎么办需要有人来保证可靠性。数据到了之后做什么— 前三个问题解决后数据确实能从主机 A 跨网络到达主机 C。但到达不是目的——核心目的是处理这个数据完成请求或应答。这四个问题不是同一层面的问题——问题本身就能分层所以解决方案自然也分了层。为了解决这些问题提出了TCP/IP 协议它本质是一种分层的解决方案问题对应层职责问题 1 — 相邻设备传递网络接口层帧同步、冲突检测、差错校验问题 2 — 定位与路由网络层IP 地址寻址 路由选择问题 3 — 可靠性传输层TCP 保证可靠交付问题 4 — 数据处理应用层应用程序间通信1.1 为什么分层协议分为硬件级别和软件级别我们只考虑软件分层。计算机的软件结构基本上是层状结构。比如虚拟文件系统对磁盘、cache、内存等设备统一设置一个struct file结构——用一层软件抽象屏蔽底层差异。继承体系也是在类层面做软件分层。软件工程里有一句名言任何问题都可以通过添加一层软件层来解决。协议本质也是软件在设计上为了更好的模块化、解耦合也被设计为层状结构。分层从软件工程角度实现了低耦合——每一层只关心自己该做的事修改一层不影响其他层。1.2 OSI 七层模型OSIOpen System Interconnection开放系统互连七层网络模型称为开放式系统互联参考模型是一个逻辑上的定义和规范。它把网络从逻辑上分为了 7 层每一层都有对应的物理设备如路由器、交换机。它的最大优点是将服务、接口和协议这三个概念明确地区分开来概念清楚理论也比较完整。通过七个层次化的结构模型使不同系统、不同网络之间能实现可靠通讯。但它既复杂又不实用——在工程实践中我们按照 TCP/IP 模型来讲解。为什么 OSI 七层最终落地是五层从网络角度看OSI 的七层协议模型其实非常完善。但在实际操作过程中会话层和表示层是不可能接入到操作系统中的——它们的功能被合并到了应用层中。所以工程实践中最终落地的是五层协议。这句话的真正含义学完网络自然会理解。1.3 TCP/IP 五层或四层模型TCP/IP 是一组协议的代名词它还包括许多协议组成了TCP/IP 协议簇。TCP/IP 通讯协议采用了 5 层的层级结构每一层都调用它的下一层所提供的网络来完成自己的需求物理层负责光/电信号的传递方式。比如现在以太网通用的网线双绞线、早期以太网采用的同轴电缆现在主要用于有线电视、光纤现在的 WiFi 无线网使用电磁波等都属于物理层的概念。物理层的能力决定了最大传输速率、传输距离、抗干扰性等。集线器Hub工作在物理层。数据链路层负责设备之间的数据帧的传送和识别。例如网卡设备的驱动、帧同步从网线上检测到什么信号算作新帧的开始、冲突检测如果检测到冲突就自动重发、数据差错校验等工作。有以太网、令牌环网、无线 LAN 等标准。交换机Switch工作在数据链路层。网络层负责地址管理和路由选择。例如在 IP 协议中通过 IP 地址来标识一台主机并通过路由表的方式规划出两台主机之间的数据传输的线路路由。路由器Router工作在网络层。传输层负责两台主机之间的数据传输。如传输控制协议TCP能够确保数据可靠的从源主机发送到目标主机。应用层负责应用程序间沟通如简单电子邮件传输SMTP、文件传输协议FTP、网络远程访问协议Telnet等。我们的网络编程主要就是针对应用层。物理层我们考虑得比较少只考虑软件相关的内容——因此很多时候我们直接称为TCP/IP 四层模型。1.4 各层在设备中的实现一般而言主机操作系统内核实现了从传输层到物理层的内容路由器实现了从网络层到物理层交换机实现了从数据链路层到物理层集线器只实现了物理层但并不绝对——很多交换机也实现了网络层的转发很多路由器也实现了部分传输层的内容比如端口转发。二、再识协议——从本质理解上一章我们知道了协议分层的框架。但协议到底是什么它和操作系统又是什么关系这一章我们从本质出发把这个问题讲透。2.1 什么是协议——从数据结构视角先来回答第一个问题。站在数据结构的视角一个朴素的理解是协议就是通信双方都认识的结构化的数据类型。仔细看看下面这张图想象这样一个场景主机 A 定义了一个结构体struct protocol里面放着a10, b20, c30然后把这个结构体的二进制数据发给主机 B。主机 B 能识别并准确提取出这三个值吗答案是肯定的——因为双方都有同样的结构体类型。用同样的代码实现协议、用同样的自定义数据类型天然就具有共识能识别对方发来的数据。这不就是约定吗从内核角度来看假设传输层定义了一个结构体主机 A 把这个结构体对象发送给主机 B 的传输层。由于协议必须相同接收方收到数据后可以直接提取结构体成员——双方用约定结构体的方式进行传输。协议的本质就是结构体。更进一步这个道理在生活中比比皆是。网购时快递单就是一种协议发件人和收件人都认识快递单的格式收件人姓名、地址、电话……双方不需要额外沟通就能完成数据的传递。网络协议的道理完全一样。所谓协议就是通信双方都认识的结构化的数据类型。因为协议栈是分层的所以每一层双方都有对应的协议——同层之间互相可以识别对方的协议。2.2 为什么要有 TCP/IP 协议搞清楚了协议的本质再来追问为什么需要 TCP/IP 这一整套协议即便是单机你的计算机内部其实也存在协议。比如其他设备和内存通信有内存协议其他设备和磁盘通信有磁盘协议如 SATA、IDE、SCSI 等。只不过我们感知不到——这些协议都在本地主机的硬件中通信的成本低、问题少。网络通信最大的特点就是主机之间变远了。任何通信特征的变化一定会带来新的问题有问题就得解决问题。所以TCP/IP 协议存在的本质原因就是通信主机距离变远了。2.3 TCP/IP 协议与操作系统的关系先聊聊操作系统和协议的关系——TCP/IP 协议在宏观上是怎么实现的操作系统都是用 C 和部分汇编写成的协议栈的部分直接被内置在内核中——也就是说协议栈本身也是用 C 写的。由于大家都要遵守 TCP/IP 协议不同操作系统的网络协议栈代码其实都差不多。其中传输层和网络层被编写在内核中因为协议本身必须高效运行在操作系统底层。协议栈的代码位于操作系统的内核中一般用 C/C 实现。当你在用户态调用socket()、bind()这些 API 时实际上最终会触发内核中的协议栈代码去执行封装、路由、发送等工作。后面我们编写 socket 程序时写的虽然是应用层代码但底层其实依赖了一整套内核协议栈。三、网络传输基本流程有了协议分层的框架接下来我们具体看一次网络传输中数据是如何从一台主机到达另一台主机的。3.1 局域网通信原理网络通信其实是网络协议栈进行通信。首先回答核心问题两台主机在同一个局域网内是可以直接通信的。下面谈局域网通信的原理——类似于上课每一次老师想和谁通话就叫谁的名字但全班都能听到。局域网内的每台主机也是如此。每台主机在局域网上有MAC 地址作为唯一的标识来保证主机的唯一性。它是一张网卡一个 MAC 地址硬件固定。具体来看一个通信过程主机 A 向主机 E 发送你好。报头结构体中src为MacAdst为MacE有效载荷 data 为你好。局域网内所有主机 B、C、D、E 都能看到这个报文——除了 E其他主机发现 dst 的 MAC 地址不是自己就直接丢弃。直到主机 E 发现是发给自己的返回信息。这就是以太网通信的基本过程。局域网和以太网是什么关系以太网是局域网的一种具体实现就像 bash 是 shell 的一种具体实现一样。除了以太网还有令牌环网等其他局域网通信标准。在以太网中任何时刻只允许一台机器向网络中发送数据如果多台同时发送会发生数据干扰——称为数据碰撞所有发送数据的主机要进行碰撞检测和碰撞避免没有交换机的情况下一个以太网就是一个碰撞域局域网通信的本质就是基于碰撞检测和碰撞避免的不断重试局域网中如果主机越来越多发生碰撞的概率就会越来越大。换个视角从系统层面看以太网。以太网就是共享资源——碰撞检测和碰撞避免本质上是让共享资源在同一时间只能被同一个主机使用。可以把它看成临界资源要保证发送数据的原子性。用操作系统的视角看待网络很多概念就能打通。主机对收到的报文通过目标 MAC 地址来判定是否是发给自己的。3.2 认识 MAC 地址MAC 地址用来识别数据链路层中相连的节点长度48 比特位即 6 个字节一般用 16 进制数字加冒号的形式表示例如08:00:27:03:fb:19在网卡出厂时就确定了不能修改通常是唯一的虚拟机中的 MAC 地址不是真实的 MAC 地址可能会冲突也有些网卡支持用户配置 MAC 地址Linux 下使用ifconfig查看 MAC 地址ether后面的字段Windows 下执行ipconfig /all后面我们详细讨论数据链路层时会谈 MAC 帧协议此处先有一个基本认识即可。3.3 封装与解包现在思考一个更深的问题主机 A 为什么要给主机 E 发消息是用户驱动的。所以真实数据通信中用户在最上层但数据必须由硬件发出、由硬件接收——这就注定了数据必须贯穿网络协议栈。我们要把各层的报头依次加到传输内容中每一层协议都要添加自己的报头。其中每层都有协议所以进行上述传输流程时必然要进行封装和解包下面我们明确几个核心概念报文 报头 有效载荷报头header对应协议层的结构体字段包含了首部有多长、“有效载荷有多长”、上层协议是什么等信息有效载荷payload每层自己的协议报头之外的就是有效载荷——除去报头剩下的数据不同协议层对完整报文有不同的叫法层报文名称传输层段segment网络层数据报datagram链路层帧frame封装的本质——从内存视角看在谈怎么解包之前先理解封装的本质。前面说过所谓协议本质是一个结构体类型报头就是结构体变量。封装的具体过程如下为了保存数据内存中有缓冲区。用户发送你好数据先放入缓冲区随后进入下一层计算出该层报头需要的大小指针向前偏移腾出报头空间——再向下一层以此类推。封装报头的本质其实就是在报文头部拷贝结构体变量。解包——两件必须做的事当底层协议收到报文不管是什么协议都要做两件事情分离报头和有效载荷解包——如何处理未来我们会遇到固定长度的报头和变长的报头处理方式不同将有效载荷向上交付给正确的协议分用——报头里就有内容用于区分协议类别封装的时候已经把相关信息写入报头了每层协议都要做到这两件事——第一件事是解包第二件事是分用。如果从内存视角来看封装和解包的过程就是入栈和出栈的过程——所以整套协议软件被称为协议栈。3.4 数据包封装和分用在网络传输的过程中数据不是直接发送给对方的而是先自上向下将数据交付给下层协议最后由底层物理设备发送到达对方后再由底层自底向上逐层交付给上层协议。从今天开始学习任何协议都要先宏观上建立两个核心认识这个协议是如何解包的——只有明确了解包方式封装也就自然能理解这个协议是如何将有效载荷交付给上层协议的——每一层的存在意义就是为上层服务四、跨网络传输——IP 地址第三章讨论了局域网内的通信但互联网上的主机绝大多数不在同一个局域网里。跨网络通信相比局域网多了个关键角色——路由器。同时跨网络通信还需要一个全局的寻址机制——这就是 IP 地址。在网络传输中必须解决主机定位和路由选择两个问题此处引入全新的地址类型IP 地址。IP 地址侧重的是标识公网中唯一的一台主机解决主机在哪、数据走哪条路的问题。你可能会问不是每台主机都有 MAC 地址吗MAC 地址就可以标识局域网内的唯一性那为什么需要两个地址答案在于场景不同。下面这张图展示了主机 A 给主机 B 跨网络发送你好的完整过程消息从主机 A 出发先向下交付添加应用层报头 → 添加 TCP 报头 → 添加 IP 层报头 → 添加 MAC 帧报头。此时判断目标主机不在局域网内于是分离分用后发送给路由器。路由器确认目标地址后路由器工作在网络层不能直接把数据报交给同层只能向下封装交给令牌环网随后再一步一步解包分用最终到达主机 B。4.1 认识 IP 地址IP 协议有两个版本IPv4 和 IPv6。我们整个课程凡是提到 IP 协议没有特殊说明的都默认指 IPv4。IPv4 地址是一个4 字节32 位的整数通常使用**“点分十进制”**的字符串表示例如192.168.0.1——用点分割的每一个数字表示一个字节范围是 0~255跨网段的主机之间传输数据需要经过一个或多个路由器。数据在每一跳中重新封装但目的 IP 地址始终不变——它指引着数据前进的方向。4.2 IP 地址 vs MAC 地址现在来回答前面的问题为什么有了 MAC 还要 IPIP 地址在整个路由过程中一直不变目前只能这样说明后面会修正。IP 地址表示的是从哪里来到哪里去——是长远目标。MAC 地址一直在变。MAC 地址表示的是上一站从哪里来下一站去哪里——是下一阶段目标。IP 地址MAC 地址路由过程一直不变长远目标一直在变下一阶段目标作用路径选择的重要依据局域网转发的重要依据目的 IP 是一种长远目标MAC 是下一阶段目标。目的 IP 决定走哪条路MAC 决定这一步交给谁。下面带上 IP 地址再来看主机 A 向主机 B 发消息的完整过程。假设主机 A 的 IP 是192.168.2.2目标主机 B 的 IP 是172.168.2.2。局域网内部还有其他主机192.168.2.3、192.168.2.4。在网络层主机 A 不知道172.168.2.2是谁但它能判断目标不在本局域网内——于是只能把报文交给路由器。下面结合封装与解包来看路由器如何解包后重新封装主机 A 上层交付下来层层封装。在网络层封装时发现目的不在同一局域网因此 MAC 帧头封装为macLeft指向路由器左网卡。数据包发出后局域网内其他主机看到 dst 不是自己的 MAC直接丢弃。路由器收到数据包后把报头和有效载荷分离解包分用后将有效载荷交给路由器的网络层。路由器网络层查看目的 IP172.168.2.2匹配路由表发现该网段从右侧网卡出去——于是在右侧链路层封装新的以太网帧头macRight由路由器右网卡把新帧发送到右侧局域网。主机 B 网卡收到路由器转发过来的完整 MAC 帧逐层向上解包。4.3 IP 网络层的意义为什么需要 IP——从底层差异看网络层的价值在网络层收发的是同一种报文IP 数据报。但网络层往下数据链路层可能是以太网报头、令牌环网报头、无线网报头……局域网标准并不统一标准不同实现方式也不同。把涉及硬件的所有局域网全部统一是不现实的。于是就出现了 IP 协议——网络层之上包括网络层所有协议标准统一IP 网络网络层往下标准不统一。IP 网络层统一互联网通信标准就和虚拟地址空间通过页表映射到物理空间一样——上层看到统一抽象底层各自实现。网络通信的宏观流程如图五、Socket 编程预备掌握了 IP 地址之后我们离编写网络程序还差最后一环数据到达主机后怎么交给具体的进程这就是传输层的核心问题也是 socket 编程的起点。5.1 源 IP 和目的 IPIP 在网络中用来标识主机的唯一性。但想一想数据传输到主机就是目的吗不是的。数据是给人用的——聊天是人在聊天下载是人在下载浏览网页是人在浏览。而人是怎么看到这些信息的通过启动的 QQ、迅雷、浏览器……它们都是进程。换句话说进程是人在系统中的代表——只要把数据交给进程人就相当于拿到了数据。所以数据传输到主机不是目的而是手段。到达主机内部再交给主机内的进程才是真正的目的。但系统中同时会存在非常多的进程。当数据到达目标主机之后怎么准确地转发给目标进程这就是下一个问题如何在网络背景下标识主机内进程的唯一性。5.2 端口号port端口号是传输层协议的内容是一个2 字节 16 位的整数用来标识一个进程告诉操作系统当前这个数据要交给哪一个进程来处理IP 地址 端口号 能够标识网络上某一台主机的某一个进程一个端口号只能被一个进程占用但一个进程可以绑定多个端口号拿上图来说两个主机上都有 QQ 进程。主机 A 的 QQ 进程端口号绑定为8888主机 B 的绑定为9999。报文就像下面这样src_port: 8888 dst_port: 9999 src_ip: 192.168.2.3 dst_ip: 192.168.2.4 data: ...这个报文一定能转发给主机 B——但发送到主机 B 不是目的只是手段。解包后拿到9999才能发送到对应的 QQ 进程。所以网络通信的本质是进程间通信。从冯·诺依曼体系视角来看进程发送数据到网卡、进程从网卡接收数据——这就是 I/O 操作。从 Linux 一切皆文件的视角网络也是文件读写网络就是 I/O。端口号范围划分范围类型说明0 ~ 1023知名端口号HTTP、FTP、SSH 等广为使用的应用层协议端口是固定的1024 ~ 65535动态端口操作系统动态分配客户端程序从这个范围获取端口端口号 vs 进程 PID我们在系统编程中学习了 PID 表示唯一一个进程端口号也能唯一标识一个进程那为什么不直接用 PID理由一解耦。PID 属于系统概念从技术角度确实能实现但端口号属于网络概念。如果都用一个系统只要修改就可能影响网络——做好系统和网络的解耦才是正确的设计。理由二范围区分。PID 每个进程都要有但并不是所有进程都需要进行网络通信。没有端口号的进程就不需要网络通信端口号是网络通信的强标识。打个比方就像 10086——你可以打 10086 找中国移动客服但你不需要知道接电话的是工号 9527 还是 9528。PID 是系统内部的概念端口号是网络对外暴露的概念二者各司其职。IP vs Port 的关系IP 地址标识主机一个主机上可能有多个需要网络通信的进程——所以一个 IP 可以对应多个 port。扩展——OS 是怎么把数据交给特定进程的怎么找到进程OS 内部存在一个hash 表用于绑定端口号和进程。进程在绑定端口时会把自己的 PCB 链入这个 hash 表。当数据到达时通过端口号查 hash 表就能找到对应进程。找到进程后怎么把网络数据交给它网络报文会源源不断地传输到网络层可能存在积压。大量报文需要管理——OS 必然有内核数据结构指向缓冲区随后对这些数据结构进行管理。在 Linux 一切皆文件的体系下网卡是文件被进程打开后进程内部有属于自己的 fdstruct file指向文件缓冲区。进程找到了通过 fd 就能找到 file 结构体也就找到了缓冲区拿到了数据报。这种说法不是特别准确后面会更正。5.3 理解 socket综上IP 地址用来标识互联网中唯一的一台主机port 用来标识该主机上唯一的一个网络进程IP Port就能表示互联网中唯一的一个进程网络通信的本质就是两个互联网进程代表人来进行通信。四元组{srcIp, srcPort, dstIp, dstPort}就能标识互联网中唯二的两个进程。网络通信的本质也是进程间通信。我们把ip port 叫做套接字socket。多个 socket 组成连接后面网络编程的核心就是对 socket 的操作。网络中不同进程之间是有独立性的要让它们看到同一个共享资源——这个共享资源就叫网络。所以网络通信本质也就是进程通信。5.4 TCP 与 UDP初步认识传输层属于内核。要进行网络通信操作系统不可能直接把内核暴露给用户必然要提供系统调用——TCP 和 UDP 就是为此而存在的。在网络通信过程中操作系统其实不知道用户未来要走什么样的通信流程就像程序替换时 OS 不知道具体要传什么参数一样所以传输层要提供多种协议让用户根据场景自由选择。两个最核心的传输层协议TCPUDP全称Transmission Control ProtocolUser Datagram Protocol连接有连接无连接可靠性可靠传输不可靠传输面向字节流数据报有连接 vs 无连接打电话就像是有连接的——先拨号、对方接听、然后通话发微信或邮件就像是无连接的——消息直接发出不确认对方是否在线。可靠 vs 不可靠TCP 和 UDP 的区别在于对数据丢失是否处理。但不要把不可靠当成缺点——要当成特点。可靠意味着要做更多的工作重传、确认、排序不可靠意味着更快、开销更小。选择哪个取决于场景要数据完整性就用 TCP要低延迟就用 UDP。此处先有一个直观认识后面还会详细讨论 TCP 的诸多细节。5.5 网络字节序什么是大端和小端内存中有低地址和高地址。假设一个数据int a 0x1234abcd按字节存储12是高位cd是低位。小端little-endian低位写低地址—— 记法“小小小”小端 低位 → 低地址大端big-endian低位写高地址—— 反之为什么会存在大小端就跟为什么会存在不同的局域网标准一样——当时各实验室有自己的实现方案各自为政历史原因形成了两种字节序。在网络中不管你是什么端规定是先发出的数据是低地址后发出的数据是高地址。于是小端机器会按字节序cd ab 34 11发出大端机器会按字节序12 34 ab cd发出接收端也按同样方式接收。但如果两端大小端不一致处理数据时就会把数据搞反。怎么解决TCP/IP 协议规定网络中传递的数据必须是大端字节序。不管当前主机是大端机还是小端机都必须按这个规定来——小端机需要先将数据转成大端再发送大端机可以直接发送。系统提供了一批转换接口函数含义htonlhost to network long32 位如 IP 地址htonshost to network short16 位如端口号ntohlnetwork to host long32 位ntohsnetwork to host short16 位如果主机是小端这些函数做相应的大小端转换然后返回如果主机是大端这些函数不做转换将参数原封不动地返回大部分时候我们不需要关心大小端问题——读写已经自动帮我们转化好了。个别情况需要我们手动转换比如填充端口号和 IP 地址时。网络规定所有发送到网络上的数据都必须是大端的5.6 socket 编程接口socket 常用 APIsys/socket.h// 创建 socket 文件描述符TCP/UDP客户端 服务器intsocket(intdomain,inttype,intprotocol);// 绑定端口号TCP/UDP服务器intbind(intsocket,conststructsockaddr*address,socklen_taddress_len);// 开始监听 socketTCP服务器intlisten(intsocket,intbacklog);// 接收请求TCP服务器intaccept(intsocket,structsockaddr*address,socklen_t*address_len);// 建立连接TCP客户端intconnect(intsockfd,conststructsockaddr*addr,socklen_taddrlen);5.7 sockaddr 结构看一下前面列出的 socket APIbind()、accept()、connect()……很多接口的参数里都有一个struct sockaddr* address。这个结构体是什么为什么这么设计三种通信场景一套接口socket 的设计者要支持多种通信场景INET socket网络通信需要 IP 地址 端口号UNIX socket本地通信特别像命名管道需要传递路径名raw socket原始套接字用来设计网络工具作为设计者难道要为三种场景设计三类不同的系统调用吗那样用户的使用成本会大大增加。解决方案再加一层软件层。还记得前面说的吗——任何问题都可以通过添加一层软件层来解决。这里正是这个思想的应用统一函数接口统一传递参数。接口统一容易但参数不一样怎么办网络通信需要传递 IP 和端口号 → 封装成sockaddr_in结构体域间通信需要传递路径名 → 封装成sockaddr_un结构体再设计一个通用结构体sockaddr作为基类所有 API 的参数都是sockaddr*类型。函数内部根据地址类型字段AF_INET/AF_INET6/AF_UNIX判断进而强转成对应的子类结构体。这不就是面向对象里的多态吗sockaddr是基类sockaddr_in和sockaddr_un是子类函数接收基类指针内部按需向下转型。具体来说IPv4 和 IPv6 的地址格式定义在netinet/in.h中。IPv4 地址用sockaddr_in结构体表示包含16 位地址类型 16 位端口号 32 位 IP 地址地址类型分别定义为常量AF_INETIPv4/AF_INET6IPv6socket API 统一使用struct sockaddr*类型使用时需要强制转化成sockaddr_inin_addr结构用来表示一个 IPv4 的 IP 地址本质就是一个 32 位的整数。