📌 核心结论(TL;DR):
网络通信本质:是跨主机的进程间通信。靠
IP + Port(即 Socket 套接字)唯一标识网络中的进程。网络字节序:网络规定使用大端字节序。发送前必须用
htons/htonl转换。MAC vs IP:IP 是终极目标(跨网络不变),MAC 是下一跳目标(局域网内一直变)。
UDP 特性:无连接、不可靠、面向数据报。
UDP 开发核心:服务器必须
bind(且 IP 选INADDR_ANY),客户端不用显式bind。
一、 网络基础概念
计算机网络的核心目的是数据共享。为了实现不同设备、不同操作系统之间的无障碍通信,采用了TCP/IP 四层(或五层)模型(应用层、传输层、网络层、链路层)。数据在发送时自上而下层层封装(加报头),接收时自下而上层层解包(去报头)。
二、 核心身份标识:IP、Port 与 MAC
IP 地址:IPv4 是 32 位整数。标识互联网中唯一的主机,路径选择的重要依据。
端口号(Port):16 位整数。标识主机上的唯一进程。
MAC 地址:48 位。标识局域网内唯一的网卡。注意:跨网络传输时,IP 地址基本不变,但 MAC 地址每经过一个路由器就会改变。
三、 UDP 协议与核心 API
UDP(User Datagram Protocol)不保证数据一定到达,但速度快。
socket():创建套接字。bind():绑定端口(仅服务器端)。sendto():发送数据。recvfrom():接收数据。
四、 代码实战:UDP 回显服务器
struct sockaddr_in server_addr; server_addr.sin_family = AF_INET; server_addr.sin_port = htons(8888); // 关键:INADDR_ANY 表示绑定所有本机网卡IP server_addr.sin_addr.s_addr = INADDR_ANY; bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr));五、 踩坑记录:云服务器端口不通
现象:本地测试正常,但用别的电脑连不上云服务器的 UDP 端口。
原因:云厂商(如阿里云、腾讯云)有安全组防火墙机制。
解决:登录云控制台,在安全组入站规则中,放行对应的 UDP 端口。
自我实战操作
cd ~/linux_study mkdir network_test cd network_test实战 1:UDP 服务端(UdpServer)
vim udp_server.c(输入以下 C 语言代码,最纯粹的底层逻辑)
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #define PORT 8888 #define BUF_SIZE 1024 int main() { // 1. 创建 UDP 套接字 int server_fd = socket(AF_INET, SOCK_DGRAM, 0); if (server_fd < 0) { perror("socket"); return 1; } // 2. 绑定 IP 和 端口 struct sockaddr_in server_addr; memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(PORT); // 端口转网络字节序 server_addr.sin_addr.s_addr = INADDR_ANY; // 监听任意IP(超级重要) if (bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) { perror("bind"); return 1; } printf("UDP 服务器已启动,监听端口 %d...\n", PORT); // 3. 循环接收和发送 char buffer[BUF_SIZE]; struct sockaddr_in client_addr; socklen_t client_len = sizeof(client_addr); while (1) { // 接收客户端发来的数据 int n = recvfrom(server_fd, buffer, BUF_SIZE - 1, 0, (struct sockaddr*)&client_addr, &client_len); if (n > 0) { buffer[n] = '\0'; printf("收到来自 %s:%d 的消息: %s\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), buffer); // Echo (原样发回) sendto(server_fd, buffer, n, 0, (struct sockaddr*)&client_addr, client_len); } } close(server_fd); return 0; }实战 2:UDP 客户端(UdpClient)
vim udp_client.c(输入以下代码)
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #define SERVER_IP "127.0.0.1" // 本地测试 #define PORT 8888 #define BUF_SIZE 1024 int main() { // 1. 创建 UDP 套接字 (不需要显式 bind,系统会自动分配随机端口) int client_fd = socket(AF_INET, SOCK_DGRAM, 0); if (client_fd < 0) { perror("socket"); return 1; } // 2. 配置服务器地址信息 struct sockaddr_in server_addr; memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(PORT); inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr); // 字符串IP转二进制 char buffer[BUF_SIZE]; while (1) { printf("请输入要发送的消息 (输入 quit 退出): "); fgets(buffer, BUF_SIZE, stdin); buffer[strcspn(buffer, "\n")] = 0; // 去掉换行符 if (strcmp(buffer, "quit") == 0) break; // 发送数据 sendto(client_fd, buffer, strlen(buffer), 0, (struct sockaddr*)&server_addr, sizeof(server_addr)); // 接收回显数据 struct sockaddr_in temp_addr; socklen_t temp_len = sizeof(temp_addr); int n = recvfrom(client_fd, buffer, BUF_SIZE - 1, 0, (struct sockaddr*)&temp_addr, &temp_len); if (n > 0) { buffer[n] = '\0'; printf("收到服务器回显: %s\n", buffer); } } close(client_fd); return 0; }实战 3:编译测试与网络排错
# 编译 gcc udp_server.c -o udp_server gcc udp_client.c -o udp_client测试流程:
开终端 A(服务端):
./udp_server开终端 B(客户端):
./udp_client在终端 B 输入
hello,看看终端 A 和 B 分别打印了什么。排错加餐:如果你把服务端跑在云服务器上,客户端在自己的电脑上(Windows),你必须在云服务器后台的安全组里,开放
8888的UDP 端口,否则数据会被防火墙拦截!