UDP函数接口分析
网络编程
创建时间: May 30, 2024 7:54 PM
UDP编程
Title: UDP编程函数
Subject: 进程间通信
Date: 2024/6/3
<aside>
💡
UDP协议使用下面的数据类型来存储目标IP和目标端口
struct sockaddr_in dest_addr;
只有面向无连接时才需要,填后面的两个参数(`const struct sockaddr` *dest_addr, `socklen_t` addrlen)
```c
sendto(udp_socket,buf,strlen(buf),0, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
```
因为面向无连接的UDP是不可靠的,不保证交付,不会补发,只管发送过去 后面就不管了,所以只需要,IP地址和任务端口号,定位到就行,无需建立连接
</aside>
-
socket创建套接字文件
AF_INET IPV4 互联网协议(网络层)
SOCK_DGRAM 指定UDP(传输层)
pritocol:指定协议,不确定,填0 (int型)#include <sys/socket.h> #include <netinet/in.h> udp_socket = socket(PF_INET, SOCK_DGRAM, 0);
man原文
当创建一个 UDP 套接字时, 它的本地和远程地址是不确定的. 可以使用带一个
有效目的地址作为参数的 sendto(2) 或者 sendmsg(2) 立即发送数据报.如果套
接字上调用了 connect(2) 时, 则设置默认的目的地址, 数据报可以使用
send(2) 或者 write(2) 发送而不需要指定目的地址. 也可以通过传递一个地址
给 sendto(2) 或者 sendmsg(2) 来发送到其它目的地址. 为了接收信息包,套接
字必须首先用 bind(2) 绑定一个本地地址, 如果没有这么做, 套接字层在第一个
用户接收请求时将自动分配一个本地端口.
所有接收操作只返回一个信息包. 当信息包小于所传递的缓冲区时, 则只返回那
些数据, 当信息包大于所传递的缓冲区时,则截断信息包并设置 MSG_TRUNC 标志.
如果设置了 MSG_DONTROUTE 标志,则发送时目的地址必须指向一个本地接口地址,
而且信息包只发到该接口.
当 UDP 的总长超过接口 MTU(Maximum Transmission Unit 最大传输单元)时,
UDP 会对信息包进行分段. 一个更为网络友好的可选方法是使用 path MTU
discovery(路径MTU发现), 它描述于 ip(7) 中的 IP_PMTU_DISCOVER 部分
-
接受端必须需要先进行bind绑定,让端口号和IP与套接字句柄进行绑定
int bind(int socket,
const struct sockaddr *address,
socklen_t address_len);
bind函数的第二个参数是不需要这个类型的,而是需要 struct sockaddr_in,所以需要进行强制类型转换成函数原型中的类型struct sockaddr -
当信息包大于所传递的缓冲区时,如果不设置MSG_TRUNC 标志则 会默认忽略数据
-
MTU:最大传输单元(值跟传输协议有关),1. 分片重组功能(超过最大传输单元),2. 路径规划(算法)
-
分片重组(Fragment Reassembly)是计算机网络和数据通信领域中的一个重要概念,涉及将分段传输的数据包重新组装成原始的完整数据。这个过程通常在接收端进行,以下是一些关键点:
- 数据分片:在网络通信中,数据包可能因为网络协议的限制或网络路径的最大传输单元(MTU)限制而被分割成更小的片段。这种情况在IP协议中尤为常见。
- 传输过程:这些分片的数据包通过网络传输到目标设备。在传输过程中,每个分片都会携带一些额外的信息,比如分片的序号和总分片数,以便接收端能够正确地重组数据。
- 重组过程:接收端在收到所有分片后,根据分片信息将它们按正确的顺序重新组装成完整的数据包。如果某个分片丢失或出错,接收端可能会请求重新发送该分片。
- 协议支持:不同的网络协议对分片和重组的支持有所不同。比如,IPv4协议支持分片和重组,而IPv6协议则尽量避免在网络层进行分片,更多依赖于传输层协议(如TCP)来处理数据的分段和重组。
- 应用场景:分片重组在很多场景下都非常重要,比如在视频流、文件传输、网页加载等需要传输大数据量的应用中。
总的来说,分片重组是确保大数据包在网络中能够可靠传输和正确接收的关键技术。
-
UDP编程
/************************************************************************************************************************************** * * 字节序:数据以字节流的方式进行传输,底层都是采用二进制,字节流的顺序是由架构决定的,现在假设使用X86架构,是采用小端存储 * 网络字节序: * 本地字节序: * * 大端存储:低地址存储高字节 当数据超过1个字节的时候才需要区分大端还是小端 假设int型 0x12345678 0x12 | 0x34 | 0x56 | 0x78 * 小端存储:低地址存储低字节 当数据超过1个字节的时候才需要区分大端还是小端 假设int型 0x12345678 0x78 | 0x56 | 0x34 | 0x12 * * 设备A采用X86架构,所以设备A采用小端存储 待发送的数据 :0x12345678 * 设备B采用ARM架构,所以设备B采用大端存储 待接收的数据 :? * * 为了统一发送数据的格式,所以互联网传输的数据统一采用大端方式,为了方便开发,linux系统提供了转换的接口:htonl、htons、ntohl、ntohs * * h :host 主机/本地 * to: 转换 * n :net 网络 * l :long 长整型 * s :short 短整型 * * htons:把本地字节序的一个短整型转换为网络字节序 * htonl:把本地字节序的一个长整型转换为网络字节序 * ntohs:把网络字节序的一个短整型转换为本地字节序 * ntohl:把网络字节序的一个长整型转换为本地字节序 * ***********************************************************************************************************************************/ #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdio.h> #include <errno.h> #include <sys/socket.h> #include <netinet/in.h> #include <netinet/ip.h> #include <arpa/inet.h> #include <sys/socket.h> #include <netinet/in.h> #include <netinet/udp.h> #include <stdlib.h> #include <string.h> int main(int argc,char *argv[]) { //检查参数有效性 //1.创建UDP套接字 int udp_socket = socket(AF_INET, SOCK_DGRAM, 0); if (udp_socket == -1) { fprintf(stderr, "udp socket error,errno:%d,%s\n",errno,strerror(errno)); exit(1); } //2.需要先绑定主机的端口和地址 struct sockaddr_in host_addr; host_addr.sin_family = AF_INET; //协议族,是固定的 host_addr.sin_port = htons(atoi(argv[1])); //目标端口,必须转换为网络字节序 host_addr.sin_addr.s_addr = inet_addr(argv[2]); //目标地址 "192.168.64.xxx" 已经转换为网络字节序 bind(udp_socket,(struct sockaddr *)&host_addr, sizeof(host_addr));//绑定的是自己的端口号的IP地址 //3.循环调用recvfrom准备接收数据 char buf[128] = {0}; while(1) { recvfrom(udp_socket,buf,sizeof(buf), 0 ,NULL,NULL); printf("recv data is [%s]\n",buf); bzero(buf,sizeof(buf)); } return 0; } // 运行可执行文件 ./xxx 端口 目标地址 int main(int argc,char *argv[]) { //检查参数有效性 //UDP协议使用下面的数据类型来存储目标IP和目标端口 struct sockaddr_in dest_addr; //1.创建UDP套接字 int udp_socket = socket(AF_INET, SOCK_DGRAM, 0); if (udp_socket == -1) { fprintf(stderr, "udp socket error,errno:%d,%s\n",errno,strerror(errno)); exit(1); } //2.向目标主机发送消息,需要设置目标端口和目标地址 char buf[128] = {0}; dest_addr.sin_family = AF_INET; //协议族,是固定的 dest_addr.sin_port = htons(atoi(argv[1])); //目标端口,必须转换为网络字节序 dest_addr.sin_addr.s_addr = inet_addr(argv[2]); //目标地址 "192.168.64.xxx" 已经转换为网络字节序 while(1) { scanf("%s",buf); sendto(udp_socket,buf,strlen(buf),0, (struct sockaddr *)&dest_addr, sizeof(dest_addr)); bzero(buf,sizeof(buf)); } return 0; }
-
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· Qt个人项目总结 —— MySQL数据库查询与断言