ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Linux网络(四):一文彻底搞懂 Linux Socket 网络编程预备知识,从网络通信本质、TCP/IP 协议、字节序到 Socket API 与底层数据结构

Linux网络(四):一文彻底搞懂 Linux Socket 网络编程预备知识,从网络通信本质、TCP/IP 协议、字节序到 Socket API 与底层数据结构 ◆ 博主名称 小此方-CSDN博客大家好欢迎来到小此方的博客。⭐️网络系列个人专栏 【主题曲】计算机网络⭐️此方的GitHub github_此方⭐️我们思考 (Rethink) · 我们重建 (Rebuild) · 我们记录 (Record)文章目录概要序論零、端口号的底层实现原理补充0.1 端口号到进程PCB的映射机制0.1.1 底层哈希表查找0.2 有效载荷传递给进程的机制0.2.1 Linux“一切皆文件”的设计思想一、Socket编程基础认知1.1 理解 Socket 与网络通信本质1.1.1 唯一标识与进程间通信1.1.2 什么是 Socket1.2 系统层与传输层接口1.2.1 传输层系统调用1.3 传输层典型协议简介1.3.1 TCP 协议Transmission Control Protocol1.3.2 UDP 协议User Datagram Protocol二、网络字节序与主机字节序2.1 字节序的基本概念2.1.1 大端与小端2.2 TCP/IP 网络字节序规定2.3 字节序转换 API 函数2.3.1 命名规范与参数含义三、Socket 编程常用接口与数据结构3.1 Socket 常见 API3.1.1 核心 API 功能简析3.2 sockaddr 结构体抽象3.2.1sockaddr结构3.2.2sockaddr_in 结构3.2.3in_addr结构3.3 sockaddr 结构体的统一设计与协议多态3.3.1 两种常见地址结构体对比3.3.2 系统底层的多态判断逻辑3.3.3 面向对象思想与协议家族扩展概要序論Hello大家好我是此方。本文将从网络通信本质出发系统讲解 TCP/UDP概念Socket 常用 API 以及 sockaddr 等核心数据结构逐步建立完整的 Socket 编程知识体系。零、端口号的底层实现原理补充应该放在上一期讲漏掉了。0.1 端口号到进程PCB的映射机制0.1.1 底层哈希表查找信息传入与解包数据信息从网络传入依次经过链路层、网络层和传输层解包。哈希映射系统拿着传输层报头中的端口号如8080去内核维护的hash表中进行索引查找。链式哈希结构内核使用链式哈希链表结构管理连接。在进程创建并绑定端口时系统会提前将进程控制块PCB根据其绑定的端口号链入hash表中。通过端口号索引即可迅速定位到该端口对应的PCB。0.2 有效载荷传递给进程的机制0.2.1 Linux“一切皆文件”的设计思想内核缓冲区在 Linux 系统中一切皆文件。Socket 在内核中也表现为文件形式拥有对应的内核缓冲区。数据交付根据端口号找到目标进程PCB后内核将解包得到的有效载荷直接拷贝放入该 Socket 对应的内核缓冲区当中。上层读取上层应用程序通过文件描述符读取该内核缓冲区即可顺利获取从网络接收到的有效载荷。一、Socket编程基础认知1.1 理解 Socket 与网络通信本质1.1.1 唯一标识与进程间通信IP 与 Port 的作用IP 地址用于在互联网中唯一标识一台主机port端口号用于唯一标识该主机上的一个网络进程。IP Port 组合IP Port能够唯一标识互联网中的某个特定进程。网络通信的本质通信双方本质上是两个互联网进程代表用户进行交互。由{srcIp, srcPort, dstIp, dstPort}构成的四元组可以唯一标识互联网中进行通信的两个进程。因此网络通信的本质就是跨主机的进程间通信。1.1.2 什么是 Socket定义我们把IP Port的组合称作套接字socket。字面含义socket原意为“插座、插口”。在网络编程中它就像是一个插座接口应用程序通过“插上”这个接口接入网络通信网络。套接字有网络套接字和本地套接字我们只讲网络套接字网络套接字学会了本地套接字自然就会了1.2 系统层与传输层接口1.2.1 传输层系统调用传输层属于操作系统内核的一部分。当我们理解了操作系统和网络协议栈后就会明确要通过网络协议栈进行通信必须调用传输层提供的系统调用接口。1.3 传输层典型协议简介1.3.1 TCP 协议Transmission Control ProtocolTCP即传输控制协议具有以下关键特性传输层协议工作于 OSI 模型的传输层。有连接通信前双方必须建立连接。你先可以短暂的这样理解打电话你喂我喂就是在建立连接可靠传输提供数据包确认、重传机制保证数据准确无误到达。面向字节流数据以无边界的字节流形式传输。水龙头接水就是面向字节流文件流和面向字节流是没有太大区别的只是表述的方式不一样。1.3.2 UDP 协议User Datagram ProtocolUDP即用户数据报协议具有以下关键特性传输层协议工作于 OSI 模型的传输层。无连接发送数据前无需建立连接直接发送。不可靠传输尽力而为交付不保证数据是否丢失或按序到达。面向数据报以独立的数据报文为单位进行传输保留数据边界。收发快递就是面向数据报这两个东西肯定不会这么简单就过去了。对于TCP与UDP协议的详细介绍我放在后面再讲现在当务之急是知晓概念并立即写出我们的第一个服务器代码。可靠与不可靠是中性词语可靠能做更多事情但是更加复杂不可靠更加简单。于是TCP和UDP从事与不同的应用场景两者不能一概而论。目前有此认识后面会专门出文章深究二、网络字节序与主机字节序2.1 字节序的基本概念2.1.1 大端与小端内存和磁盘文件中的多字节数据均有大端big-endian与小端little-endian之分。网络数据流同样需要明确定义字节存储与传输的顺序我好像讲过传送门一篇文章讲透——计算机中的数据存储原理发送端通常将发送缓冲区中的数据按内存地址从低到高的顺序发送。接收端把从网络上接收到的字节依次保存到接收缓冲区中同样按内存地址从低到高的顺序保存。网络数据流规定先发送的数据是低地址后发送的数据是高地址。为什么一定要规定大端目前没有合理的解释 一个可能的原因发送过来的数据更加符合人类的阅读习惯。2.2 TCP/IP 网络字节序规定统一标准TCP/IP 协议规定网络数据流应采用大端字节序即低地址高字节。强制要求所有发送到网络上的数据都必须是大端的主机适应不论当前主机是大端机还是小端机都会按照该规定发送和接收数据若当前发送主机是小端需先将数据转换为大端后再发送。若当前发送主机是大端则忽略转换直接发送。2.3 字节序转换 API 函数为了保证网络程序的可移植性使同样的 C 代码在大端和小端计算机上编译后都能正常运行头文件arpa/inet.h提供了以下转换 API#includearpa/inet.huint32_thtonl(uint32_thostlong);uint16_thtons(uint16_thostshort);uint32_tntohl(uint32_tnetlong);uint16_tntohs(uint16_tnetshort);如果主机是小端字节序这些函数会将参数做相应的大端/小端转换后返回。如果主机是大端字节序这些函数不做任何转换直接原封不动地返回参数。2.3.1 命名规范与参数含义这么多我怎么记h代表host主机字节序。n代表network网络字节序。l代表long32 位长整数常用于IP 地址转换。s代表short16 位短整数常用于Port 端口号转换。三、Socket 编程常用接口与数据结构3.1 Socket 常见 APISocket API 是系统层提供的一套抽象网络编程接口。常见的核心 API 函数定义如下// 创建 socket 文件描述符 (TCP/UDP客户端 服务器)intsocket(intdomain,inttype,intprotocol);// 绑定端口号 (TCP/UDP服务器)intbind(intsocket,conststructsockaddr*address,socklen_t address_len);// 开始监听socket (TCP服务器)intlisten(intsocket,intbacklog);// 接收请求 (TCP服务器)intaccept(intsocket,structsockaddr*address,socklen_t*address_len);// 建立连接 (TCP客户端)intconnect(intsockfd,conststructsockaddr*addr,socklen_t addrlen);3.1.1 核心 API 功能简析socket用于创建套接字并返回一个文件描述符是进行一切网络通信的基础入口。bind将创建的套接字与指定的本地地址IP Port绑定在一起主要用于服务器端。listen将套接字设置为监听模式准备接受来自客户端的连接请求仅 TCP 服务器端使用。accept阻塞等待并接收客户端的连接请求成功后返回一个新的套接字用于后续通信仅 TCP 服务器端使用。connect向指定的服务器端发起连接请求仅 TCP 客户端使用。3.2 sockaddr 结构体抽象socket API是一层抽象的网络编程接口,适用于各种底层网络协议,如IPv4、IPv6,以及后面要讲的UNIX Domain Socket. 然而, 各种网络协议的地址格式并不相同。IPv4和IPv6的地址格式定义在netinet/in.h中,IPv4地址用sockaddr_in结构体表示,包括16位地址类型, 16位端口号和32位IP地址.IPv4、IPv6地址类型分别定义为常数AF_INET、AF_INET6. 这样,只要取得某种sockaddr结构体的首地址,不需要知道具体是哪种类型的sockaddr结构体,就可以根据地址类型字段确定结构体中的内容.socket API都可以用struct sockaddr *类型表示, 在使用的时候需要强制转化成sockaddr_in; 这样的好处是程序的通用性, 可以接收IPv4, IPv6, 以及UNIX Domain Socket各种类型的sockaddr结构体指针作为参数;3.2.1sockaddr结构3.2.2sockaddr_in 结构虽然socket api的接口是sockaddr, 但是我们真正在基于IPv4编程时, 使用的数据结构是sockaddr_in;这个结构里主要有三部分信息: 地址类型, 端口号, IP地址.3.2.3in_addr结构in_addr用来表示一个IPv4的IP地址. 其实就是一个32位的整数;3.3 sockaddr 结构体的统一设计与协议多态3.3.1 两种常见地址结构体对比在实际使用中我们根据不同的通信场景定义具体的结构体再强制类型转换为struct sockaddr传给 Socket API网络通信IPv4使用struct sockaddr_in前 16 位地址类型AF_INET16 位端口号32 位 IP 地址8 字节填充本地通信UNIX Domain Socket使用struct sockaddr_un前 16 位地址类型AF_UNIX108 字节路径名3.3.2 系统底层的多态判断逻辑Socket API 在底层接收到强转后的struct sockaddr指针后通过读取结构体最前方的地址家族标识sin_family自行区分通信类型if(address-sin_familyAF_INET){// 底层转化为对应的网络结构体执行网络通信}elseif(address-sin_familyAF_UNIX){// 底层转化为对应的本地结构体执行本地通信}3.3.3 面向对象思想与协议家族扩展Socket API 实现了“用什么通信传什么结构体但使用同一套通用 API 接口”。这种通过头部字段区分类型、使用通用结构体指针转接的设计是 C 语言层面对面向对象多态思想的经典实现。由于统一套接口的设计未来即使出现新的通信方式也只需按规范定义新的结构体并传入系统接口即可保证了 API 的极高通用性与扩展性。好的本期内容就到这里如果对你有帮助还不要忘记点赞三联支持。我是此方我们下期再见。bye! Linux、C、算法持续连载中欢迎关注WeChat Official Account 【此方的技术栈】。
返回列表