应用——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 次消息后退出

相关推荐
阿部多瑞 ABU5 分钟前
`chenmo` —— 可编程元叙事引擎 V2.3+
linux·人工智能·python·ai写作
阿蒙Amon10 分钟前
C#每日面试题-Array和ArrayList的区别
java·开发语言·c#
666HZ66621 分钟前
数据结构2.0 线性表
c语言·数据结构·算法
SmartRadio27 分钟前
ESP32添加修改蓝牙名称和获取蓝牙连接状态的AT命令-完整UART BLE服务功能后的完整`main.c`代码
c语言·开发语言·c++·esp32·ble
徐同保34 分钟前
nginx转发,指向一个可以正常访问的网站
linux·服务器·nginx
HIT_Weston37 分钟前
95、【Ubuntu】【Hugo】搭建私人博客:_default&partials
linux·运维·ubuntu
且去填词41 分钟前
Go 语言的“反叛”——为什么少即是多?
开发语言·后端·面试·go
知乎的哥廷根数学学派1 小时前
基于生成对抗U-Net混合架构的隧道衬砌缺陷地质雷达数据智能反演与成像方法(以模拟信号为例,Pytorch)
开发语言·人工智能·pytorch·python·深度学习·机器学习
实心儿儿1 小时前
Linux —— 基础开发工具5
linux·运维·算法
oMcLin1 小时前
如何在SUSE Linux Enterprise Server 15 SP4上通过配置并优化ZFS存储池,提升文件存储与数据备份的效率?
java·linux·运维