【Socket编程预备知识】

网络通信的最终目的不是把数据扔给主机,而是把数据交给人正在使用的程序 ------QQ、浏览器、游戏客户端。

这些程序在操作系统中以进程 的形式存在。

所以,网络通信的本质 = 两个不同主机上的进程之间的通信

🧠 关键认知转变:

之前我们关心"主机到主机",现在开始关心"进程到进程"。

要让进程能收发网络数据,**操作系统提供了一套编程接口------Socket API。**而在使用它之前,必须先搞懂几个核心概念:IP地址、端口号、字节序等。


一、数据到底要传给谁?

明确:数据传输到主机是目的吗 ?

--> 不是,因为数据是给人使用的 。 进程是人在系统的代表 , 只要把数据给进程,人就相当于拿到了数据 。所以数据传输到主机不是目的,而是手段 。 到达主句内部 , 再交给主机内的进程,才是目的 。

  • 你用 QQ 发消息 → 数据要给 QQ 进程
  • 你打开网页 → 数据要给浏览器进程
  • 一台主机同时跑很多进程,必须有办法区分

所以网络通信的真正目标是:从「发送方进程」 → 传到「接收方进程」

但是 , 系统中 , 同时会存在非常多的进程 ,当数据到达目标主机后 , 怎么转发给目标进程 ??? 这就要在网络背景下,在系统中 , 标识主机的唯一性

二、认识端口号

2.1 源IP与目的IP:找到那台主机

IP地址 (如 192.168.0.1)的作用是在整个互联网中唯一标识一台主机

  • 源IP地址:数据从哪台主机发出来。

  • 目的IP地址:数据要送到哪台主机。

当你在浏览器里访问 www.baidu.com,DNS解析出百度服务器的IP地址(如 110.242.68.66),那么你发出的数据包中:

  • 源IP = 你电脑的公网IP

  • 目的IP = 110.242.68.66

🌰 小比喻:IP地址就像 省+市+街道+门牌号,能让你准确找到一栋大楼。

2.2 端口号

端口号(port)是传输层协议的内容

  • 传输层的概念
  • 2字节 , 16 位整数(0 ~ 65535)
  • 作用:标识主机里的一个进程

一句话:IP 找到哪台主机,端口找到哪个程序。

要点:

  • 一个进程可以绑定多个端口
  • 一个端口只能被一个进程占用

IP + port 能够便是网络上的某一台主机的某一个进程

2.3 端口范围划分

范围 分类 说明
0 ~ 1023 知名端口号 系统预留,给标准服务:HTTP=80,HTTPS=443,SSH=22,FTP=21
1024 ~ 65535 动态/私有端口号 操作系统动态分配给客户端程序,比如你的QQ、Chrome

🌰 小比喻:

  • 主机 = 一栋写字楼

  • 端口号 = 楼层+房间号

  • 知名端口 = 星巴克(总在一楼)、顺丰快递点(固定位置)

2.4 端口号 vs 进程ID(PID)

你可能会问:操作系统里每个进程都有唯一的PID,为什么不用PID来代替端口号?

对比项 端口号 PID
范围 0~65535,网络约定 系统动态分配,可能很大
变化 相对稳定(服务固定端口) 每次启动都变
耦合性 网络标准,与系统解耦 强依赖特定操作系统实现
多对一 一个进程可绑定多个端口 一个进程只有一个PID
一对多 一个端口只能被一个进程占用 不适用

核心原因 :PID是操作系统内部概念,而网络通信需要跨平台、跨系统。如果强绑PID,那么Windows和Linux的PID管理方式不同,无法互通。

端口号是TCP/IP协议栈的一部分,独立于操作系统。

另外,一个进程可以同时监听多个端口(比如一个Web服务器同时监听80和443),而一个端口只能被一个进程占用。

简要总结:

  • PID:操作系统内部标识进程
  • Port:网络层面标识进程
  • 为什么不用 PID?会让网络与系统强耦合,所以专门设计端口号

2.5 源端口与目的端口:谁发的,发给谁

传输层协议(TCP/UDP)的报文头中,有两个关键的字段:

  • 源端口号:发送方进程绑定的端口

  • 目的端口号:接收方进程绑定的端口

就是在描述 "数据是谁发的,要发给谁 "

这样,对方回复时,就知道把数据回给哪个进程

复制代码
例如:你的QQ(端口 12345) 发送消息 → 腾讯服务器(端口 80)
数据包中:
  源IP = 你的IP,源端口 = 12345
目的IP = 服务器IP,目的端口 = 80

服务器处理完后,回复给你的QQ时,会把源端口和目的端口交换:

  • 源IP = 服务器IP,源端口 = 80

  • 目的IP = 你的IP,目的端口 = 12345

🌰 小比喻:

  • 你写信给"上海XX公司 财务部 收"(目的端口=财务部)

  • 落款写"北京YY小区 张三 寄"(源端口=张三)

  • 对方回信时,写"北京YY小区 张三 收"(目的端口指向你),落款"上海XX公司 财务部"(源端口不变)

三、理解socket

Socket = IP 地址 + 端口号 --》 唯一标识一个网络进程

1. 把IP地址和端口号组合起来,就能在整个互联网中唯一标识一个进程

这个组合,就叫 Socket(套接字)

  1. 网络通信本质上就是两个socket之间的对话。

  2. 更进一步,一个完整的网络连接由四元组唯一确定:

    { 源IP, 源端口, 目的IP, 目的端口 }

  1. 所以,网络通信的本质,也是进程间通信

网络通信的本质是进程间通信(IPC)

我们之前学过的进程间通信(管道、消息队列、共享内存)都在同一台主机 上。

网络通信 ,就是跨主机的进程间通信------只不过借助了网络协议栈。

🧠 这一点非常重要:socket编程,本质就是在写一个能跟另一台机器上的进程"说话"的程序。

四、传输层的典型代表

4.1 TCP(传输控制协议)

  • 有连接
  • 可靠传输
  • 面向字节流
  • 像:打电话(先接通再说话)

4.2 UDP(用户数据报协议)

  • 无连接
  • 不可靠
  • 面向数据报
  • 像:寄信(直接扔邮箱,不管收没收到)

这部分先记特点,后面深入学。

特性 TCP UDP
全名 传输控制协议 用户数据报协议
连接 有连接(先打电话,再说话) 无连接(直接扔包裹,不管收到没)
可靠性 可靠传输(确认、重传、排序) 不可靠传输(丢了不重发,顺序可能乱)
传输方式 面向字节流(像水管,无边界) 面向数据报(一个个独立包,有边界)
速度 较慢(因为可靠性开销) 较快
典型应用 HTTP、HTTPS、FTP、SSH、大部分文件下载 视频直播、DNS查询、网络游戏(部分)

五、网络字节序

5.1 什么是字节序?

对于一个多字节整数(比如 0x12345678),在内存中存储有两种方式:

  • 大端(Big-Endian):高位字节存在低地址。例如 0x12 在低地址。

  • **小端(Little-Endian):****低位字节存在低地址。**例如 0x78 在低地址。

我们的x86 PC是小端 的,而很多网络设备、ARM等可能是大端的。

如果不做转换,直接发送内存中的整数,对方会解析错。

5.2 TCP/IP规定:网络字节序 = 大端

为了保证所有设备都能正确解析,发送前,主机字节序 要转为网络字节序(大端);接收后,再转回主机字节序。

操作系统提供了四个便捷函数:

函数 含义
htonl() Host to Network Long (32位整数转换)
htons() Host to Network Short (16位整数转换)
ntohl() Network to Host Long
ntohs() Network to Host Short

h:host(主机)

n:network(网络)

s:short(16 位,端口)

l:long(32 位,IP)

🌰 小比喻:

你和英国人交流,要先把中文(小端)翻译成英文(大端),收到英文回复后再翻译回中文。

在编程中,IP地址(32位整数)和端口号(16位整数) 都需要用这些函数进行转换,然后再填充到socket地址结构中。

六、socket编程接口速览

下面的C语言接口是POSIX标准,几乎所有操作系统都支持。

复制代码
// 1. 创建socket描述符(类似打开文件)
int socket(int domain, int type, int protocol);

// 2. 绑定端口和IP(服务器必须做)
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

// 3. 监听连接(TCP服务器)
int listen(int sockfd, int backlog);

// 4. 接受连接(TCP服务器)
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

// 5. 建立连接(TCP客户端)
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

sockaddr结构:通用地址

为了支持IPv4、IPv6、Unix域套接字等不同地址格式,socket API使用一个"通用"结构 struct sockaddr

复制代码
struct sockaddr {
    sa_family_t sa_family;   // 地址类型,如 AF_INET
    char        sa_data[14]; // 地址数据(IP、端口等)
};

实际编程中,我们使用 IPv4专用结构 sockaddr_in:

复制代码
struct sockaddr_in {
    sa_family_t    sin_family;   // AF_INET
    in_port_t      sin_port;     // 端口号(网络字节序)
    struct in_addr sin_addr;     // IP地址(网络字节序)
};

其中 in_addr 就是一个32位整数:

复制代码
struct in_addr {
    in_addr_t s_addr;   // 32位IP地址
};

为什么需要sockaddr?

因为API要统一处理多种协议族的地址,所以设计了一个抽象类型 sockaddr*

使用时,你把 sockaddr_in 结构体的指针强制转换(struct sockaddr*) 传给函数,函数内部根据 sin_family 字段判断是哪种地址类型。

🌰 小比喻:
sockaddr 就像"包裹"的通用标签,里面写着包裹类型(是快递信、还是生鲜、还是文件)。

真正寄快递时,你会用一个具体的盒子(sockaddr_in)去包装,然后告诉快递员"我这是一个通用包裹"(强制转型)。

相关推荐
Joseph Cooper1 小时前
Linux HID 子系统实战:从虚拟键盘到 input 事件上报
linux·c语言·计算机外设
星恒讯工业路由器1 小时前
4G点对点组网技术详解
网络
吴爃1 小时前
Spring Boot 项目在 K8S 中的打包、部署与运维发布实践
运维·spring boot·kubernetes
Elastic 中国社区官方博客2 小时前
在 Elastic 中使用 MCP 自动化用户旅程以进行合成监控
大数据·运维·人工智能·elasticsearch·搜索引擎·自动化·可用性测试
长安链开源社区2 小时前
学者观察 | 基于区块链的隐私计算技术——北京理工大学教授祝烈煌
运维·区块链
萧行之2 小时前
Docker部署Loki+Grafana+Vector实现全服务器日志监控(含N8N/SSH/Fail2ban监控)
服务器·docker·grafana
learning-striving2 小时前
Ubuntu26.04下载安装教程
运维·服务器·vmware·虚拟机
码上行动 662 小时前
用U盘制作系统盘以及如何装系统
运维
byoass2 小时前
企业云盘数据备份与恢复策略:定时备份增量备份异地容灾实战
网络·安全·云计算