发展历史:
ARPA网络(Advanced Research Projects Agency Network),即阿帕网,是互联网的雏形。它诞生于20世纪60年代末,1969年12月,美国高级研究计划局(ARPA)成功地将加州大学洛杉矶分校、加州大学圣巴巴拉分校、斯坦福大学和犹他大学四所大学的4台大型计算机连接在一起,标志着阿帕网的正式运行。
NCP协议
阿帕网早期采用的是NCP(Network Control Protocol)协议。NCP的出现解决了1822协议的一些不足,提供了一种标准方法来在不同主机的不同进程间建立可靠的、流量控制的双向通信链接。NCP协议的出现是协议分层概念的一个早期例子,并被纳入OSI模型。然而,NCP也存在一些明显的缺点:
- 可扩展性差:NCP只能在“同构”环境中运行,即网络上的所有计算机都必须运行相同的操作系统。这限制了不同类型的计算机和不同操作系统的计算机之间的相互通信。
- 没有纠错功能:当数据在传输过程中发生错误时,NCP无法检测或纠正这些错误。
TCP/IP协议
随着网络技术的发展,NCP协议的局限性越来越明显。20世纪70年代中期,为了实现异构网络之间的互连,科学家们开始研究新的网络协议。1980年,TCP/IP协议研制成功。1983年,ARPANET正式采用TCP/IP协议,取代了NCP。TCP/IP协议具有明显的优势,它可以在各种硬件和操作系统上实现互操作,不受硬件和操作系统的限制。
TCP(传输控制协议)
TCP(Transmission Control Protocol)即传输控制协议。其功能包括:
差错检测和处理:负责检测网络传输中的差错并进行处理,以保证数据的完整性。在数据传输过程中,TCP会将数据分割成多个数据包,为每个数据包编号,并在接收端按照编号重新组装数据。
- 流量控制:根据接收端的处理能力,动态调整发送端的数据发送速率,避免接收端因处理不过来而丢失数据。
- 可靠性高:TCP提供了可靠的、面向连接的传输服务,能保证数据的可靠传输。
IP(网际协议)
实现不同网络之间的连接:负责将数据包从源地址传输到目标地址,是互联网的核心协议。
简单的不可靠服务:IP层不保证数据的可靠性和完整性,数据的可靠性和完整性由上层的TCP协议负责。
UDP(用户数据报协议)
传输的实时性较高:UDP在通信之前不需要建立连接,具有占用资源少、通信效率高的优点。不保证数据的完整性:UDP提供的是无连接的、不可靠的传输服务。它在发送数据时,只是简单地将数据包发送出去,而不关心接收端是否正确接收到数据。
网络协议
一、IP地址
1. 基本概念
定义:唯一标识网络设备的逻辑地址,采用分层结构(网络号 + 主机号)。
点分十进制 :32位二进制分为4个字节,如 `192.168.1.1`。
2. 分类与范围
类别 首字节范围 网络号长度 主机号长度 私有地址范围
- A类 0~127 8位(1字节) 24位(3字节) `10.0.0.0 ~ 10.255.255.255`
- B类 128~191 16位(2字节) 16位(2字节) `172.16.0.0 ~ 172.31.255.255`
- C类 192~223 24位(3字节) 8位(1字节) `192.168.0.0 ~ 192.168.255.255`
- D类 224~239 组播地址
- E类 240~255 保留地址
3. 公有地址与私有地址
- 公有地址 :全球唯一,由Inter NIC分配(如 `8.8.8.8`)。
- 私有地址 :内部网络使用,需NAT转换才能访问公网(如 `192.168.1.1`)。
二、IP数据包格式(WireShark分析)
1. IP报文结构
字段 | 长度 | 作用 |
版本号 | 4位 | IPv4(4)或IPv6(6) |
首部长度 | 4位 | 报头长度(单位:4字节),范围20~60字节 |
服务类型 | 8位 | 优先级(PPP)、延迟(D)、吞吐量(T)、可靠性(R)、成本(M) |
总长度 | 16位 | 数据包总长度(最大1500字节,超限则分片) |
标识符 | 16位 | 唯一标识数据包,用于分片重组。 |
标志位 | 3位 | 分片控制(0:允许分片;1:禁止分片;2:后续分片)。 |
片偏移 | 13位 | 分片数据的位置(单位:8字节)。 |
生存时间TTL | 8位 | 每经过一台路由器减1,为0时丢弃(Windows默认128,Linux默认64)。 |
协议 | 13位 | 上层协议类型(6:TCP;17:UDP)。 |
首部校验和 | 16位 | 校验报头完整性。 |
源/目的IP | 32位×2 | 发送方和接收方的IP地址。 |
2. 分片与重组
- 分片触发条件 :数据包总长度 > 网络MTU(如以太网默认1500字节)。
- 重组规则 :根据标识符和片偏移按序拼接。
三、TCP连接管理:三次握手与四次挥手
1. 三次握手(建立连接)
- SYN:客户端发送 `SYN=1, seq=x`,进入 `SYN-SENT` 状态。
- SYN-ACK:服务端回应 `SYN=1, ACK=1, seq=y, ack=x+1`,进入 `SYN-RCVD` 状态。
- ACK:客户端确认 `ACK=1, seq=x+1, ack=y+1`,双方进入 `ESTABLISHED` 状态。
目的:
验证双方收发能力。
防止失效连接请求(如网络延迟导致重复SYN)。
2. 四次挥手(断开连接)
- FIN :主动方发送 `FIN=1, seq=u`,进入 `FIN-WAIT-1` 状态。
- ACK :被动方回应 `ACK=1, ack=u+1`,进入 `CLOSE-WAIT` 状态。
- FIN :被动方处理完数据后发送 `FIN=1, seq=v`,进入 `LAST-ACK` 状态。
- ACK :主动方确认 `ACK=1, ack=v+1`,进入 `TIME-WAIT` 状态(等待2MSL后关闭)。
目的:
确保数据完整传输,防止半关闭状态导致数据丢失。
2MSL等待:防止最后一个ACK丢失,确保被动方正常关闭。
四、关键问题解答
Q1:为什么建立连接需要三次握手?
防止历史连接干扰:若两次握手,延迟的SYN可能导致服务端误开连接。
同步初始序列号:确保双方序列号一致,保证数据传输有序。
Q2:为什么断开连接需要四次挥手?
半关闭状态:被动方需处理未完成数据后再发送FIN,因此需两次确认。
总结
IP地址是网络通信的基础,需理解分类与公私区别。
IP数据包通过分片与重组适应不同网络,各字段协同保障传输。
TCP连接管理通过握手与挥手确保可靠性,三次握手防干扰,四次挥手保完整。掌握这些核心概念,可深入理解网络通信机制,为协议分析与故障排查打下基础!
网关、子网掩码与DNS服务器核心知识总结
一、子网掩码(Subnet Mask)
1. 定义与作用
功能:
- 区分网络与主机:通过与IP地址按位“与”运算,提取网络号和主机号。
- 划分子网:将大网络分割为小网络,优化地址分配和管理。
- 格式:32位二进制数,通常以点分十进制表示(如 `255.255.255.0`)。
2. 计算方法
网络号:`IP地址 & 子网掩码`
示例:
- IP地址:`192.168.3.99`
- 子网掩码:`255.255.255.0`
- 网络号:`192.168.3.0`
- 主机号:`IP地址 & 反子网掩码`
- 反子网掩码:`~子网掩码`(如 `0.0.0.255`)
- 主机号:`99`
3. 默认子网掩码
IP地址类别 | 默认子网掩码 | 示例 |
A类 | 255.0.0.0 | 10.0.0.1/8 |
B类 | 255.255.0.0 | 172.16.0.100/16 |
C类 | 255.255.255.0 | 192.168.1.58/2 |
二、网关(Gateway)
1. 定义
- 功能:不同网络间通信的“出口”,通常是路由器的IP地址(如 `192.168.1.1`)。
- 类比:如同校门连接校内与校外,网关连接局域网与互联网。
2. 典型配置
默认网关:
- 家庭网络:`192.168.x.1` 或 `192.168.x.254`。
- 企业网络:核心路由器地址(如 `10.0.0.1`)。
3. 作用
- 路由转发:将本地数据包发送到外部网络。
- 协议转换:如NAT(网络地址转换)。
三、DNS服务器(Domain Name System)
1. 功能
- 域名解析:将人类可读的域名(如 `www.baidu.com`)转换为机器可读的IP地址(如 `183.2.172.185`)。
- 缓存加速:本地DNS缓存减少重复查询时间。
2. 常见DNS服务器
服务商 DNS地址
- 谷歌DNS `8.8.8.8`
- 阿里DNS `223.5.5.5`
- 114DNS `114.114.114.114`
3. DNS查询示例
- bash
- ping www.baidu.com
- 解析结果:183.2.172.185
四、实际应用与工具
1. WireShark抓包分析
过滤规则 :
- 协议过滤:`tcp`、`http`。
- IP过滤:`ip.src == 192.168.0.88`。
- 端口过滤:`tcp.port == 80`。
- 观察三次握手 : SYN → SYN-ACK → ACK (通过追踪TCP流查看)。
五、TCP连接管理
1. 三次握手(建立连接)
- SYN :客户端发送请求(`SYN=1, seq=x`)。
- SYN-ACK :服务端响应(`SYN=1, ACK=1, seq=y, ack=x+1`)。
- ACK :客户端确认(`ACK=1, ack=y+1`)。
2. 四次挥手(断开连接)
- FIN → 2. ACK → 3. FIN → 4. ACK (等待2MSL确保数据完整)。
六、总结
- 子网掩码 :网络划分与地址解析的核心工具。
- 网关 :跨网络通信的必经之路。
- DNS :域名与IP地址的“翻译官”。
- 工具与协议 :WireShark抓包和TCP连接管理是网络分析的基础。
UDP协议与子网计算核心知识总结
一、UDP协议详解
1. 基本特性
- 无连接 :无需建立连接,直接发送数据报。
- 不可靠传输 :无确认、重传、流量控制机制,可能丢包或乱序。
- 高效性 :首部仅8字节(TCP为20-60字节),适用于实时通信。
- 适用场景 :音视频通话(如Zoom)、在线游戏、DNS查询、广播/组播。
2. UDP报文结构
字段 | 长度(位) | 作用 |
源端口号 | 16 | 发送方端口号(可选,可为0) |
目的端口号 | 16 | 接收方端口号(必须指定) |
长度 | 16 | 整个UDP数据报的总长度(字节) |
校验和 | 16 | 验证数据完整性(可选) |
数据 | 16 | 实际传输的载荷数据 |
3. UDP vs TCP对比
特性 | UDP | TCP |
连接性 | 无连接 | 面向连接(三次握手) |
可靠性 | 不可靠 | 可靠(确认、重传) |
效率 | 高 | 低 |
首部开销 | 8字节 | 20-60字节 |
适用场景 | 实时通信、广播 | 文件传输、网页访问 |
4. 适用场景
- 实时音视频传输(如直播、VoIP)。
- 广播/组播通信(如局域网设备发现)。
- 对可靠性要求低但实时性要求高的场景(如游戏状态同步)。
5. 与TCP对比
对比项 | TCP | UDP |
连接性 | 面向连接(三次握手) | 无连接 |
可靠性 | 可靠(重传、确认机制) | 不可靠 |
效率 | 低(首部20字节) | 高(首部8字节) |
应用场景 | 文件传输、邮件 | 实时音视频、广播 |
二、子网计算核心方法
1. 子网划分原理**
- 目的 :将一个大的IP网络划分为多个子网,提高地址利用率。
- 关键操作 :从主机位“借位”作为子网位。
- 公式 :
子网数 :\( 2^{\text{借位数}} \)
每个子网可用主机数**:\( 2^{\text{剩余主机位}} - 2 \)(减去网络地址和广播地址)。
2. 示例1:B类地址划分(172.16.0.0/16 → /17)
- 借位数 :1位
- 子网数 :\( 2^1 = 2 \)
- 每个子网可用主机数 :\( 2^{15} - 2 = 32766 \)
- 子网划分结果:
子网 | 网络地址 | 可用主机范围 | 广播地址 |
子网1 | 172.16.0.0/17 | 172.16.0.1 ~ 172.16.127.254 | 172.16.127.255 |
子网2 | 172.16.128.0/17 | 172.16.128.1 ~ 172.16.255.254 | 172.16.255.255 |
3. 示例2:C类地址划分(192.168.1.53/27)**
- 借位数 :3位(原24位 → 27位)
- 子网数 :\( 2^3 = 8 \)
- 每个子网可用主机数 :\( 2^5 - 2 = 30 \)
- 子网掩码 :255.255.255.224(二进制:`11111111.11111111.11111111.11100000`)
- 子网划分结果 (部分):
子网 | 网络地址 | 可用主机范围 | 广播地址 |
子网1 | 192.168.1.0/27 | 192.168.1.1 ~ 1.30 | 192.168.1.31 |
子网2 | 192.168.1.32/27 | 192.168.1.33 ~ 1.62 | 192.168.1.63 |
子网3 | 192.168.1.64/27 | 192.168.1.65 ~ 1.94 | 192.168.1.95 |
4. (192.168.1.127/26)
- 子网掩码 :255.255.255.192(二进制:`11111111.11111111.11111111.11000000`)
- 借位数 :2位 → 子网数 :\( 2^2 = 4 \)
- 每个子网可用主机数 :\( 2^6 - 2 = 62 \)
- 子网划分结果 :
子网 | 网络地址 | 可用主机范围 | 广播地址 |
子网1 | 192.168.1.0/26 | 192.168.1.1 ~ 1.62 | 192.168.1.63 |
子网2 | 192.168.1.64/26 | 192.168.1.65 ~ 1.126 | 192.168.1.127 |
子网3 | 192.168.1.128/26 | 192.168.1.129 ~ 1.190 | 192.168.1.191 |
子网4 | 192.168.1.192/26 | 192.168.1.193 ~ 1.254 | 192.168.1.255 |
当前IP(192.168.1.127)归属:
- 子网2 (网络地址192.168.1.64/26)。
- 广播地址:192.168.1.127(注意:此IP不可用于主机)。
三、UDP协议与客户端实现
UDP客户端代码实现
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main(int argc, char* argv[]) {
// 1. 创建Socket
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
// 2. 配置目标地址(IP+端口)
struct sockaddr_in dest_addr = {
.sin_family = AF_INET,
.sin_port = htons(atoi(argv[2])),
.sin_addr.s_addr = inet_addr(argv[1])
};
// 3. 发送数据
send_data(sockfd, &dest_addr, sizeof(dest_addr));
// 4. 关闭Socket
close(sockfd);
}
2. 关键函数说明
- socket() :创建UDP套接字(`SOCK_DGRAM`)。
- sendto() :发送数据包,需指定目标地址。
- htons() :将端口号转换为网络字节序。
- inet_addr() :将IP字符串转换为二进制格式。
3. 错误处理
- 检查命令行参数数量(需提供IP和端口)。
- 使用`perror`输出错误信息(需修正代码中的`EXIT_FALLURE`为`EXIT_FAILURE`)。
4. 发送数据逻辑
void send_data(int sockfd, struct sockaddr_in* pdest_addr, int length) {
char buf[256];
while (1) {
printf("> ");
fgets(buf, sizeof(buf), stdin);
buf[strlen(buf)-1] = '\0'; // 去除换行符
sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr*)pdest_addr, length);
if (strcmp(buf, "quit") == 0) break;
}
}
三、网络测试工具(NetAssist)使用
1. 配置步骤
- 协议类型:选择**UDP**。
- 本地主机地址:本机IP(如`192.168.1.100`)。
- 本地端口:接收数据的端口(如`8080`)。
2. 测试流程
- 客户端发送数据后,NetAssist的接收区会显示内容。
- 输入`quit`可终止发送循环。
五、常见问题
1. 代码编译错误 :
- 修正`EXIT_FALLURE`为`EXIT_FAILURE`。
- 检查`inet_aton`参数是否正确(需传递`struct in_addr`指针)。
2. 数据未接收 :
- 确保目标IP和端口配置正确。
- 检查防火墙是否阻止UDP通信。
3. 文件读取失败 :
检查文件路径和权限。
四、UDP服务端代码实现代码
一、核心函数API详解
1. bind函数
- 功能:将套接字与指定的IP和端口绑定。
- 参数说明:
- sockfd :已创建的套接字文件描述符。
- addr`:指向`struct sockaddr`的指针,通常使用`struct sockaddr_in`结构体填充地址信息。
- addrlen :地址结构体的大小(单位:字节)。
关键结构体 :
c
struct sockaddr_in {
sa_family_t sin_family; // 地址族(AF_INET)
in_port_t sin_port; // 端口号(网络字节序)
struct in_addr sin_addr; // IP地址(网络字节序)
};
```
2. recvfrom函数
功能 :接收UDP数据报,并获取发送方的地址信息。
参数说明 :
- ockfd:已绑定的套接字文件描述符。
- buf:接收数据的缓冲区首地址。
- len:缓冲区大小。
- src_addr:保存发送方地址的结构体指针(可为`NULL`)。
- addrlen:地址结构体长度的指针(输入时为缓冲区大小,输出时为实际地址长度)。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
void receive_message(int sockfd);
int main(int argc, char* argv[]) {
if (argc != 3) {
fprintf(stderr, "Usage: %s <IP> <Port>\n", argv[0]);
exit(EXIT_FAILURE);
}
// 创建UDP套接字
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd == -1) {
perror("socket");
exit(EXIT_FAILURE);
}
// 配置服务器地址
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(atoi(argv[2]));
inet_aton(argv[1], &server_addr.sin_addr);
// 绑定套接字
if (bind(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {
perror("bind");
goto release_resources;
}
// 进入消息接收循环
receive_message(sockfd);
release_resources:
close(sockfd);
return 0;
}
void receive_message(int sockfd) {
char buf[512];
struct sockaddr_in client_addr;
socklen_t addr_len = sizeof(client_addr);
printf("Waiting for messages...\n");
while (1) {
memset(buf, 0, sizeof(buf));
ssize_t bytes = recvfrom(sockfd, buf, sizeof(buf), 0,
(struct sockaddr*)&client_addr, &addr_len);
if (bytes == -1) {
perror("recvfrom");
continue;
}
// 打印客户端信息
printf("Client IP: %s\n", inet_ntoa(client_addr.sin_addr));
printf("Client Port: %d\n", ntohs(client_addr.sin_port));
printf("Received: %s\n", buf);
// 收到"quit"时退出
if (strncmp(buf, "quit", 4) == 0) {
break;
}
}
}
技术要点总结
1. 核心流程
创建套接字 → 绑定地址 → 接收数据 → 处理数据 → 关闭套接字。
2. 关键细节
- 网络字节序转换 :使用`htons()`和`inet_aton()`确保IP和端口格式正确。
- 地址复用 :可在`bind`前设置`SO_REUSEADDR`选项避免端口占用问题。
- 非阻塞模式 :通过`fcntl()`设置套接字为非阻塞,提升高并发场景性能。
3. 扩展应用
- 多线程处理 :为每个客户端请求创建线程,实现并发响应。
- 数据持久化 :将接收的数据写入数据库或文件。
- 安全增强 :添加数据加密(如TLS)或身份验证机制。
五、服务器端与客户端信息发送代码
1. 套接字初始化与绑定 (`init_socket` 函数)
int init_socket(const char* ip, const char* port) {
int sockfd = socket(AF_INET, SOCK_DGRAM, 0); // 创建UDP套接字
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(atoi(port)); // 端口转换(字符串→网络序)
inet_aton(ip, &addr.sin_addr); // IP地址转换(字符串→二进制)
bind(sockfd, (struct sockaddr*)&addr, sizeof(addr)); // 绑定地址
return sockfd;
}
- 功能 :创建UDP套接字并绑定到指定IP和端口。
- 关键点 :SOCK_DGRAM 表示使用UDP协议。
bind 将套接字与地址绑定,确保服务器监听指定端口。
2. 密钥认证 (`authentication_key` 函数)
int authentication_key(const char* ip, const char* port) {
int sockfd = init_socket(ip, port); // 初始化监听套接字
struct sockaddr_in addr;
char buf[512];
while (1) {
recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr*)&addr, &addrlen); // 接收密钥
unsigned char loginstatus = (strncmp(buf, LOGIN_KEY, 4) == 0) ? LOGIN_SUCCESS : LOGIN_FAILURE;
if (loginstatus == LOGIN_SUCCESS) {
pid_t pid = fork(); // 创建子进程处理后续通信
if (pid == 0) {
close(sockfd); // 子进程关闭原套接字解除与父进程共享套接字的关系,避免干扰父进程的监听
int new_sockfd = init_socket(ip, "0"); // 绑定随机端口
sendto(new_sockfd, &loginstatus, 1, 0, (struct sockaddr*)&addr, addrlen); // 发送成功响应
recv_data(new_sockfd); // 进入数据接收循环
break;
}
} else {
sendto(sockfd, &loginstatus, 1, 0, (struct sockaddr*)&addr, addrlen); // 发送失败响应
}
}
return new_sockfd;
}
功能 :验证客户端发送的密钥,认证成功后创建子进程处理后续通信。
关键点 :
- 密钥验证逻辑 :仅比较前4个字符(`strncmp(buf, LOGIN_KEY, 4)`),可能存在安全隐患。
- 子进程设计 :子进程绑定随机端口(`"0"`),实现多客户端并发处理。
- 端口切换:认证成功后,使用新端口通信,原端口继续监听新连接。
3. 数据接收 (`recv_data` 函数)
void recv_data(int sockfd) {
struct sockaddr_in client_addr;
char buf[512];
while (1) {
recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr*)&client_addr, &addrlen); // 接收数据
printf("Client IP: %s\n", inet_ntoa(client_addr.sin_addr));
printf("Client Port: %d\n", ntohs(client_addr.sin_port));
printf("Content: %s\n", buf);
if (strncmp(buf, "quit", 4) == 0) break; // 退出条件
}
close(sockfd);
exit(EXIT_SUCCESS);
}
- 功能 :循环接收客户端数据,打印来源信息,直到收到“quit”命令。
- 关键点 :
- 使用 `recvfrom` 获取客户端地址信息(IP和端口)。
- 收到“quit”后关闭套接字并退出。
4. 信号处理 (`signal_handler` 函数)
void signal_handler(int signum) {
waitpid(-1, NULL, WNOHANG); // 非阻塞回收子进程
printf("Received signal: %s\n", strsignal(signum));
}
- 功能 :处理 `SIGCHLD` 信号,回收僵尸进程。
- 关键点:WNOHANG确保父进程不会阻塞等待子进程退出。
5. 主函数 (`main`)
int main(int argc, char* argv[]) {
signal(SIGCHLD, signal_handler); // 注册信号处理函数
int sockfd = authentication_key(argv[1], argv[2]); // 启动认证
recv_data(sockfd); // 进入数据接收循环(实际由子进程执行)
return 0;
}
流程:
- 注册信号处理器。
- 启动密钥认证。
- 主进程进入数据接收循环(实际由子进程执行)。
客户端代码:
1. 密钥发送与认证 (`send_authentication_key` 函数)
void send_authentication_key(int sockfd, struct sockaddr_in* addr, struct sockaddr_in* server_data_addr, int addrlen) {
char buf[512];
unsigned char flag;
while (1) {
fgets(buf, sizeof(buf), stdin); // 输入密钥
buf[strlen(buf)-1] = '\0'; // 去除换行符
sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr*)addr, addrlen); // 发送密钥
recvfrom(sockfd, &flag, 1, 0, (struct sockaddr*)server_data_addr, &addrlen); // 接收响应
if (flag == LOGIN_SUCCESS) break;
}
}
- 功能 :发送密钥并等待服务器认证结果。
- 关键点 :循环发送密钥直到认证成功。
server_data_addr 保存服务器新端口信息,用于后续通信
2. 数据交互 (`send_message` 函数)
void send_message(int sockfd, struct sockaddr_in* addr, int addrlen) {
char buf[512];
while (1) {
fgets(buf, sizeof(buf), stdin); // 输入消息
buf[strlen(buf)-1] = '\0'; // 去除换行符
sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr*)addr, addrlen); // 发送消息
if (strncmp(buf, "quit", 4) == 0) break; // 退出条件
}
}
- 功能:发送用户输入的消息,直到输入“quit”。
- 关键点:使用 `sendto` 发送数据到服务器的新端口。
3. 主函数 (`main`)
int main(int argc, char* argv[]) {
int sockfd = socket(AF_INET, SOCK_DGRAM, 0); // 创建UDP套接字
struct sockaddr_in addr;
addr.sin_port = htons(atoi(argv[2])); // 服务器端口
inet_aton(argv[1], &addr.sin_addr); // 服务器IP
send_authentication_key(sockfd, &addr, &server_data_addr, addrlen); // 认证
send_message(sockfd, &server_data_addr, addrlen); // 数据交互
close(sockfd);
return 0;
}
流程 :
- 创建套接字。
- 配置服务器地址。
- 发送密钥并完成认证。
- 进入数据交互循环。
- 关闭套接字退出。
- 服务器端 :通过多进程实现并发处理,但端口切换逻辑需优化。
- 客户端 :实现基本认证与交互,但缺乏健壮性设计。
- 改进方向 :增强安全性、完善错误处理、优化端口管理。
六、总结
- UDP协议 :轻量高效,适合实时通信,但需容忍数据丢失。
- 子网划分 :通过借位优化IP分配,需掌握掩码计算与子网范围推导。
- 核心公式 : 子网数 = \( 2^{\text{借位数}} \)
可用主机数 = \( 2^{\text{剩余主机位}} - 2 \)
- 这是本人的学习笔记不是获利的工具,小作者会一直写下去,希望大家能多多监督我
- 文章会每攒够两篇进行更新发布(受平台原因,也是希望能让更多的人看见)
- 感谢各位的阅读希望我的文章会对诸君有所帮助