Linux学习-通信(网络通信)
一、Linux 应用软件编程 - 网络编程 整体框架
涵盖 文件操作、多任务并发 基础,核心聚焦 “不同主机进程间通信”,解决 物理网络连通 + 软件进程互通 问题。
二、网络通信核心概念
1. 网络标识体系
标识类型 | 作用 | 特点 |
---|---|---|
IP 地址 | 软件地址,标识主机 | 分公网(直连互联网)、私网(局域网内) |
MAC 地址 | 硬件地址,固定标识设备 | 全球唯一 |
端口号 | 标识主机内不同网络进程 | 16 位整数(0~65535) |
2. 网络协议与分层模型
层级(从下到上) | 核心功能 | 常见协议 | 典型设备 | 关键技术/标识 | 应用场景示例 |
---|---|---|---|---|---|
1. 物理层 | 定义物理设备标准,处理物理介质上的原始比特流传输,规定电气、机械、接口特性 | 无独立协议(依赖硬件标准) | 集线器、中继器、网线、光纤、无线AP | 比特流、物理接口(RJ-45、LC) | 以太网物理连接、Wi-Fi信号传输 |
2. 数据链路层 | 封装原始比特流为“帧”,实现相邻节点间可靠传输,处理帧同步、差错控制、MAC寻址 | 以太网协议(Ethernet)、PPP(点到点协议)、HDLC(高级数据链路控制协议) | 交换机、网卡 | 帧、MAC地址(设备硬件地址) | 局域网内设备间数据转发 |
3. 网络层 | 实现跨网络的“数据包”路由与转发,通过IP地址定位主机,选择最优传输路径 | IP(IPv4/IPv6)、ICMP(控制报文协议,如ping)、ARP(IP转MAC)、RARP(MAC转IP) | 路由器、三层交换机 | 数据包、IP地址(网络逻辑地址) | 不同局域网间数据跨网段传输 |
4. 传输层 | 提供端到端通信服务,控制数据传输的可靠性/效率,处理顺序、流量、差错控制 | TCP(面向连接、可靠传输)、UDP(无连接、高效传输) | 无(依赖主机操作系统) | 字节流(TCP)、数据报(UDP)、端口号(进程标识) | 文件传输(TCP)、视频会议(UDP) |
5. 应用层 | 直接为用户应用程序提供服务,定义应用间通信的协议与数据格式 | HTTP(网页浏览)、HTTPS(安全网页)、FTP(文件传输)、SMTP(邮件发送)、DNS(域名解析) | 计算机、服务器(运行应用程序) | 应用数据(如HTML、文件、邮件内容) | 网页访问、文件上传下载、邮件发送 |
3. IP 地址深度解析
- 结构:
IP = 网络位 + 主机位
(通过子网掩码区分),例:192.168.0.121/24
中,24
表示网络位占 24 位。 - IP地址分类:
类别 范围 子网掩码 应用场景 A 1.0.0.0~126.255.255.255
255.0.0.0
大规模网络 B 128.0.0.0~191.255.255.255
255.255.0.0
中大规模网络 C 192.0.0.0~223.255.255.255
255.255.255.0
中小规模网络 D 224.0.0.0~239.255.255.255
- 组播/广播 E 240.0.0.0~255.255.255.254
- 实验用途 - 特殊地址:
- 私网 IP(如
10.0.0.0/8
、192.168.0.0/16
):局域网内使用,无法直连互联网。 - 回环地址(
127.0.0.0/8
):用于本机进程间通信。
- 私网 IP(如
4. 端口号
16位整形数据(unsigned short)0 ~65535
功能:标记同一主机的不同网络进程
范围 | 类型 | 说明 | 典型协议/应用 |
---|---|---|---|
1~1023 | 知名端口 | 系统/通用服务保留 | HTTP(80)、FTP(20/21)、HTTPS(443)、TFPT(69) |
1024~49151 | 注册端口 | 特殊服务使用(需 IANA 分配) | MQTT(1883/8883) |
49152~65535 | 动态/私有端口 | 应用临时分配 | 客户端随机端口 |
三、网络调试与配置工具
工具 | 作用 | 示例命令 |
---|---|---|
ping | 检测网络连通性 | ping www.baidu.com |
ifconfig | Linux 查看/配置 IP | ifconfig eth0 |
ipconfig | Windows 查看 IP | ipconfig /all |
虚拟机网络配置 | 桥接模式(直连局域网)、NAT(共享主机网络) | VMware 中设置“桥接模式” |
Linux下:
修改网络配置文件:sudo vim/etc/network/interfaces
重启网络服务:sudo /etc/init.d/networking restart
测试: ping www.baidu.com
四、网络编程实践(UDP 为例)
1. C/S 模型对比
模型 | 客户端特点 | 开发场景 | 资源加载方式 |
---|---|---|---|
B/S | 通用(浏览器) | 主要开发服务端 | 全依赖服务端加载 |
C/S | 专用客户端 | 服务端 + 客户端都需开发 | 本地可缓存资源 ,无需所有数据都请求服务器 |
2. UDP 编程流程(C/S 架构)
- 服务端:
socket()
→bind()
→recvfrom()
→sendto()
→close()
- 客户端:
socket()
→sendto()
→recvfrom()
→close()
五、关键函数解析
函数 | 功能 | 核心参数说明 |
---|---|---|
socket() | 创建套接字 | domain (AF_INET=IPv4)、type (SOCK_DGRAM=UDP) |
bind() | 绑定 IP+端口 | 需指定 struct sockaddr_in (含 IP、端口) |
sendto() | 发送数据 | 需传入对端地址(dest_addr ) |
recvfrom() | 接收数据 | 可获取发送端地址(src_addr ) |
socket
:创建套接字
int socket(int domain, int type, int protocol);
- 功能:创建一个网络套接字(通信端点),返回用于后续网络操作的文件描述符。
- 参数:
domain
:地址族(协议族),指定网络通信的地址类型。常用值:AF_INET
(IPv4)、AF_INET6
(IPv6)。type
:套接字类型,指定通信方式。常用值:SOCK_STREAM
(TCP,面向连接、可靠传输)、SOCK_DGRAM
(UDP,无连接、不可靠传输)。protocol
:具体协议,通常填0
表示使用type
对应的默认协议(如SOCK_STREAM
对应 TCP,SOCK_DGRAM
对应 UDP)。
- 返回:成功返回非负套接字描述符(
sockfd
),失败返回-1
(需检查errno
)。
bind
:绑定 IP + 端口
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
- 功能:将套接字
sockfd
与指定的IP + 端口
绑定,让系统知道该套接字监听哪个地址。 - 参数:
sockfd
:套接字(socket()
返回的文件描述符)。addr
:要绑定的地址(需强转为struct sockaddr *
,通常用struct sockaddr_in
填充)。addrlen
:地址结构体的大小(sizeof(struct sockaddr_in)
)。
- 返回:成功返回
0
,失败返回-1
(需检查errno
)。
sendto
:发送 UDP 数据到指定地址
(补充说明,与之前知识联动)
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);
- 功能:向指定地址(
dest_addr
)发送 UDP 数据。 - 参数:
dest_addr
:接收方的地址(struct sockaddr_in
填充 IP + 端口)。
recvfrom
:接收 UDP 数据并获取发送方地址
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);
- 功能:从套接字
sockfd
接收数据,同时获取 发送方的地址信息(src_addr
)。 - 参数:
sockfd
:套接字。buf
:存储接收数据的缓冲区。len
:希望接收的最大字节数。flags
:通常填0
(阻塞接收)。src_addr
:用于存储 发送方地址(需用struct sockaddr_in
解析)。addrlen
:地址结构体的大小(传入sizeof(src_addr)
的指针)。
- 返回:成功返回实际接收的字节数,失败返回
-1
。
网络地址结构体(struct sockaddr_in
)
用于描述 IPv4 网络地址,是 UDP/TCP 编程的基础数据结构。
struct sockaddr_in {sa_family_t sin_family; // 地址族(固定为 AF_INET 表示 IPv4)in_port_t sin_port; // 端口号(网络字节序)struct in_addr sin_addr; // IP 地址(网络字节序)
};// IP 地址的二进制形式(网络字节序)
struct in_addr {uint32_t s_addr;
};
参数:
sin_family
:- 必须填
AF_INET
(表示 IPv4 地址族),否则通信失败。
- 必须填
sin_port
:- 存储 端口号,但必须用 网络字节序(通过
htons
转换)。
- 存储 端口号,但必须用 网络字节序(通过
sin_addr
:- 存储 IP 地址(二进制形式,网络字节序),常用
inet_addr
或inet_pton
转换字符串 IP。
- 存储 IP 地址(二进制形式,网络字节序),常用
字节序转换(网络大端 vs 主机小端 )
网络通信要求 端口号、IP 地址 必须用 网络字节序(大端),而主机 CPU 可能是 小端,因此需要转换。
1. 核心函数
函数 | 功能 | 适用场景 |
---|---|---|
htons(host_short) | 主机短整型 → 网络字节序 | 端口号转换(16 位) |
ntohs(net_short) | 网络字节序 → 主机短整型 | 端口号转换(16 位) |
htonl(host_long) | 主机长整型 → 网络字节序 | IP 地址转换(32 位) |
ntohl(net_long) | 网络字节序 → 主机长整型 | IP 地址转换(32 位) |
2. 示例(端口号转换)
uint16_t host_port = 50000; // 主机字节序(小端)
uint16_t net_port = htons(host_port); // 转换为网络字节序(大端)
3. 字节序直观对比
- 主机小端(x86 架构):低字节存低地址,例:
0x1234
存储为0x34 0x12
- 网络大端:高字节存低地址,例:
0x1234
存储为0x12 0x34
IP 地址字符串 ↔ 二进制转换
-
inet_addr(const char *cp)
:- 功能:将字符串 IP(如
"192.168.0.171"
)转换为 网络字节序的二进制 IP(uint32_t
)。 - 示例:
struct in_addr ip; ip.s_addr = inet_addr("192.168.0.171"); // 转换为网络字节序
- 功能:将字符串 IP(如
-
inet_ntoa(struct in_addr in)
:- 功能:将二进制 IP(网络字节序)转换为 字符串 IP(如
"192.168.0.171"
)。 - 示例:
struct in_addr ip = {.s_addr = inet_addr("192.168.0.171")}; char *ip_str = inet_ntoa(ip); // 转换为字符串 "192.168.0.171"
- 功能:将二进制 IP(网络字节序)转换为 字符串 IP(如
六、练习(UDP 全双工通信)
-
服务端(
server.c
):#include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <stdio.h> #include <string.h>int main() {int sockfd = socket(AF_INET, SOCK_DGRAM, 0); // 创建 UDP 套接字struct sockaddr_in addr = {.sin_family = AF_INET, .sin_port = htons(50000), .sin_addr.s_addr = INADDR_ANY};bind(sockfd, (struct sockaddr*)&addr, sizeof(addr)); // 绑定端口char buf[1024];struct sockaddr_in client_addr;socklen_t len = sizeof(client_addr);while (1) {// 接收客户端数据 + 获取客户端地址ssize_t cnt = recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr*)&client_addr, &len);buf[cnt] = '\0';printf("收到来自 %s:%d 的数据:%s\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), buf);// 回复客户端sendto(sockfd, "已收到", 6, 0, (struct sockaddr*)&client_addr, len);}close(sockfd);return 0; }
-
客户端(
client.c
):#include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <stdio.h> #include <string.h>int main() {int sockfd = socket(AF_INET, SOCK_DGRAM, 0); // 创建 UDP 套接字struct sockaddr_in server_addr = {.sin_family = AF_INET, .sin_port = htons(50000), .sin_addr.s_addr = inet_addr("192.168.0.171")};char buf[1024];while (1) {fgets(buf, sizeof(buf), stdin); // 从终端读入数据sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr*)&server_addr, sizeof(server_addr)); // 发数据// 接收服务端回复ssize_t cnt = recvfrom(sockfd, buf, sizeof(buf), 0, NULL, NULL);buf[cnt] = '\0';printf("服务端回复:%s\n", buf);}close(sockfd);return 0; }