当前位置: 首页 > news >正文

网络编程概述与UDP编程

一、 网络编程概述

1.1 概述

在现代软件开发与系统交互场景里,基于 Socket 的网络多进程通信占据核心地位,其适用场景广泛且深入到各类数字化交互中:

  • 直播场景:主播端通过 Socket 建立的网络连接,将音视频流以数据包形式,依托 TCP 或 UDP 协议(依据场景选择,如低延迟需求可能用 UDP 结合纠错策略 ),实时传输到平台服务器,再由服务器通过多进程通信分发给百万级观众端,实现直播内容的跨地域传播。
  • 文件发送与资源下载:发送端把文件拆分为网络数据包,借由 TCP 可靠传输(保障文件完整性 ),按序发送给接收端;资源下载同理,客户端与服务器通过 Socket 握手建立连接,以流式传输逐步获取资源,像浏览器下载软件安装包,背后就是基于 Socket 网络通信的文件传输逻辑。
  • 即时聊天:聊天消息(文字、表情、语音片段 )作为字符串或二进制数据,通过网络载体发送,例如 QQ、微信等应用,利用自定义协议封装消息,基于 TCP 保证消息不丢包、不乱序,让双方主机能解析还原对话内容,实现实时交互。

        对于 IOT(物联网) ,智能家居是典型场景:智能空调基于 TCP/UDP 协议,与家庭网关建立连接,采集室内温度(数据上发 ),同时接收手机 APP 发送的 MQTT 协议指令(如下发 “设置 26℃” ),完成制冷温度调节(下发控制 ),依托现有网络实现设备互联与智能控制。本质上,就是把物理设备数据与控制指令,通过网络协议转化为可传输、解析的信息,打通 “设备 - 网络 - 控制端” 链路 。

1.2 计算机网络发展

起源背景与 ARPA 成立

1957 年苏联发射 Sputnik(斯普特尼克)人造卫星,这一事件触动美国对军事、科技主导权的担忧 —— 若苏联凭借卫星实现通信、情报优势,美国将陷入被动。因此,1958 年 1 月 7 日,美国紧急拨款成立 ARPA(Advanced Research Projects Agency,美国高级研究计划署 ) ,核心目标是突破苏联可能的技术封锁,研发先进网络、通信技术,为军事及科研提供支撑,后续 ARPA 推动的网络研究,成为现代计算机网络的重要起源 。

计算机网络核心需求(区别传统通信 )
  • 非语音优先:传统电话网聚焦语音通话,而计算机网络设计初衷是 数据传输与多设备协同 ,比如科研计算数据共享、军事信息交互,不局限于 “通话” 单一功能,更强调多类型数据(文件、指令、传感器数据等 )的传递 。
  • 简单可靠:早期网络用于科研、军事,需在复杂环境(如跨地域、恶劣条件 )下稳定运行,所以追求 结构简洁、传输容错性强 。例如,即便部分节点故障,仍能通过冗余路由传递数据,保障整体网络不瘫痪 。
  • 异构设备兼容:要连接不同厂商、架构的计算机(如早期大型机、小型机 ),让 IBM 主机与 DEC 终端能互传数据,就得突破硬件、系统差异,制定通用通信规则,这也是网络协议诞生的驱动力之一 。
  • 节点平等与路由冗余:网络里没有 “中心主机优先”,所有节点(如实验室的计算终端、军事监测设备 )地位等同 ,保障数据交互的去中心化;同时,“冗余路由” 是为了避免单点故障,像军事网络,若一条通信链路被破坏,能自动切换到备用链路,确保信息持续传输 。
  • 唯一标识(IP 地址 ):网络里每台设备必须有 独一无二的 “身份” ,才能精准收发数据,这就是 IP 地址的核心作用。
IP 地址体系(IPv4 & IPv6 )
  • IPv4:采用 32 位二进制 表示地址(如 192.168.1.1 ,转换为二进制是 32 个 0/1 组合 ),分成 4 个 8 位段(即 “点分十进制” ),每个段取值 0 - 255 。但由于互联网早期分配策略与地址消耗速度,IPv4 资源极度稀缺 —— 中国仅拿到 156.x.x.x 、188.x.x.x 等少量 “高级资源段”(实际是 A 类、B 类地址中的部分分配 ),导致局域网常用 NAT(网络地址转换 ) 技术,让多设备共享一个公网 IPv4,通过 “区域唯一 IP + 多级路由”(如家庭路由器分配 192.168.xxx 内网地址,再映射到公网 IP )缓解地址不足问题 。
  • IPv6:为解决 IPv4 枯竭,推出 128 位二进制 地址(格式如 2001:0db8:85a3:0000:0000:8a2e:0370:7334 ),地址空间从 IPv4 的约 43 亿个,暴增至近乎 “无限”(理论上约 \(2^{128}\) 个 ),能为全球设备(包括未来海量物联网终端 )分配独立公网地址,彻底解决地址短缺,推动万物互联更高效落地 。

1.3 TCP/IP & UDP 协议

协议本质与应用逻辑

基于 Socket(套接字 ) ,TCP/IP 和 UDP 是网络数据传输的 “规则框架”,让不同设备(如手机与服务器、智能手表与云端 )能跨网络通信。两者都以 “报文头 + 数据体” 形式封装信息:

  • “报文头” 类似快递面单:包含 “消息类型”(如 TCP 的连接标识、UDP 的端口号 )、源 IP、目的 IP 等,告诉网络 “数据从哪来、到哪去、按什么规则传” ;
  • “数据体” 是实际内容:可以是聊天消息、文件片段、传感器数值等,依托 “面单(报文头 )” 的指引,在网络中流转 。
TCP/IP vs UDP 核心差异(以传输文件、直播为例 )
  • TCP(传输控制协议 )
    • 可靠传输:通过 “三次握手” 建立连接(确保双方收发能力正常 )、“确认应答”(发出去的数据,对方必须回传确认,丢包就重发 )、“拥塞控制”(网络拥堵时自动降低发送速度 ),像 传输重要文件(如合同、安装包 ) ,必须保证数据完整,就依赖 TCP 。缺点是 传输延迟高 ,不适合对实时性要求极致的场景 。
  • UDP(用户数据报协议 )
    • 无连接、低延迟:无需提前握手,直接发数据,省去连接耗时,像 直播推流、游戏实时交互 (如 FPS 游戏里的子弹射击指令 ),追求 “瞬间响应” ,哪怕偶尔丢包(画面卡顿一下、游戏子弹偶尔没同步 ),也优先保证实时性。但缺点是 不可靠 ,数据发出去不保证对方收到,需应用层自己处理丢包、乱序问题 。
网络分层模型(OSI 七层 & TCP/IP 四层 )
  • OSI 七层结构(理论参考 ):从下到上为 物理层(网线、光纤传输电信号 / 光信号 )→ 数据链路层(以太网协议,处理网卡收发包 )→ 网络层(IP 协议,负责寻址、路由 )→ 传输层(TCP/UDP,负责端到端数据可靠 / 快速传输 )→ 会话层(管理应用程序间的连接会话 )→ 表示层(数据加密、压缩、格式转换 )→ 应用层(HTTP、FTP 等具体应用协议 ) 。它是理想的 “标准化分层”,便于理解网络各环节职责,但实际应用中较难严格分层实现 。
  • TCP/IP 四层结构(实际主流 ):简化为 网络接口层(对应 OSI 物理层 + 数据链路层,处理硬件、链路通信 )→ 网络层(IP 协议,管理地址、路由 )→ 传输层(TCP/UDP,负责端到端传输 )→ 应用层(HTTP、MQTT 等应用协议,直接对应用户功能 ) 。更贴合实际开发与协议设计,比如我们写代码调用 HTTP(应用层 ),底层自动通过 TCP(传输层 )、IP(网络层 )等完成通信,无需关注七层细节 。

组包和解包:

1.4 小端字节序和大选字节序

二、UDP编程

1. UDP 协议概述

  • 无连接特性:UDP 协议面向【无连接】,通信前无需像 TCP 那样建立连接,直接发送数据报,降低通信初始开销 。
  • 角色平等性:不区分客户端和服务器,仅有发送端和接收端概念,双方都可主动发数据,灵活适用于广播、组播场景 。
  • 传输可靠性:因无连接,数据传输无确认、重传机制,存在丢包、乱序风险,数据安全性依赖应用层保障 。
  • 传输效率:无需连接管理、确认等额外流程,数据传递速度较快 。
  • 典型应用场景:适用于广播、组播,以及对数据完整性要求不绝对严格的场景,如直播(容忍偶尔丢包不影响观看流畅性 )、网络游戏(优先保证实时交互,轻微数据丢失可通过游戏逻辑弥补 )。

2. 本地数据转网络相关函数

2.1 htons:本地 16 位数据转网络数据【小转大】

  • 函数原型
    #include <arpa/inet.h>
    uint16_t htons(uint16_t hostshort);
    
  • 功能:将无符号 short 类型本地小端字节序数据(如主机存储的端口号 ),转换为网络大端字节序,适配网络传输要求(网络协议通常以大端序解析数据 ),常用于【端口号小转大】 。
  • 参数uint16_t hostshort ,uint16_t 是无符号 short 类型(16 位二进制位 ),参数内容为本地主机 16 位二进制数据(如端口号实际值 )。
  • 返回值:转换后符合网络大端字节序的 16 位无符号 short 类型数据 。

2.2 htonl:本地 32 位数据转网络数据【小转大】

  • 函数原型
    #include <arpa/inet.h>
    uint32_t htonl(uint32_t hostlong);
    
  • 功能:把无符号 int 类型本地小端字节序数据(如 32 位 IP 地址 ),转为网络大端字节序,常用于【32 位 int 类型 IP 地址小转大】 。
  • 参数uint32_t hostlong ,uint32_t 是无符号 int 类型(32 位二进制位 ),参数为本地主机 32 位二进制数据(如 IP 地址实际值 )。
  • 返回值:转换后符合网络大端字节序的 32 位无符号 int 类型数据 。

自定义实现示例(补充代码逻辑 )

以下是手动实现 my_htonsmy_htonl 及测试函数的代码,展示字节序转换逻辑:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>uint16_t my_htons(uint16_t host_value) {uint16_t net_value = 0;// 小端转大端:交换高低 8 位net_value |= (host_value << 8) | (host_value >> 8);return net_value;
}uint32_t my_htonl(uint32_t host_value) {uint32_t net_value = 0;// 小端转大端:重新排列 4 个字节顺序net_value |= (host_value & 0xFF) << 24;net_value |= ((host_value >> 8) & 0xFF) << 16;net_value |= ((host_value >> 16) & 0xFF) << 8;net_value |= (host_value >> 24);return net_value;
}void test_htons(void) {uint16_t host_value = 7385;uint16_t net_value = htons(host_value);printf("htons net_value : %d\n", net_value);net_value = my_htons(host_value);printf("my_htons net_value : %d\n", net_value);
}void test_htonl(void) {uint32_t host_value = 0xC0A80D48; // 示例 IP 地址转换前值uint32_t net_value = htonl(host_value);printf("htonl net_value : %d\n", net_value);net_value = my_htonl(host_value);printf("my_htonl net_value : %d\n", net_value);
}int main(int argc, char const *argv[]) {test_htons();test_htonl();return 0;
}
  • 逻辑说明
    • my_htons 通过位移操作,交换 16 位数据的高低 8 位,实现小端到网络大端(大端通常等价于 “高字节在前” )转换 。
    • my_htonl 拆分 32 位数据的 4 个字节,重新按大端序(高字节对应高位 )拼接,完成小端到网络大端转换 。

2.3 ntohs:网络 16 位数据转本地数据【大转小】

  • 函数原型
    #include <arpa/inet.h>
    uint16_t ntohs(uint16_t netshort);
    
  • 功能:将无符号 short 类型网络大端字节序数据(如网络接收的端口号 ),转换为本地小端字节序,适配主机存储解析,常用于【端口号大转小】 。
  • 参数uint16_t netshort ,表示网络大端序的 16 位无符号 short 类型数据(如网络传来的端口号 )。
  • 返回值:转换后符合本地小端字节序的 16 位无符号 short 类型数据 。

2.4 ntohl:网络 32 位数据转本地数据【大转小】

  • 函数原型
    #include <arpa/inet.h>
    uint32_t ntohl(uint32_t netlong);
    
  • 功能:把无符号 int 类型网络大端字节序数据(如网络接收的 32 位 IP 地址 ),转为本地小端字节序,常用于【32 位 int 类型 IP 地址大转小】 。
  • 参数uint32_t netlong ,表示网络大端序的 32 位无符号 int 类型数据(如网络传来的 IP 地址 )。
  • 返回值:转换后符合本地小端字节序的 32 位无符号 int 类型数据 。

3. IPv4 地址相关操作

3.1 IPv4 地址描述

IPv4 地址有两种表示形式:

  • 点分十进制字符串:如 192.168.13.72 ,直观易读,要求字符串长度最小 16 字节(保障数据完整性 ),用于人机交互展示 。
  • 4 字节 int 类型数值:网络传输、程序内部操作时常用,以紧凑二进制形式存储,减少数据量,提升传输、处理效率 。

3.2 点分十进制 IP 转网络 4 字节 int 类型 IP 地址

  • 函数 inet_pton
    • 原型
      #include <arpa/inet.h>
      int inet_pton(int af, const char *src, void *dst);
      
    • 功能:将【点分十进制】IPv4/IPv6 字符串(如 192.168.13.20 ),转换为对应协议的 32 位(IPv4 )或 128 位(IPv6 )整数 IP 描述形式,存入指定内存 。
    • 参数
      • int af:协议族,AF_INET 对应 IPv4,AF_INET6 对应 IPv6 。
      • const char *src:点分十进制格式的 IP 地址字符串(如 192.168.13.20 )。
      • void *dst:存储转换后 IP 地址二进制数据的内存地址(如 uint32_t 变量地址,用于存 IPv4 转换结果 )。
    • 返回值:转换成功返回 1;地址族不支持返回 0;输入无效(非合法 IP 格式 )返回 -1,并设置 errno 为 EINVAL 。

代码示例(inet_pton 应用 )

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>int main(int argc, char const *argv[]) {// 点分十进制 IPv4 地址char *str = "192.168.13.72"; // 存储转换后的 4 字节 int 类型 IP 数据uint32_t ip_value = 0; // 调用 inet_pton 转换,AF_INET 表示 IPv4 协议int ret = inet_pton(AF_INET, str, &ip_value); if (ret != 1) {perror("inet_pton error!");exit(EXIT_FAILURE);}// 转换后为网络大端序 4 字节 IP 数据,符合网络传输要求printf("ip_value : %d\n", ip_value); return 0;
}
  • 说明192.168.13.72 转换后,ip_value 存储大端序二进制数据(如示例中对应 1208854720 ),可用于网络通信的 IP 地址设置 。

3.3 网络 4 字节 int 类型 IP 地址转点分十进制 IP 地址

  • 函数 inet_ntop
    • 原型
      #include <arpa/inet.h>
      const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
      
    • 功能:将 32 位(IPv4 )或 128 位(IPv6 )整数形式的 IP 地址,按协议要求转换为【点分十进制】(IPv4 )或对应格式(IPv6 )的字符串,支持 IPv4、IPv6 。
    • 参数
      • int af:协议族(AF_INET 对应 IPv4,AF_INET6 对应 IPv6 )。
      • const void *src:存储 IP 地址二进制数据的内存地址(如 uint32_t 变量地址,存 IPv4 大端序数据 )。
      • char *dst:存储转换后点分十进制字符串的缓冲区,IPv4 要求最小 16 字节空间 。
      • socklen_t sizedst 缓冲区的字节长度 。
    • 返回值:转换成功返回指向 dst 的指针(即点分十进制字符串首地址 );失败返回 NULL 。

代码示例(inet_ntop 应用 )

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>int main(int argc, char const *argv[]) {// 网络大端序 4 字节 IP 数据(对应 192.168.13.72 转换后的值 )uint32_t net_ip = 1208854720; // 存储点分十进制 IP 的缓冲区char ip_addr[16] = ""; // 调用 inet_ntop 转换,AF_INET 表示 IPv4 协议const char *ip = inet_ntop(AF_INET, &net_ip, ip_addr, 16); if (NULL == ip) {perror("inet_ntop error!");exit(EXIT_FAILURE);}printf("ip_addr : %s\n", ip_addr);printf("ip : %s\n", ip); return 0;
}
  • 说明net_ip 是网络大端序的 4 字节 IP 数据,经 inet_ntop 转换后,ip_addr 存储点分十进制字符串(如 192.168.13.72 ),实现 “网络二进制 IP” 到 “可读字符串” 的转换 。

4. UDP 编程

4.1 UDP 编程流程

  • UDP 发送端socket() → sendto() → close()

    • socket():创建网络通信套接字,建立通信 “通道” 。
    • sendto():指定目标地址、端口,发送 UDP 数据报 。
    • close():关闭套接字,释放资源 。
  • UDP 接收端socket() → bind() → recvfrom() → close()

    • socket():创建套接字 。
    • bind():绑定本地 IP、端口,让套接字监听指定地址的数据 。
    • recvfrom():阻塞等待接收 UDP 数据报,同时获取发送端地址信息 。
    • close():关闭套接字 。

4.2 UDP 编程相关函数

4.2.1 socket:申请创建 Socket 套接字
  • 函数原型
    #include <sys/types.h>
    #include <sys/socket.h>
    int socket(int domain, int type, int protocol);
    
  • 功能:创建网络通信套接字,返回对应文件描述符,作为后续网络操作(收发数据等 )的标识 。
  • 参数
    • int domain:协议族,如 AF_INET(IPv4 协议 )、AF_INET6(IPv6 协议 )。
    • int type:套接字类型,SOCK_DGRAM 表示 UDP 套接字(无连接、基于数据报 );SOCK_STREAM 表示 TCP 套接字(面向连接、基于流 );SOCK_RAW 表示原始套接字(可直接操作网络层协议 )。
    • int protocol:协议类型,IPPROTO_UDP 表示 UDP 协议;IPPROTO_TCP 表示 TCP 协议 。
  • 返回值:成功返回套接字文件描述符(非负整数 );失败返回 -1 。
代码示例(socket 创建 UDP 套接字 )
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>int main(int argc, char const *argv[]) {// 存储套接字文件描述符int socket_fd = -1; // 创建 UDP 套接字:AF_INET(IPv4 ) + SOCK_DGRAM(UDP 类型 ) + IPPROTO_UDP(UDP 协议 )socket_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (-1 == socket_fd) {perror("socket error!");exit(EXIT_FAILURE);}printf("socket_fd : %d\n", socket_fd); getchar(); close(socket_fd); return 0;
}
补充说明:struct sockaddr 和 struct sockaddr_in 结构体
  • struct sockaddr:通用套接字地址结构体,系统定义的标准格式,用于兼容不同协议地址:

    struct sockaddr {sa_family_t sa_family; // 地址族(如 AF_INET )char        sa_data[14]; // 协议地址(14 字节,存具体地址、端口等 )
    };
    
  • struct sockaddr_in:针对 IPv4 协议的地址结构体,更直观描述 IPv4 通信地址:

    struct sockaddr_in {sa_family_t    sin_family; // 地址族(固定为 AF_INET )in_port_t      sin_port;   // 16 位端口号(网络字节序 )struct in_addr sin_addr;   // 32 位 IPv4 地址(网络字节序 )char           sin_zero[8]; // 填充字段,使结构体长度与 sockaddr 一致
    };
struct in_addr {uint32_t s_addr; // 32位无符号整数,存储IPv4地址(网络字节序)
};
  • 该结构体专门用于存储 IPv4 地址的 32 位二进制表示,s_addr字段的值必须是网络字节序(大端序),通常通过inet_pton等函数转换得到。

结构体使用说明

  • sockaddrsockaddr_in的关系
    两者内存大小相同(均为 16 字节),sockaddr_inAF_INET协议族的专用结构体,字段更清晰;而sockaddr是通用结构体,作为系统调用(如bindsendto)的参数类型。使用时需将sockaddr_in*强制转换为sockaddr*
  • 初始化注意事项
    sin_zero字段需用bzeromemset清零,保证与sockaddr结构体大小一致;sin_family必须设为AF_INETsin_portsin_addr.s_addr必须转换为网络字节序。

4.2.2 bind:绑定套接字到本地地址和端口

  • 函数原型
    #include <sys/types.h>
    #include <sys/socket.h>
    int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    
  • 功能:将创建的套接字与本地 IP 地址和端口绑定,确保数据能通过该地址和端口收发(服务器必须绑定,客户端可选)。
  • 参数
    • sockfdsocket函数返回的套接字文件描述符。
    • addr:指向sockaddr(或sockaddr_in)结构体的指针,包含本地 IP 和端口信息(需转换为网络字节序)。
    • addrlenaddr结构体的字节长度(如sizeof(struct sockaddr_in))。
  • 返回值:成功返回0;失败返回-1

4.2.3 sendto:发送 UDP 数据报

  • 函数原型
    #include <sys/types.h>
    #include <sys/socket.h>
    ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);
    
  • 功能:向指定目标地址发送 UDP 数据报(无需提前建立连接)。
  • 参数
    • sockfd:套接字文件描述符。
    • buf:待发送数据的缓冲区地址。
    • len:待发送数据的字节长度。
    • flags:发送标志(通常为0,表示默认行为)。
    • dest_addr:指向目标地址结构体(sockaddr_in)的指针,包含对方 IP 和端口。
    • addrlen:目标地址结构体的字节长度。
  • 返回值:成功返回发送的字节数;失败返回-1

4.2.4 recvfrom:接收 UDP 数据报

  • 函数原型
    #include <sys/types.h>
    #include <sys/socket.h>
    ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);
    
  • 功能:接收 UDP 数据报,并获取发送方的地址信息。
  • 参数
    • sockfd:套接字文件描述符。
    • buf:存储接收数据的缓冲区地址。
    • len:缓冲区的最大容量(字节)。
    • flags:接收标志(通常为0)。
    • src_addr:输出参数,用于存储发送方的地址信息(需提前分配内存)。
    • addrlen:输入输出参数,输入时为src_addr的容量,输出时为实际地址长度。
  • 返回值:成功返回接收的字节数;失败返回-1

4.2.5 close:关闭套接字

  • 函数原型
    #include <unistd.h>
    int close(int fd);
    
  • 功能:关闭套接字,释放相关资源。
  • 参数fd为套接字文件描述符。
  • 返回值:成功返回0;失败返回-1

三、UDP 客户端与服务器完整通信示例

服务器代码(udp_server.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <unistd.h>#define PORT 8888         // 服务器端口号
#define BUFFER_SIZE 1024  // 缓冲区大小int main() {int sockfd;struct sockaddr_in server_addr, client_addr;socklen_t client_addr_len = sizeof(client_addr);char buffer[BUFFER_SIZE];// 1. 创建UDP套接字sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);if (sockfd == -1) {perror("socket creation failed");exit(EXIT_FAILURE);}// 2. 初始化服务器地址结构体memset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET;                  // IPv4server_addr.sin_addr.s_addr = htonl(INADDR_ANY);   // 绑定所有本地IPserver_addr.sin_port = htons(PORT);                // 端口转换为网络字节序// 3. 绑定套接字到本地地址和端口if (bind(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {perror("bind failed");close(sockfd);exit(EXIT_FAILURE);}printf("UDP server started on port %d...\n", PORT);while (1) {// 4. 接收客户端数据ssize_t recv_len = recvfrom(sockfd, buffer, BUFFER_SIZE - 1, 0,(struct sockaddr*)&client_addr, &client_addr_len);if (recv_len == -1) {perror("recvfrom failed");continue;}buffer[recv_len] = '\0';  // 手动添加字符串结束符// 打印客户端信息和接收的数据char client_ip[INET_ADDRSTRLEN];inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, INET_ADDRSTRLEN);printf("Received from %s:%d: %s\n", client_ip, ntohs(client_addr.sin_port), buffer);// 若收到"exit",则退出服务器if (strcmp(buffer, "exit") == 0) {printf("Server exiting...\n");break;}// 5. 回复客户端(echo功能)const char* reply = "Message received";if (sendto(sockfd, reply, strlen(reply), 0,(struct sockaddr*)&client_addr, client_addr_len) == -1) {perror("sendto failed");}}// 6. 关闭套接字close(sockfd);return 0;
}

客户端代码(udp_client.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <unistd.h>#define SERVER_IP "127.0.0.1"  // 服务器IP(本地回环地址)
#define SERVER_PORT 8888       // 服务器端口号
#define BUFFER_SIZE 1024       // 缓冲区大小int main() {int sockfd;struct sockaddr_in server_addr;socklen_t server_addr_len = sizeof(server_addr);char buffer[BUFFER_SIZE];// 1. 创建UDP套接字sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);if (sockfd == -1) {perror("socket creation failed");exit(EXIT_FAILURE);}// 2. 初始化服务器地址结构体memset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_port = htons(SERVER_PORT);// 转换服务器IP为网络字节序if (inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr) != 1) {perror("invalid server IP");close(sockfd);exit(EXIT_FAILURE);}printf("Enter message to send (type 'exit' to quit):\n");while (1) {// 3. 从标准输入获取数据printf("> ");fgets(buffer, BUFFER_SIZE, stdin);// 移除fgets添加的换行符buffer[strcspn(buffer, "\n")] = '\0';// 4. 发送数据到服务器if (sendto(sockfd, buffer, strlen(buffer), 0,(struct sockaddr*)&server_addr, server_addr_len) == -1) {perror("sendto failed");continue;}// 若发送"exit",则退出客户端if (strcmp(buffer, "exit") == 0) {printf("Client exiting...\n");break;}// 5. 接收服务器回复ssize_t recv_len = recvfrom(sockfd, buffer, BUFFER_SIZE - 1, 0,NULL, NULL);  // 不关心服务器地址,设为NULLif (recv_len == -1) {perror("recvfrom failed");continue;}buffer[recv_len] = '\0';printf("Server reply: %s\n", buffer);}// 6. 关闭套接字close(sockfd);return 0;
}

 

https://github.com/0voice

http://www.lryc.cn/news/602758.html

相关文章:

  • 关于前端的性能优化
  • 【数据架构09】人工智能及数据智能架构篇
  • pg数据库,本地服务器下不同端口迁移
  • 用了Flutter包体积增大就弃用Flutter吗?包体积与开发效率,这两者之间如何权衡?
  • 微信小程序点击输入框时,顶部导航栏被遮挡问题如何解决?
  • 鸿蒙打包签名
  • Linux驱动23 --- RkMedia 使用
  • gdb 基本命令
  • 3DGRUT: 革命性的3D高斯粒子光线追踪与混合光栅化技术深度解析
  • Error: Unable to find a match: python3.8
  • 【Linux操作系统】简学深悟启示录:Linux环境基础开发工具使用
  • Spring IOC与DI
  • 【服务器知识】nginx配置ipv6支持
  • JVM 内存共享区域详解
  • RabbitMQ概念与管理端配置说明
  • 学习游戏制作记录(改进剑投掷状态)7.28
  • 四、计算机组成原理——第7章:输入/输出系统
  • Unity_UI_NGUI_组合控件2
  • 数论1.01
  • socketpair函数详解
  • MCU+RTOS调试
  • STM32-基本定时器
  • JavaScript手录-排序算法篇
  • 二分查找的「左右为难」:如何优雅地找到数组中元素的首尾位置
  • 城阳区奥赛暑假公益班第三次入门组初赛模拟赛
  • 把振动数据转成音频并播放
  • 提取apk中的各种语言翻译成表格,python脚本
  • Lakehouse: Unifying DW Advanced Analytics in Open Platforms
  • 《Java 程序设计》第 8 章 - Java 常用核心类详解
  • 未授权访问漏洞 总结