应用——UDP 网络编程

UDP 网络编程

一、程序分析

1.1 程序结构

客户端 (02cli.c)

复制代码
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
typedef struct sockaddr *(SA);  // 定义类型别名

int main(int argc, char **argv)
{
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (-1 == sockfd)
    {
        perror("sockfd");
        return 1;
    }
    
    struct sockaddr_in ser;  // 服务器地址
    bzero(&ser, sizeof(ser));
    ser.sin_family = AF_INET;
    ser.sin_port = htons(50000);
    ser.sin_addr.s_addr = inet_addr("192.168.14.128");

    int i = 10;
    while (i--)
    {
        char buf[512]="hello";
        sendto(sockfd,buf,strlen(buf),0,(SA)&ser,sizeof(ser));
        bzero(buf,sizeof(buf));
        recvfrom(sockfd,buf,sizeof(buf),0,NULL,NULL);
        printf("recv:%s\n",buf);
        sleep(1);
    }

    close(sockfd);
    return 0;
}

服务器 (01server.c)

复制代码
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
typedef struct sockaddr *(SA);

int main(int argc, char **argv)
{
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (-1 == sockfd)
    {
        perror("socket");
        return 1;
    }
    
    struct sockaddr_in ser, cli;
    bzero(&ser, sizeof(ser));
    bzero(&cli, sizeof(cli));
    ser.sin_family = AF_INET;
    ser.sin_port = htons(50000);
    ser.sin_addr.s_addr = inet_addr("192.168.14.128");
    
    int ret = bind(sockfd, (SA)&ser, sizeof(ser));
    if (-1 == ret)
    {
        perror("bind");
        return 1;
    }
    
    int i = 10;
    socklen_t len = sizeof(cli);
    while (1)
    {
        char buf[512] = {0};
        recvfrom(sockfd, buf, sizeof(buf), 0, (SA)&cli, &len);
        printf("recv:%s\n", buf);
        
        time_t tm;
        time(&tm);
        struct tm *info = localtime(&tm);
        sprintf(buf, "%s %d:%d:%d", buf, info->tm_hour, info->tm_min, info->tm_sec);
        
        sendto(sockfd, buf, strlen(buf) + 1, 0, (SA)&cli, len);
    }
    
    close(sockfd);
    return 0;
}

二、关键知识点解析

2.1 socket 创建

复制代码
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
// AF_INET: IPv4 地址族
// SOCK_DGRAM: UDP 协议(无连接数据报)
// 0: 自动选择协议(UDP 协议对应 IPPROTO_UDP)

2.2 地址结构体

复制代码
struct sockaddr_in {
    sa_family_t    sin_family;     // 地址族,AF_INET
    in_port_t      sin_port;       // 端口号
    struct in_addr sin_addr;       // IP 地址
    unsigned char  sin_zero[8];    // 填充字节
};

2.3 字节序转换

复制代码
ser.sin_port = htons(50000);      // 主机字节序转网络字节序(端口)
ser.sin_addr.s_addr = inet_addr("192.168.14.128");  // 字符串转网络字节序

2.4 绑定和发送

复制代码
// 服务器绑定地址
bind(sockfd, (SA)&ser, sizeof(ser));

// 发送数据
sendto(sockfd, buf, strlen(buf), 0, (SA)&ser, sizeof(ser));
// 参数:socket描述符, 数据缓冲区, 数据长度, 标志, 目标地址, 地址长度

// 接收数据
recvfrom(sockfd, buf, sizeof(buf), 0, (SA)&cli, &len);
// 参数:socket描述符, 缓冲区, 缓冲区大小, 标志, 来源地址, 地址长度指针

三、执行流程

3.1 服务器端流程

复制代码
1. socket()    创建 UDP socket
2. bind()      绑定到 192.168.14.128:50000
3. while(1)    无限循环
   ├─ recvfrom()  接收客户端数据
   ├─ printf()    打印接收到的数据
   ├─ time()      获取当前时间
   ├─ sprintf()   在数据后附加时间
   └─ sendto()    发送响应给客户端

3.2 客户端流程

复制代码
1. socket()    创建 UDP socket
2. for(10次)   循环 10 次
   ├─ sendto()    发送 "hello" 到服务器
   ├─ recvfrom()  接收服务器响应
   ├─ printf()    打印接收的数据
   └─ sleep(1)    等待 1 秒
3. close()     关闭 socket

四、代码特点

4.1 原始代码的特点

  1. 简洁直接:没有过多错误处理

  2. 固定配置:IP 和端口硬编码在代码中

  3. 简单通信:客户端发送固定字符串,服务器返回带时间戳的响应

  4. 类型别名 :使用 typedef struct sockaddr *(SA) 简化代码

4.2 代码中的细节

复制代码
// 客户端接收时忽略发送方信息
recvfrom(sockfd, buf, sizeof(buf), 0, NULL, NULL);
// NULL 表示不关心发送方地址

// 服务器发送时包含字符串结束符
sendto(sockfd, buf, strlen(buf) + 1, 0, (SA)&cli, len);
// strlen(buf) + 1 包含 '\0' 结束符

// 客户端每次发送前都初始化为 "hello"
char buf[512]="hello";  // 每次循环重新初始化

// 服务器的时间处理
sprintf(buf, "%s %d:%d:%d", buf, ...);
// 注意:这里 buf 同时作为源和目标,可能产生未定义行为

五、运行结果示例

服务器输出

复制代码
recv:hello
recv:hello
recv:hello
...
(持续接收客户端消息)

客户端输出

复制代码
recv:hello 14:30:25
recv:hello 14:30:26
recv:hello 14:30:27
...
(接收到带时间戳的响应)

六、注意事项

  1. IP地址 :代码中的 192.168.14.128 需要根据实际情况修改

  2. 时间戳问题 :服务器的 sprintf(buf, "%s %d:%d:%d", buf, ...) 可能存在逻辑问题

  3. 错误处理:实际应用中应增加更多的错误检查

  4. 端口号:50000 端口需要确保没有被占用

  5. 循环次数:客户端固定发送 10 次消息后退出

相关推荐
U-52184F692 小时前
CGAL 实战笔记:深入理解 2D 符合三角剖分与网格生成 (针对 CAM 开发)
笔记·算法
Chukai1232 小时前
第3章:基于LlamaIndex+Ollama+ChromaDB搭建本地简单RAG问答系统
开发语言·人工智能·python·rag·rag问答系统
chenyuhao20242 小时前
Linux系统编程:多线程同步与单例模式
linux·服务器·c++·后端·单例模式
飞天狗1112 小时前
C. Product 1 Modulo N(同余)
算法
私人珍藏库2 小时前
[吾爱大神原创工具] PythonEnvManager - Python 环境管理工具 [更新自定义扫描路径]
开发语言·python
Lueeee.2 小时前
RTSP协议
linux
AI科技星2 小时前
光速的几何本质与运动极限:基于张祥前统一场论对光子及有质量粒子运动的统一诠释
数据结构·人工智能·经验分享·算法·计算机视觉
忍冬行者2 小时前
通过ansible分发免密公钥、清理不再使用的公钥及验证公钥状态
linux·服务器·ansible
没有bug.的程序员2 小时前
负载均衡的真正含义:从算法到架构的深度解析
java·jvm·算法·微服务·架构·负载均衡