欢迎光临
我们一直在努力

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 什么是 Socket
    • 1.2 系统层与传输层接口
      • 1.2.1 传输层系统调用
    • 1.3 传输层典型协议简介
      • 1.3.1 TCP 协议(Transmission Control Protocol)
      • 1.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 常见 API
      • 3.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 Protocol)

TCP 即传输控制协议,具有以下关键特性:

  • 传输层协议:工作于 OSI 模型的传输层。
  • 有连接:通信前双方必须建立连接。(你先可以短暂的这样理解:打电话,你喂我喂就是在建立连接)
  • 可靠传输:提供数据包确认、重传机制,保证数据准确无误到达。
  • 面向字节流:数据以无边界的字节流形式传输。(水龙头接水就是面向字节流)

文件流和面向字节流是没有太大区别的,只是表述的方式不一样。

1.3.2 UDP 协议(User Datagram Protocol)

UDP 即用户数据报协议,具有以下关键特性:

  • 传输层协议:工作于 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:

#include <arpa/inet.h>

uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);

  • 如果主机是小端字节序,这些函数会将参数做相应的大端/小端转换后返回。
  • 如果主机是大端字节序,这些函数不做任何转换,直接原封不动地返回参数。

2.3.1 命名规范与参数含义

这么多我怎么记?

  • h:代表 host(主机字节序)。
  • n:代表 network(网络字节序)。
  • l:代表 long(32 位长整数,常用于 IP 地址 转换)。
  • s:代表 short(16 位短整数,常用于 Port 端口号 转换)。

三、Socket 编程常用接口与数据结构

3.1 Socket 常见 API

  Socket API 是系统层提供的一套抽象网络编程接口。常见的核心 API 函数定义如下:

// 创建 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);

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_INET)
    • 16 位端口号
    • 32 位 IP 地址
    • 8 字节填充
  • 本地通信(UNIX Domain Socket):使用 struct sockaddr_un
    • 前 16 位:地址类型(AF_UNIX)
    • 108 字节路径名

3.3.2 系统底层的多态判断逻辑

  Socket API 在底层接收到强转后的 struct sockaddr 指针后,通过读取结构体最前方的地址家族标识(sin_family)自行区分通信类型:

if (address->sin_family == AF_INET) {
// 底层转化为对应的网络结构体,执行网络通信
} else if (address->sin_family == AF_UNIX) {
// 底层转化为对应的本地结构体,执行本地通信
}

3.3.3 面向对象思想与协议家族扩展

  • Socket API 实现了“用什么通信传什么结构体,但使用同一套通用 API 接口”。这种通过头部字段区分类型、使用通用结构体指针转接的设计,是 C 语言层面对面向对象多态思想的经典实现。
  • 由于统一套接口的设计,未来即使出现新的通信方式,也只需按规范定义新的结构体并传入系统接口即可,保证了 API 的极高通用性与扩展性。

好的本期内容就到这里,如果对你有帮助,还不要忘记点赞三联支持。我是此方,我们下期再见。bye! Linux、C++、算法持续连载中,欢迎关注WeChat Official Account 【此方的技术栈】。

赞(0)
未经允许不得转载:171主机测评 » Linux网络(四):一文彻底搞懂 Linux Socket 网络编程预备知识,从网络通信本质、TCP/IP 协议、字节序到 Socket API 与底层数据结构
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址