Linux之网络(一)---socket

📅 2026/8/21 6:43:42
Linux之网络(一)---socket
前提知识IP 在全网范围内唯一标识一台主机。 源 IP标识数据从哪一台主机发出 目的 IP标识数据要交付到哪一台主机但是数据送到主机并不是最终目的数据最终是给人使用的聊天、下载、浏览网页。 而电脑里QQ、迅雷、浏览器本质都是进程进程是人在操作系统里的代表。真正最终目标把数据交付给目标主机上对应的进程。问题来了一台主机上同时运行几十上百个进程数据抵达主机后操作系统如何区分该交给哪一个进程只依靠 IP 地址不够还需要端口号port同一台电脑上同时跑浏览器、QQ、迅雷都会收发网络数据包。 如果没有端口号区分操作系统收到网络数据根本不知道该交给哪个软件。 端口号就相当于进程的门牌号。pid不就可以标识进程的唯一性???为什么还需要端口号??概念是什么谁在用作用范围PID进程 ID操作系统给进程分配的编号操作系统内核用仅在单台主机内部有效端口号Port网络通信中标识进程的编号网络协议栈传输层用在网络通信中有效pid是系统观念,有端口号可以对其进行解耦pid每个进程都有,但不是所有进程都需要进行通行原因 1PID 是动态的每次启动都变不稳定同一个程序比如 QQ今天启动 PID 是 1234明天重启可能就是 5678。如果网络通信靠 PID 寻址对方主机怎么知道你这次 QQ 的 PID 是多少总不能每次启动都通知全世界吧而端口号是程序自己指定 / 绑定的可以固定。比如 HTTP 服务永远绑定 80 端口不管进程 PID 怎么变客户端只要访问 80 端口就能找到服务。原因 2PID 是操作系统内部概念不同系统格式不一样Linux 的 PID 是整数Windows 的进程 ID 格式又不同其他操作系统可能还有别的实现网络是跨平台的Linux 要和 Windows 通信如果用 PID 寻址两边操作系统对 PID 的理解都不一样根本无法统一。而端口号是 TCP/IP 协议标准规定的全世界所有操作系统统一16 位无符号整数0~65535。不管什么系统端口号含义完全一致。原因 3不是所有进程都需要网络通信pid 每个进程都有但不是所有进程都需要进行通信。一台主机上同时运行几百个进程桌面进程、输入法进程、系统后台服务…… 绝大多数进程根本不收发网络数据。如果用 PID 作为网络标识等于给所有进程都分配了一个 网络地址但大部分根本用不上浪费且混乱。端口号是按需分配的只有需要网络通信的进程才会绑定端口不需要通信的进程不占端口干净高效。原因 4一个进程可以绑定多个端口一个端口只能被一个进程占用一个 Web 服务器进程可以同时绑定 80 端口HTTP和 443 端口HTTPS反过来80 端口同一时刻只能被一个进程占用如果用 PID 代替端口号一个进程只有一个 PID怎么同时提供多种网络服务做不到。端口号机制更灵活进程可以按需绑定多个端口提供多种服务。ip vs port比如我要投诉一个,先拨打,在说明工号10086 --- 4321概念电话类比作用所属分层IP 地址总机号码 (10086)定位主机哪台电脑网络层Port 端口员工工号 (4321)定位进程哪个软件传输层10086客服总机号码 ≈ IP 地址先拨打总机 10086用来定位整个客服中心一台主机保证电话可以送到这个机构。4321员工工号 ≈ 端口号 port接通总机之后再报工号 4321用来定位客服中心里具体的工作人员主机内进程。OS是如何讲收到的数据转发给特定端口的进程????端口哈希表操作系统内核会维护一张哈希表图上横向数组就是哈希桶key端口号示例8080value对应进程的 PCB /socket 缓冲区流程拿到目的端口 → 哈希运算定位桶 → 查表找到绑定该端口的 socket → 将报文放入 socket 接收缓冲区唤醒对应进程读取数据找到进程如何把网络数据,交给这个进程Linux 一切皆文件socket 本质是文件描述符 fd内核通过缓冲区完成内核态 → 用户态的数据交付端口号范围划分• 0 - 1023 : 知名端口号, HTTP, FTP, SSH 等这些广为使用的应用层协议, 他们的端口号都 是固定的.• 1024 - 65535 : 操作系统动态分配的端口号. 客户端程序的端口号, 就是由操作系统从这个范 围分配的.IP 地址用来标识互联网中唯一的一台主机 port 用来标识该主机上唯一的一个网络进程IPPort 就能表示互联网中唯一的一个进程所以通信的时候本质是两个互联网进程代表人来进行通信{srcIpsrcPortdstIpdstPort} 这样的4元组就能标识互联网中唯二的两个进程所以网络通信的本质也是进程间通信网络字节序我们已经知道,内存中的多字节数据相对于内存地址有大端和小端之分, 磁盘文件中的多字节数据相对于 文件中的偏移地址也有大端小端之分, 网络数据流同样有大端小端之分. 那么如何定义网络数据流的地 址呢?为使网络程序具有可移植性,使同样的C代码在大端和小端计算机上编译后都能正常运行,可以调用以下 库函数做网络字节序和主机字节序的转换。网络字节序转换函数htonl/htons/ntohl/ntohs函数作用长度htonl()host‑to‑network‑long主机字节序 → 网络字节序32 位 (IP 地址)4 字节htons()host‑to‑network‑short主机字节序 → 网络字节序16 位 (端口号)2 字节ntohl()network‑to‑host‑long网络字节序 → 主机字节序32 位4 字节ntohs()network‑to‑host‑short网络字节序 → 主机字节序16 位2 字节这些函数名很好记, h 表示 host , n 表示 network , l 表示 32 位长整数, s 表示 16 位短整 数。例如 htonl 表示将 32 位的长整数从主机字节序转换为网络字节序,例如将IP地址转换后准备发 送。如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回;如果主机是大端字节序,这些函数不做转换,将参数原封不动地返回socket编程接口socket 常见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);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结构 体指针做为参数;