UDP通信实现

news/2024/9/17 5:22:49/ 标签: udp, 网络协议, 网络

目录

前言

一、基础知识

1、跨主机传输

        1、字节序

         2、主机字节序和网络字节序

         3、IP转换

2、套接字

3、什么是UDP通信 

二、如何实现UDP通信 

        1、socket():创建套接字

        2、bind():绑定套接字 

         3、sendto():发送指定套接字文件数据

         4、recvfrom():接收指定地址信息的数据

 三、具体实现代码


前言

        在前面我们知道,在使用UDP通信是在传输层选择使用UDP协议,并且在传输层只有两个协议,分别是UDP和TCP协议,在本节中,我们就来学习如何实现UDP通信

一、基础知识

1、跨主机传输

        1、字节序

字节序:不同类型的CPU主机,内存存储多字节数据时的存在不同序列存储方式

        a、小端字节序(小端存储):低序字节存储在内存低地址上,高序字节存储在内存高地址上

        b、大端字节序(大端存储):低序字节存储在内存高地址上,高序字节存储在内存低地址上

        short、int、long 有字节序的概念

        char、float、字符串没有字节序的说法

如何查看电脑是大端存储还是小端存储

//查看电脑大端存储还是小端存储
#include<stdio.h>
int main(int argc, const char *argv[])
{int a=0x87654321;char *p=&a;printf("a=%#x\n",*p);return 0;
}

我的电脑输出的是a=0x21,说明是小段存储,其实大多数电脑都是小端存储

        

         2、主机字节序和网络字节序

  •         主机字节序:主机本身在计算机中存储多字节数据的方式(大端、小端:CPU)现目前电脑一般都是小端
  •         网络字节序:数据在网络中规定的传输方式,网络字节序使用大端字节序方式传输

        所以在跨主机传输过程中,需要使用统一的字节序,即网络字节序,避免兼容性问题

        主机字节序转换为网络字节序,便于传输:

#include <arpa/inet.h>uint32_t htonl(uint32_t hostlong);
功能:把 hostlong 主机字节序整型转换为网络字节序,返回值就是网络字节序整数uint16_t htons(uint16_t hostshort);
功能:把 hostshort 主机字节序短整型转换为网络字节序,返回值就是网络字节序短整数

        网络字节序转换为主机字节序,便于解析识别:

#include <arpa/inet.h>uint32_t ntohl(uint32_t netlong);
功能:把 netshort 网络字节序 整型 转换为主机存储的主机字节序,返回值就是 主机字节序整数uint16_t ntohs(uint16_t netshort);
功能:把 netshort 网络字节序短整型 转换为主机存储的主机字节序,返回值就是 主机字节序

         3、IP转换

        在主句传输数据时会对大于两个字节的数据进行网络字节序的转换,那么IP地址通常是大于两个字节的,也同样要进行IP转换

        IP地址整数转换为二进制网络字节序的IP地址 :

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>in_addr_t inet_addr(const char *cp);
功能:将 ip地址字符串 转换为 IP地址整数(网络字节序IP地址)参数:const char *cp:要转换的 IP地址的点分十进制字符串首地址返回值:
成功,返回 转换后的 网络字节序的IP地址   typedef uint32_t in_addr_t; 

        IP地址的二进制网络字节序转换为IP地址的整数:

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>char *inet_ntoa(struct in_addr in);
功能:把 IP地址转换为 点分十进制字符串格式参数:struct in_addr in:指定要转换的IP地址 的结构体类型,结构体中的成员为 IP地址类型:typedef uint32_t in_addr_t;struct in_addr {in_addr_t s_addr;//IP地址 网络字节序 整数};

2、套接字

        最早套接字和共享内存、消息队列、管道一样,只能实现一个主机内部的进程间通信,随着TCP/IP网络模型的引入,使得套接字能够支持不同主机之间的进程间通信,socket函数,创建一个套接字文件,可以在内核空间中创建两块缓冲区,用于发送数据,接收数据。也包含对应的TCP/IP协议规则

         使用socket()函数创建套接字文件

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>int socket(int domain, int type, int protocol);
功能:创建socket套接字,用于网络通信参数:
参数1:int domain:地址族,协议族AF_UNIX, AF_LOCAL   Local communication              unix(7)AF_INET             IPv4 Internet protocols          ip(7)AF_INET6            IPv6 Internet protocols          ipv6(7)
参数2:int type:类型SOCK_STREAM:字节流套接字,流式套接字,默认使用TCP协议SOCK_DGRAM:数据报套接字,报式套接字,默认使用UDP协议SOCK_RAW:原始套接字,其协议需要在第三个参数中指定
参数3:int protocol:协议0:使用默认协议IPPROTO_TCPIPPROTO_UDP返回值:
成功,返回 套接字文件描述符(套接字)
失败,返回-1,设置errno

3、什么是UDP通信 

        根据传输层的协议不同,通信的实现、通信的方式也各不相同,是不同的方式完成通信

        传输层:TCP、UDP

        通信方式有两种:UDP通信 与 TCP通信

         UDP通信的步骤:

二、如何实现UDP通信 

        1、socket():创建套接字

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>int socket(int domain, int type, int protocol);
功能:创建socket套接字,用于网络通信,在内核空间中创建套接字文件(有两个缓冲区:发送缓冲区、接收缓冲区),返回该套接字文件缓冲区的文件描述符参数:
参数1:int domain:地址族,协议族AF_UNIX, AF_LOCAL   Local communication              unix(7)AF_INET             IPv4 Internet protocols          ip(7)AF_INET6            IPv6 Internet protocols          ipv6(7)
参数2:int type:类型SOCK_STREAM:字节流套接字,流式套接字,默认使用TCP协议SOCK_DGRAM:数据报套接字,报式套接字,默认使用UDP协议SOCK_RAW:原始套接字,其协议需要在第三个参数中指定
参数3:int protocol:协议0:使用默认协议IPPROTO_TCPIPPROTO_UDP返回值:
成功,返回 套接字文件描述符(套接字)
失败,返回-1,设置errno

        2、bind():绑定套接字 

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>int bind(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);功能:绑定地址信息到指定的套接字文件描述符上,为套接字通信指定使用的IP、port参数:
参数1:int sockfd:指定要将地址信息绑定到哪个套接字上,套接字文件描述符
参数2:const struct sockaddr *addr:地址信息结构体的指针,通用的一个地址结构体,由于存在不同的地址族选择,所以地址的表示方式不一样,为了统一表示,所以参数为通用的地址信息结构体类型用来表示有一个地址信息结构体:通用结构体struct sockaddr {sa_family_t sa_family;char        sa_data[14];}真实的地址信息结构体需要根据地址族来指定不同的地址族有不同的地址信息结构体AF_INET地址族的地址信息结构体:struct sockaddr_in {sa_family_t    sin_family;//指定地址族,AF_INETin_port_t      sin_port;//端口号的网络字节序,2个字节struct in_addr sin_addr;//使用的ip地址的网络字节序(结构体类型)};struct in_addr {//ip地址的网络字节序结构体uint32_t       s_addr;//ip地址网络字节序};
参数3:           socklen_t addrlen:真实的地址信息结构体大小返回值:
成功,返回0
失败,返回-1,设置errno

         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);
功能:发送数据给指定的接收方,即当前进程套接字发送数据给指定的ip、port进程参数:
参数1:int sockfd:套接字,通信:使用指定的套接字描述符来发送数据(数据传入哪个套接字的缓冲区用于发送)
参数2:const void *buf:指定要发送的数据的首地址(把指定地址的数据进行发送)
参数3:size_t len:发送多少个字节
参数4:int flags:选项0:阻塞方式发送,当缓冲区满,阻塞等待,不继续执行MSG_DONTWAIT:非阻塞方式发送,当缓冲区满,不等待,返回错误失败
参数5:const struct sockaddr *dest_addr:地址信息结构体,指定将数据发送给谁(ip、port),填写对方的地址信息地址信息根据地址族不同,结构体信息内容不同如果:AF_INETAF_INET地址族的地址信息结构体:struct sockaddr_in {sa_family_t    sin_family;//指定地址族,AF_INETin_port_t      sin_port;//端口号的网络字节序,2个字节struct in_addr sin_addr;//使用的ip地址的网络字节序(结构体类型)};struct in_addr {//ip地址的网络字节序结构体uint32_t       s_addr;//ip地址网络字节序};
参数6:
socklen_t addrlen:真实的地址信息结构体的大小返回值:
成功,返回发送的字节数
失败,返回-1,设置errno                                                

         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);
功能:接收数据包,同时可以接收到数据包从哪里来(额可以获取发送方的地址信息)参数:
参数1:int sockfd:通信使用的套接字文件描述符,指定获取哪个套接字的数据(对方发送发送到ip、port的套接字缓冲区)
参数2:void *buf:存储读取到的数据,接收的数据存储的地址
参数3:size_t len:要读取多少个字节
参数4:int flags:选项   0:阻塞方式接收,当缓冲区为空,没有接收到数据时,阻塞等待,不继续执行MSG_DONTWAIT:非阻塞方式接收,当缓冲区为空(没有数据),不等待,返回错误失败 
参数5:struct sockaddr *src_addr:地址信息结构体,不同的地址族地址信息结构体不同,不同的地址族使用对应的结构体来存储,发送方的地址信息如果不想知道发送方的地址信息,则填NULL
参数6:socklen_t *addrlen:地址信息结构体的大小,指针对应空间存储如果不想获取,则填NULL返回值:
成功,返回收到的字节数     
失败,返回-1,设置错误码           

 三、具体实现代码

         在实现通信之前,我们使用下面网络调试助手来进行通信传输

通过网盘分享的文件:scomm.exe
链接: https://pan.baidu.com/s/1OkiZLT_CeoryEZepaOSGqQ 提取码: 8a85

 首先,更改下面的代码

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <pthread.h>#define PORT 20000
#define DEST_PORT 10000
#define DEST_IP "192.168.124.29"
#define BUF_SIZE 128int sockfd;
struct sockaddr_in destaddr;void *send_thread(void *arg) {char buf[BUF_SIZE];while (1) {bzero(buf, BUF_SIZE);fgets(buf, BUF_SIZE, stdin);sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr*)&destaddr, sizeof(struct sockaddr_in));}return NULL;
}void *recv_thread(void *arg) {char buf[BUF_SIZE];while (1) {struct sockaddr_in rcv_addr;socklen_t rcv_addr_len = sizeof(rcv_addr);int size = recvfrom(sockfd, buf, BUF_SIZE - 1, 0, (struct sockaddr*)&rcv_addr, &rcv_addr_len);if (size > 0) {buf[size] = '\0';printf("Received: %s\n", buf);}}return NULL;
}int main(int argc, const char *argv[]) {// 创建套接字,使用UDP通信sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0) {perror("socket creation failed");return -1;}// 绑定套接字struct sockaddr_in udpaddr;udpaddr.sin_family = AF_INET;udpaddr.sin_port = htons(PORT);udpaddr.sin_addr.s_addr = INADDR_ANY;if (bind(sockfd, (struct sockaddr*)&udpaddr, sizeof(struct sockaddr_in)) < 0) {perror("bind failed");close(sockfd);return -1;}// 设置对方的信息destaddr.sin_family = AF_INET;destaddr.sin_port = htons(DEST_PORT);destaddr.sin_addr.s_addr = inet_addr(DEST_IP);// 创建发送和接收线程pthread_t send_tid, recv_tid;pthread_create(&send_tid, NULL, send_thread, NULL);pthread_create(&recv_tid, NULL, recv_thread, NULL);// 等待线程完成pthread_join(send_tid, NULL);pthread_join(recv_tid, NULL);// 关闭套接字close(sockfd);return 0;
}

 但是代码中的IP地址要更改

更改步骤:

1、将下面的DEST_IP改为打开网络调试助手的IP地址,

        如何查找打开的网络调试助手的代码和端口

在这里查看网络端口和接收端的IP地址,每个人的不一样,因此要读者自己去设置


http://www.ppmy.cn/news/1521779.html

相关文章

2024.9.6

1> 手写unique_ptr智能指针 #include <iostream> //#include <memory> using namespace std; //unique_ptr<AA> p0(new AA("西施"));// 分配内存并初始化。 template <typename T> class unique_ptr { public:explicit unique_ptr(T p) …

当天审稿,当天上线,9月检索!

各领域CNKI知网普刊&#xff0c;最快一期预计下周送检&#xff0c;最快1天上线 领域广&#xff0c;计算机&#xff0c;社科&#xff0c;医学等各个方向都能收 包检索&#xff0c;可提供期刊部发票 知名出版社英文普刊 NO.1、Food Science and Nutrition Studies ISSN: 2573…

Linux——redis主从复制、哨兵模式

一、redis 的安全加固&#xff1a; 对redis数据库访问的角度 auth // 验证登录redis 数据库的用户acl // 设置redis用户的权限将配置完成的ACL策略写入配置文件 config rewrite //目前redis生效的配置全部写入到默认配置文件的尾部写入到acl文件中&#xff0c;在加载配置文件时…

开源通用验证码识别OCR —— DdddOcr 源码赏析(二)

文章目录 前言DdddOcr分类识别调用识别功能classification 函数源码classification 函数源码解读1. 分类功能不支持目标检测2. 转换为Image对象3. 根据模型配置调整图片尺寸和色彩模式4. 图像数据转换为浮点数据并归一化5. 图像数据预处理6. 运行模型&#xff0c;返回预测结果 …

DWPD指标:为何不再适用于大容量SSD?

固态硬盘&#xff08;Solid State Drives, SSD&#xff09;作为计算机行业中最具革命性的技术之一&#xff0c;凭借其更快的读写速度、增强的耐用性和能效&#xff0c;已经成为大多数用户的首选存储方案。然而&#xff0c;如同任何其他技术一样&#xff0c;SSD也面临自身的挑战…

C++ 栈的使用

在 C++ 中,栈(Stack)是一种后进先出(LIFO,Last In First Out)的数据结构,表示最后插入的元素最先被移除。C++ 提供了 STL(Standard Template Library)中的 std::stack 容器适配器来方便使用栈。 std::stack 的使用 std::stack 是一个容器适配器,它默认使用 std::de…

接口请求400

接口请求400 在Web开发中&#xff0c;接口请求错误是开发者经常遇到的问题之一。其中&#xff0c;400错误&#xff08;Bad Request&#xff09;尤为常见&#xff0c;它表明发送到服务器的请求有误或不能被服务器理解。本文将深入探讨接口请求400错误&#xff0c;从常见报错问题…

基于纠错码的哈希函数构造方案

一、前言 随着大数据时代的到来&#xff0c;交通数据量急剧增加&#xff0c;由此带来的交通安全问题日益凸显。传统的驾驶人信用管理系统在数据存储和管理上存在着诸多不足之处&#xff0c;例如中心化存储方案无法有效地进行信用存证及数据溯源。区块链技术以其去中心化和不可…

python网络爬虫(零)——认识网页结构

网页一般有三部分组成&#xff0c;分别是HTML&#xff08;超文本标记语言&#xff09;、CSS&#xff08;层叠样式表&#xff09;、JScript&#xff08;活动脚本语言&#xff09; 1.HTML HTML是整个网页的结构&#xff0c;相当于整个网站的框架。带“<”“>”符号都属于H…

Trying to update a textarea with string from an OpenAI request

题意&#xff1a;把从 OpenAI 请求中得到的字符串更新到一个文本区域中。 问题背景&#xff1a; Can anyone assist me with an issue Im facing. Im trying to append a string received back from an OpenAI request to an exisitng textarea element. The requested string…

设计之道:ORM、DAO、Service与三层架构的规范探索

引言&#xff1a; 实际开发中&#xff0c;遵守一定的开发规范&#xff0c;不仅可以提高开发效率&#xff0c;还可以提高项目的后续维护性以及项目的扩展性&#xff1b;了解一下本博客的项目设计规范&#xff0c;对项目开发很有意义 一、ORM思想 ORM&#xff08;Object-Relation…

P0.9/P1.25全倒装共阴节能COB超微小间距LED显示屏已抢占C位

COB&#xff08;Chip on Board&#xff09;技术最早发源于上世纪60年代&#xff0c;是将LED芯片直接封装在PCB电路板上&#xff0c;并用特种树脂做整体覆盖。COB实现“点” 光源到“面” 光源的转换。点间距有P0.3、P0.4、P0.5、P0.6、P0.7、P0.9、P1.25、P1.538、P1.5625、P1.…

pytorch计算张量中三维向量的欧式距离

如果 X 是一个包含多个三维向量的张量&#xff0c;形状为 [b, n, 3]&#xff0c;其中 b 是批次大小&#xff0c;n 是每个批次中的向量数量&#xff0c;那么可以使用类似的广播机制来计算同一批次内不同位置的三维向量之间的欧式距离。 以下是具体实现步骤&#xff1a; 扩展张量…

时序预测 | 基于DLinear+PatchTST多变量时间序列预测模型(pytorch)

目录 效果一览基本介绍程序设计参考资料 效果一览 基本介绍 DLinearPatchTST多变量时间序列 dlinear,patchtst python代码&#xff0c;pytorch架构 适合功率预测&#xff0c;风电光伏预测&#xff0c;负荷预测&#xff0c;流量预测&#xff0c;浓度预测&#xff0c;机械领域预…

Java 基于微信小程序的小区服务管理系统,附源码

博主介绍&#xff1a;✌stormjun、8年大厂程序员经历。全网粉丝15w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&…

websocket和轮询的区别?

问&#xff1a; websocket和轮询的区别&#xff1f; 回答&#xff1a; WebSocket 和定时轮询&#xff08;每隔几秒发送一次请求&#xff09;是两种不同的实时通信方法&#xff0c;各有优缺点&#xff0c;适用于不同的场景。以下是它们的主要区别及适用场景&#xff1a; WebSo…

Node.js sqlite3事件深入解析:trace、profile、change、error、open

在Node.js环境中&#xff0c;sqlite3库不仅提供了丰富的API用于数据库操作&#xff0c;还定义了一系列的事件&#xff0c;使得开发者能够监听和响应数据库操作过程中的各种状态变化。本文将深入解析sqlite3库中的trace、profile、change、error、open这五个事件&#xff0c;包括…

0903,LIST(merge,splice,sort,unique),SET(insert,erase)

目录 03_vector_delete.cc 04_vector_shrink.cc 05_vec_emplace_back.cc 06_listspec_splice.cc 07_classstruct.cc 08_set.cc 09_setErase.cc 作业 01 STL中的容器包括哪些&#xff1f;各自具有哪些特点&#xff1f; 02 题目&#xff1a;编写代码&#xff1a;将…

Android Camera系列(一):SurfaceView+Camera

心行慈善&#xff0c;何需努力看经—《西游记》 Android Camera系列&#xff08;一&#xff09;&#xff1a;SurfaceViewCamera Android Camera系列&#xff08;二&#xff09;&#xff1a;TextureViewCamera Android Camera系列&#xff08;三&#xff09;&#xff1a;GLSur…

20240902软考架构-------软考96-100答案解析

每日打卡题96-100答案 96、【2018年真题】 难度&#xff1a;难 CORBA服务端构件模型中&#xff0c; 是CORBA对象的真正实现&#xff0c;负责完成客户端请求。 A.伺服对象&#xff08;Servant&#xff09; B.对象适配器&#xff08;Object Adapter&#xff09; C.对象请求代理&…