【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)去包装,然后告诉快递员"我这是一个通用包裹"(强制转型)。

相关推荐
llrraa201014 小时前
配置docker国内镜像源
运维·docker·容器
starvapour14 小时前
Ubuntu切换到Fcitx5中文输入法
linux·运维·ubuntu
凯勒姆14 小时前
网工网络设备原理及配置
网络·智能路由器
1892280486115 小时前
NY382固态MT29F32T08GSLBHL8-24QM:B
大数据·服务器·人工智能·科技·缓存
上海云盾-小余15 小时前
网站恶意爬虫拦截策略:智能识别与封禁实操方案
网络·爬虫·安全·web安全
xhbh66615 小时前
网关端口映射和路由器端口转发有什么区别?配置要点全解析
运维·服务器·网络·智能路由器·端口映射·映射·无痕网关
STDD15 小时前
Soulmask《灵魂面具》 专用服务器搭建教程
运维·服务器·github
lolo大魔王15 小时前
Linux的监测程序
linux·运维·github
半壶清水15 小时前
用P4 Tutorial、BMv2 和 Mininet‌解析网络第一集------模拟环境搭建
运维·服务器·网络·网络协议·tcp/ip
.YYY15 小时前
RHCE--Linux循环执行的例行性任务:crontab从入门到精通
linux·运维·服务器