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

网络.1 UDP

1.网络概述

网络使设备之间在不同区域完成数据交换。

网络传输过程中必须遵守协议。

协议:网络数据传递要求的协议内容。

eg:Socket协议、IP协议、TCP/UDP协议

起源:美国1958年成立APPA(the Advanced Research Projects Agency 美国高级研究计划署)。

1.1 网络结构:

1.2 TCP/IP模型

1.3 IP地址

IP地址是当前设备在网络中的唯一地址。

1.3.1 IPv4

IPv4下32bit位组成,常用方式有两种:

  • 4字节IP地址存储方式:

  • 点分十进制IP进制字符串形式:

192.168.16.126

1.4 端口号

  • 数据在发送到目标设备时,需要明确到底教给哪一个进程。
  • 计算机为主机中每一个联网的设备分配了唯一的端口号。
  • 端口号类型为short,范围0~65535。推荐使用1000以上的端口号,避免与系统特定的端口号冲突。

完成数据在网络端的传输,必须提供目标主机的IP地址和端口号。

1.5 其他

  • MAC地址:物理地址,设备出场自带,无法修改。可利用MAC地址过滤,实现白名单和黑名单功能。
  • 子网掩码NetMask:判断两个IP是否在同一个网段。
  • 回环地址/本地地址:利用网络方式完成本机不同进程之间的数据交换。可利用本机地址+端口号完成。IPv4下本机地址是127.0.0.1,IPv6是::1。

 2. 网络字节序/大端字节序

小端字节序:计算机存储数据的方式

大端字节序:网络传递过程中存储数据的方式

本地到网络【小转大】,网络到本地【大转小】

eg:

3.网络处理数据API

  • hton ==> Host to NetWork  本地转网络
  • ntoh ==> NetWork to Host  网络转本地

htonl 和 ntohl 处理 4 字节 IP 地址数据本地和网络数据转换

htons 和 ntohs 处理 2 字节 port 端口号数据本地和网络数据转换

3.1 htonl()

 #include <arpa/inet.h>

uint32_t htonl(uint32_t hostlong);

(uint32_t :无符号整数类型,只能表示非负整数,明确规定位数为 32 个二进制位,取值范围0 到 2³² - 1(即 0 到 4294967295)

函数功能:

  •  将本地 uint32_t 无符号 int 类型转换,从小端字节序转换到网络传递要求所需的大端字节序数据形式。

参数:

  • uint32_t hostlong : 用户提供的本地无符号 int 类型数据。

返回值:

  •  转换为大端字节序网络所需数据内容。

3.2 htons()

 #include <arpa/inet.h>

uint16_t htons(uint16_t hostshort);

函数功能:

  • 将本地 uint16_t 无符号 short 类型转换,从小端字节序转换到网络传递要求所需的【大端字节序】数据形式。

参数:

  • uint16_t hostshort :用户提供的本地无符号 short 类型数据。

返回值:

  • 转换为大端字节序网络所需数据内容。

3.3 ntohl()

 #include <arpa/inet.h>

uint32_t ntohl(uint32_t netlong);

函数功能:

  • 将网络大端字节序 无符号 int 类型数据转换为本地小端字节序数据

参数:

  • uint32_t netlong:  用户提供的网络传递无符号 int 类型数据。

返回值:

  • 转换为小端字节序本地所需数据内容。

3.4 

 #include <arpa/inet.h>

uint16_t ntohs(uint16_t netshort);

函数功能:

  • 将网络大端字节序无符号 short 类型数据转换为本地小端字节序数据

参数:

  • uint16_t netshort:用户提供的网络传递无符号 short 类型数据。

返回值:

  • 转换为小端字节序本地所需数据内容。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>#include <arpa/inet.h>u_int32_t my_htonl(uint32_t hostlong);
u_int16_t my_htons(uint16_t hostshort);u_int32_t my_ntohl(uint32_t netlong);
u_int16_t my_ntohs(uint16_t netshort);
int main(int argc, char const *argv[])
{//IPv4 ==> 192.168.16.125 对应大端字节序 4 字节 IP 地址数据 ==> 2098243776//端口号 8848 对应大端字节序 2 字节数据 ==> 36898 uint32_t local_ip_addr =3232239741;uint16_t local_post =8848;uint32_t net_ip_addr =my_htonl(local_ip_addr);printf("my_htonl:%d\n",net_ip_addr);net_ip_addr=htonl(local_ip_addr);printf("htonl:%d\n",net_ip_addr);uint16_t net_post =my_htons(local_post);printf("my_htons:%d\n",net_post);net_post=htons(local_post);printf("htons:%d\n",net_post);uint32_t host_ip_addr = my_ntohl(net_ip_addr);printf("host_ip_addr : %u\n", host_ip_addr);uint16_t host_port = my_ntohs(36898);printf("host_port : %d\n", net_post);return 0;
}u_int32_t my_htonl(uint32_t hostlong)
{uint8_t buff[4]={0};for (size_t i = 0; i < 4; i++){//将 hostlong 的地址转换为 uint8_t* 类型,即把它当作一个字节数组来访问memcpy(&buff[i], ((uint8_t *)&hostlong) + 3 - i, 1);}return *((uint32_t* )buff);}
u_int16_t my_htons(uint16_t hostshort)
{uint8_t buff[2]={0};for (size_t i = 0; i < 2; i++){//将 hostshort 的地址转换为 uint8_t* 类型,即把它当作一个字节数组来访问memcpy(&buff[i], ((uint8_t *)&hostshort) + 1 - i, 1);}return *((uint32_t* )buff);
}
u_int32_t my_ntohl(uint32_t netlong)
{return my_htonl(netlong);
}
u_int16_t my_ntohs(uint16_t netshort)
{return my_htons(netshort);
}

4.IP地址转换API

IPv4 地址有两种模式方式:

  • 4 字节无符号 int 类型方式描述
  • 点分十进制字符串 IPv4 地址描述,一般会采用 char 类型数组形式存储,数组容量为 16

网络传递中,IPv4 地址数据采用 4 字节无符号 int 数据形式,

本地采用点分十进制字符串 IPv4 地址

4.1 inet_pton()

#include <arpa/inet.h>

 int inet_pton(int af, const char *src, void *dst);

函数功能:

  •  本地IP转网络地址。将本地点分十进制 IP 地址字符串,根据 IP 协议转换为目标网络传递所需 IP 地址方式。通常情况下都是 IPv4 协议。

参数:

  • int af :IP PROTOCOL IP 地址协议,可以选择 IPv4 或者 IPv6
  • const char *src :  字符串形式的 IP 地址数据,例如 点分十进制 IPv4 协议地址
  • void *dst : 用于存储网络传递所需的大端字节序目标变量地址

返回值:

  • 标准转换正常: 1
  • 提供的字符串数据不满足 IP 地址协议要求: 0 
  • 提供 AF 协议族不合法: -1 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>#include <arpa/inet.h>int main(int argc, char const *argv[])
{char ip_addr_str[16] = "192.168.16.125";int net_ip_addr = 0;int ret = inet_pton(AF_INET, ip_addr_str, &net_ip_addr);if (1 == ret){printf("net_ip_addr : %u\n", net_ip_addr);}else{printf("What are you弄啥嘞!\n");}return 0;
}

4.2 inet_ntop()

#include <arpa/inet.h>

const char *inet_ntop(int af, const void *src,char *dst, socklen_t size);

函数功能:

  • 网络地址转本地IP。将网络传递使用的 IP 地址数据以及数据字节长度,根据当前协议要求,转换为本地字符串形式的 IP 地址数据,支持 IPv4 和 IPv6。

参数:

  • int af :  IP PROTOCOL IP 地址协议,可以选择 IPv4 或者 IPv6
  • const void *src :   网络传递字节序/大端字节序 IP 地址数据地址
  • char *dst :  用于存储字符串形式 IP 地址数据的 char 类型缓冲区空间首地址
  • socklen_t size :  对应 char 类型缓冲区字节个数

返回值:

  •  函数转换成功,返回 dst 对应字符数组空间首地址
  • 转换失败,返回 NULL,同时设置 errno
#include <stdio.h>
#include <stdlib.h>
#include <string.h>#include <arpa/inet.h>#define IP_ADDR_STR_LEN (16)int main(int argc, char const *argv[])
{int net_ip_addr = 2098243776;char ip_addr_str[IP_ADDR_STR_LEN] = "";socklen_t socklen = IP_ADDR_STR_LEN;const char *ret = inet_ntop(AF_INET, &net_ip_addr, ip_addr_str, socklen);printf("ip_addr_str : %s\n", ip_addr_str);return 0;
}

5. UDP网络传输

5.1 UDP特征

  • 面向无连接,非完成可靠数据传递方式
  • 数据发送速度快
  • 没有客户端和服务器,只有发送端和接收端
  • 利用数据包形式完成数据传递

5.2 UDP流程概述

通过 socket 函数创建/申请 socket 套接字,socket 支持不同版本,不同协议。

  • 接收端

需要通过 bind 函数,明确当前 UDP 接收端进程绑定的端口号是哪一个。

利用 recvfrom  接收目标数据。

  • 发送端

利用 sendto 将【打包】的 UDP 数据包发送给目标接收端

5.3 UDP相关API

5.3.1 socket()

socket 是网络编程中用于进行进程间通信的基础,所有的数据都是通过 Socket 文件描述符对应的网络通道进行数据发送和接收。

#include <sys/types.h>         

#include <sys/socket.h>

int socket(int domain, int type, int protocol);

函数功能:

  • 根据协议限制,Socket类型要求和支持的协议要求,创建对应的 Socket 套接字,提供给进程使用的是文件描述符 FD

参数:

  • int domain :  当前 IP 地址协议要求,提供给当前参数是 AF_INET
  • int type :  指定 Socket 类型,可以选择 SOCK_STREAM TCP Socket 类型 ,SOCK_DGRAM UDP Socket 类型, SOCK_RAW 原始 Socket 类型

SOCK_STREAM :流式套接字,对应 TCP 协议(可靠、面向连接)

SOCK_DGRAM :数据报套接字,对应 UDP 协议(不可靠、无连接)

SOCK_RAW:原始套接字,用于直接访问底层协议(如 ICMP)

  • int protocol : 协议支持,提供给函数是 IP_PROTOCOL

返回值:

  • 申请创建 Socket 成功,返回值是当前 Socket 对应的文件描述符
  • 申请创建失败,返回 -1,同时设置 errno

5.3.2 bind()

#include <sys/types.h>    

#include <sys/socket.h>

int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

函数功能:

  •  根据 IP 版本协议和指定的网络地址结构体信息,当前进程绑定指定端口进行数据监控和接收。

参数:

  • int sockfd :  Socket 对应的文件描述符
  • const struct sockaddr *addr :  IP 地址结构体数据,需要提供的参数有 IP 协议,IP 地址 4 字节网络字节序数据和端口号网络字节序。同时要求 IP 地址必须是本机地址
  • socklen_t addrlen :  当前 struct sockaddr IP 地址结构体字节个数

返回值:

  • bind 操作成功, 返回 0
  • bind 操作失败返回 -1,同时设置对应的 errno

5.3.3 sendto()

#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 接收端,需要的数据【发送数据内容】【目标地址信息】

参数:

  • int sockfd : UDP 协议要求的 Socket
  • const void *buf :  发送数据缓冲区地址
  • size_t len : 发送数据字节数
  • int flags :  一般都是 0
  • const struct sockaddr *dest_addr : 目标接收端 IP 地址结构体数据,包括 IP 地址协议,IP 地址 4 字节网络字节序数据和端口号网络字节序数据。
  • socklen_t addrlen : 对应 struct sockaddr 结构体字节数

返回值:

  • 发送成功,返回发送数据字节数
  • 发送失败,返回 -1,并且设置对应的 errno

5.3.4 recvfrom()

#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 发送端数据

参数:

  • int sockfd : UDP 协议要求的 Socket
  • void *buf :  接收数据缓冲区地址
  • size_t len : 接收数据最大字节数,一般对应缓冲区大小
  • int flags :  一般都是 0
  • struct sockaddr *dest_addr : 当前参数可以提供用于存储发送端 IP 地址数据的结构体地址,也可以提供 NULL ,表示不接收发送端 IP 地址数据
  • socklen_t addrlen : 提供 socklen_t  类型变量地址,用于存储接收到的发送端 IP 地址结构体字节个数,如果 src_addr 提供 NULL,当前参数也提供 NULL

返回值:

  • 返回值 > 0 表示接收到的有效字节个数

  • 返回值 == 0 表示当前数据已接收完毕,或者 EOF (End Of File)

  • 返回值 -1 表示接受失败,同时设置对应的 errno

发送端:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h> 
#include <sys/socket.h>// 接收端 IPv4 点分十进制地址
#define DEST_IP_ADDR "192.168.16.125"
// 接收端端口号
#define DEST_PORT (9527)/*
UDP 发送端1. 申请 UDP 支持的 Socket2. 明确发送数据内容3. 准备接收端目标 IP 地址相关数据4. sendto 发送5. close 关闭资源
*/
int main(int argc, char const *argv[])
{// 1. 申请 UDP 支持的 Socket/*AF_INET    对应 IPv4 协议SOCK_DGRAM 满足 Socket Datagram 数据包套接字需求IPPROTO_IP IPPROTO_IP 支持 IP 协议*/int sock_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);if (-1 == sock_fd){perror("socket failed!");exit(1);}// 2. 明确发送数据内容char *str = "Hello UDP Receiver!";// 3. 准备目标接收端 IP 地址结构体数据,使用结构体 struct sockaddr_instruct sockaddr_in dest_addr;socklen_t socklen = sizeof(struct sockaddr_in);memset(&dest_addr, 0, socklen);// 3.1 明确当前使用的 IP 协议为 IPv4dest_addr.sin_family = AF_INET;// 3.2 端口号明确为 8848,提供大端字节序/网络字节序数据dest_addr.sin_port = htons(DEST_PORT);// 3.3 本地点分十进制 IP 地址字符串,转换为网络字节序数据 4 字节 IP 地址数据inet_pton(AF_INET, DEST_IP_ADDR, &(dest_addr.sin_addr.s_addr));// 4. sendto 发送ssize_t ret = sendto(sock_fd,                             // UDP Socket 对应文件描述符str,                                 // 发送数据缓冲区,对应字符串strlen(str),                         // 发送数据字节数0,                                   // 标志位 == 0(const struct sockaddr *)&dest_addr, // 接收端 IP 地址数据结构体地址。 socklen);                            // 接收端 IP 地址结构体字节数if (-1 == ret){perror("sendto failed!");/*【资源管理】发送数据出现问题,后续进程是执行退出操作,需要在退出之前,将使用的相关资源进行关闭,在 sendto 之前使用的资源,有且只有 socket 对应的文件描述符,利用 SystemCall close 函数进行关闭操作*/close(sock_fd);exit(1);}// 5. close 关闭资源close(sock_fd);return 0;
}

接收端:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>#include <unistd.h>#include <arpa/inet.h>
#include <sys/types.h> 
#include <sys/socket.h>// 本机 IPv4 点分十进制地址
#define HOST_IP_ADDR "192.168.16.125"
// 本机用于接收数据的端口号
#define HOST_PORT (9527)#define BUFFER_SIZE (256)/*
UDP 接收端1. 申请 UDP 支持的 Socket2. 【准备本机用于接收数据 IP 和 端口号 结构体】3. 【bind 操作】4. 准备接受数据缓冲区空间5. recvfrom 接收数据6. close 关闭资源
*/int main(int argc, char const *argv[])
{// 1. 申请 UDP 支持的 Socketint sock_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);// 2.【准备本机用于接收数据 IP 和 端口号 结构体】struct sockaddr_in host_addr;socklen_t socklen = sizeof(struct sockaddr_in);memset(&host_addr, 0, socklen);// 2.1 本地 IPv4 协议host_addr.sin_family = AF_INET;// 2.2 本地用于接收数据的端口号,需要网络字节序host_addr.sin_port = htons(HOST_PORT);// 2.3 本地 IPv4 点分十进制地址转换为网络字节序 IP 地址inet_pton(AF_INET, HOST_IP_ADDR, &(host_addr.sin_addr.s_addr));// 3. 【bind 操作】int ret = bind(sock_fd, (const struct sockaddr *)&host_addr, socklen);if (ret){perror("bind failed!");close(sock_fd);exit(1);}// 4. 准备接受数据缓冲区空间// 4.1 char 类型缓冲区空间,采用数组形式char buffer[BUFFER_SIZE] = "";// 4.2 本机用于存储发送端 IP 地址数据结构体struct sockaddr_in src_addr;memset(&src_addr, 0, socklen);// 5. recvfrom 接收数据ssize_t count = recvfrom(sock_fd,     // UDP 协议支持的 Socket 套接字buffer,      // 数据缓冲区地址BUFFER_SIZE, // 当前数据缓冲区字节数,同时也是最大接收数据字节数0,           // 标志位 0(struct sockaddr *)&src_addr,   // 用于存储发送端 IP 地址相关数据&socklen);   // 接收到的 struct sockaddr_in 字节数// 用于临时存储数据发送端 IP 地址字符串char src_ip_addr[16] = "";if (count > 0){printf("Data from %s:%u, Data : %s\n",// 网络 4 字节 IP 地址数据,转换为 IPv4 本地点分十进制 IP 地址字符串inet_ntop(AF_INET, &(src_addr.sin_addr.s_addr), src_ip_addr, 16),// 网络 2 字节 Port 端口号数据转本地ntohs(src_addr.sin_port),// 收到的数据内容buffer);}// 6. close 资源close(sock_fd);return 0;
}


文章转载自:

http://mHGfOGgo.nLffL.cn
http://xzIcYYcg.nLffL.cn
http://2cLDkJ0r.nLffL.cn
http://EGt1B5CO.nLffL.cn
http://xraCiqho.nLffL.cn
http://Su8ZhCy7.nLffL.cn
http://KaKgHttX.nLffL.cn
http://Pl1UgtVv.nLffL.cn
http://xv5oTxjj.nLffL.cn
http://yNuPzTka.nLffL.cn
http://TixSAe1c.nLffL.cn
http://iHvEwged.nLffL.cn
http://exImaldT.nLffL.cn
http://y7F8WBQw.nLffL.cn
http://ybbGv8O7.nLffL.cn
http://8YFMydec.nLffL.cn
http://AFsiNaZ0.nLffL.cn
http://zkhh2ETV.nLffL.cn
http://DVdNO5Th.nLffL.cn
http://2bR4Z0e5.nLffL.cn
http://qm17SP24.nLffL.cn
http://zxh70hRN.nLffL.cn
http://z39gsLcn.nLffL.cn
http://LPZDccuI.nLffL.cn
http://Tjxrooon.nLffL.cn
http://MUpDpmhW.nLffL.cn
http://vcHrEndg.nLffL.cn
http://rIjdVhgq.nLffL.cn
http://EzsbtO4i.nLffL.cn
http://lrW5HWNN.nLffL.cn
http://www.dtcms.com/a/386444.html

相关文章:

  • 在 Mac 环境安装全局默认版本 Python
  • 小迪安全v2023学习笔记(八十三讲)—— 组件安全JacksonFastJsonXStreamCVE复现
  • 鲁能大师显卡跑分计算规则,与算力对应关系?
  • 边缘智能的“隐形引擎”——TinyML 模型在 ARM Cortex-M 系列上的极致量化与加速实战
  • kernel32.dll如何修复?科普kernel32.dll缺失的故障问题的多种解决方法
  • git推送远程仓库
  • 「日拱一码」091 机器学习——集成学习
  • MTK Linux Charger驱动分析(五)- adapter_class.c
  • HarmonyOS 5.0应用开发——V2装饰器@once的使用
  • Day25_【深度学习(3)—PyTorch使用(5)—张量形状操作】
  • 微服务分布式事务解决方案梳理
  • 告别“长明灯”——塔能科技地下车库照明改造的极致节能
  • vue 3 阿里云视频播放器 如何让用户自己给视频添加节点
  • LinkedList 底层实现与 ArrayList 对比分析
  • 滚珠花键在半导体制造设备中承担怎样的核心功能?
  • 服装制造企业痛点解决方案:EFISH-SBC-RK3588 柔性化吊挂调度方案
  • 10cm钢板矫平机:工业制造中的“整形医生”
  • html表单登录模式代码
  • QUIC 协议域名封堵:核心原理、关键技术与实现路径(C/C++代码实现)
  • 8 基于机器学习进行遥感影像的地物分类-以随机森林为例
  • Qt读写SQLite示例
  • Jmeter性能测试之阶梯式场景、波浪式场景、服务器监控
  • 黄昏时刻复古胶片风格人像风光摄影后期Lr调色教程,手机滤镜PS+Lightroom预设下载!
  • Django ORM多对多关系实战指南
  • 【从零开始java学习|第十七篇】面向对象进阶
  • Three.js 开发实战教程(一):环境搭建与第一个 3D 场景
  • 旅游小程序的功能优势
  • LeetCode:7.接雨水
  • Android 安卓 问题解决记录 腾讯IM和厂商离线推送问题 点击离线推送无法唤醒APP启动页但某些Service服务和Application被启动
  • 动态规划解决系列子序列问题