UDP通信实现

news2024/9/23 13:19:18

目录

前言

一、基础知识

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_TCP
        IPPROTO_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_TCP
        IPPROTO_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_INET
               in_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_INET
        AF_INET地址族的地址信息结构体:
        struct sockaddr_in {
               sa_family_t    sin_family;//指定地址族,AF_INET
               in_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 128

int 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.coloradmin.cn/o/2110910.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C语言操作符汇总(上)

目录 前言 一、操作符的分类 二、⼆进制和进制转换 1. 二进制转10进制 2. 10进制转2进制数字 3. 2进制转8进制和16进制 3.1 2进制转8进制 3.2 二进制转16进制 三、原码、反码、补码 四、移位操作符 1. 左移操作符 2. 右移操作符 五、位操作符&#xff1a;&…

10-1RT-Thread动态内存管理

10-1RT-Thread动态内存管理 在嵌入式系统中&#xff0c;变量和中间数据一般存放在系统存储空间中。只有在实际使用时&#xff0c;才将它们从存储空间读取到CPU进行运算。存储空间可分为两种&#xff0c;内部存储空间rem和外部存储空间rome或flash。其中ram或称之为内存&…

【Linux详解】命令行参数|环境变量

目录 一、命令行参数 二、环境变量 1.环境变量的基本概念 2.查看环境变量的方法 3.环境变量相关命令 4.环境变量的组织方式以及获取环境变量的三种方法 环境变量具有全局属性 一、命令行参数 【示例1】main函数也是函数&#xff0c;main函数可以带参吗&#xff1f; 没…

Python教程(二十) : 十分钟入门【PyQt6】

文章目录 专栏列表环境准备1 安装 Python2 安装 PyQt6 PyQt6 中的模块使用模块创建一个窗体&#xff1a; PyQt6 常用的控件1. QPushButton&#xff08;按钮&#xff09;2. QLabel&#xff08;标签&#xff09;3. QLineEdit&#xff08;文本输入框&#xff09;4. QTextEdit&…

(4)SVG-path中的椭圆弧A(绝对)或a(相对)

1、概念 表示经过起始点(即上一条命令的结束点)&#xff0c;到结束点之间画一段椭圆弧 2、7个参数 rx&#xff0c;ry&#xff0c;x-axis-rotation&#xff0c;large-arc-flag&#xff0c;sweep-flag&#xff0c;x&#xff0c;y &#xff08;1&#xff09;和&#xff08;2&a…

FFMpeg环境搭建(WIN10)

0、前期准备 软件环境&#xff1a;Win10 qtcreator 软件准备&#xff1a;MSYS2 安装包、 FFmpeg源码 1、软件安装 通过MSYS2安装编译工具 1、打开MSYS2安装包&#xff0c;一路next即可 &#xff08;注&#xff1a;如果需要更改路径可以自行更改&#xff09; 2、安装完成…

虚拟现实辅助工程技术助力多学科协同评估

在当今高速发展的经济环境中&#xff0c;制造业面临着多重挑战&#xff0c;包括提高产品性能、压缩设计周期、实现轻量化设计和降低成本。为了有效应对这些挑战&#xff0c;多学科协同评估成为缩短研发周期和提升研制质量的关键手段。 传统的多学科评估面临着数据孤立与融合困难…

Android 系统源码项目加载预编好的so库

Android 系统源码项目加载预编好的so库 文章目录 Android 系统源码项目加载预编好的so库一、前言二、源码中加载so1、Android.mk加载so加载so的主要相关代码&#xff1a; 2、Android.bp加载so&#xff08;1&#xff09;Android.mk使用源码命令编译成Android.bp&#xff08;2&am…

Java灰度发布

有没有在北京面试java的小伙伴&#xff0c;每家公司面试问的问题都不一样&#xff0c;昨天面试官问到了灰度发布&#xff0c;一脸懵&#xff0c;好像在哪儿听说过&#xff0c;毕竟我都没发布过&#xff0c;之前都是项目组长在干这些事儿&#xff0c;所以聊聊&#xff0c;了解一…

驾驭Autofac,ASP.NET WebApi实现依赖注入详细步骤总结

前言 嘿&#xff0c;小伙伴们&#xff0c;今天我们来一场 Autofac 的学习之旅吧&#xff01; Autofac 是一个轻量级的依赖注入框架&#xff0c;专门为 .NET 应用程序量身定做&#xff0c;它就像是你代码中的 “魔法师”&#xff0c;用它来管理对象的生命周期&#xff0c;让你…

828华为云征文|华为云Flexus X实例docker部署最新gitlab社区版,搭建自己的私人代码仓库

828华为云征文&#xff5c;华为云Flexus X实例docker部署最新gitlab社区版&#xff0c;搭建自己的私人代码仓库 华为云最近正在举办828 B2B企业节&#xff0c;Flexus X实例的促销力度非常大&#xff0c;特别适合那些对算力性能有高要求的小伙伴。如果你有自建MySQL、Redis、Ng…

大数据采集迁移工具

Flume Sqoop kafka框架 MQ&#xff1a;消息队列 broker相当于服务器 消息队列

栈和队列(1)

空栈先移动栈顶再加数据&#xff0c;满栈先插入数据再移 栈的基本概念栈是一种后进先出&#xff08;LIFO&#xff0c;Last In First Out&#xff09;的数据结构。栈支持两种主要的操作&#xff1a;•压栈&#xff08;Push&#xff09;&#xff1a;向栈中添加一个元素。•弹栈&…

Kubernetes v1.28.0安装详解

Kubernetes v1.28.0安装详解 一.环境初始化 要在所有节点执行命令进行配置 1、检查操作系统的版本 此部署环境为CentOS 7.9 [rootCentOS7 ~]# cat /etc/redhat-release CentOS Linux release 7.9.2009 (Core) [rootCentOS7 ~]#2、主机名解析 为了方便集群节点间的互相调…

活动系统开发之采用设计模式与非设计模式的区别-数据库设计及代码设计

1、数据库ER图 2、应用框架选用 PHP语言对应的thinkphp6.1应用框架 3、功能代码设计(后端) a、父类Base.php i&#xff1a;控制登录&#xff0c;只能登录后管理员才能操作&#xff1b; ii&#xff1a;控制按钮权限&#xff0c;管理员不仅要登录&#xff0c;且必须要有对应菜单…

报错处理:超过Uobject最大数量

处理方式 一、打包时项目中设置游戏中UObject的最大数量为100000000 二、打包后的配置文件中设置 打包路径&#xff1a; 一厅统管\Windows\YZ_YTTG\Saved\Config\Windows\Engine.ini文件下添加配置文件 [/Script/Engine.GarbageCollectionSettings] gc.MaxObjectsInEditor1000…

API 网关 OpenID Connect 实战:单点登录(SSO)如此简单

作者&#xff1a;戴靖泽&#xff0c;阿里云 API 网关研发&#xff0c;Higress 开源社区 Member 前言 随着企业的发展&#xff0c;所使用的系统数量逐渐增多&#xff0c;用户在使用不同系统时需要频繁登录&#xff0c;导致用户体验较差。单点登录&#xff08;Single Sign-On&a…

2024最新!Facebook手机版和网页版改名教程!

Facebook作为全球最大的社交平台之一&#xff0c;允许用户自定义名字和昵称。在Facebook更新姓名可以帮助您更好的展现账号形象。本文将为您提供详细的步骤指导&#xff0c;帮助您在手机APP和网页版上轻松完成Facebook改名操作。 Facebook手机版改名 打开Facebook APP并登录账号…

区块链ARC如何能让节点能够大规模处理交易数据

​​发表时间&#xff1a;2024年8月7日 TAAL技术主管Michael Bckli表示&#xff0c;TAAL公司一直在对ARC进行测试&#xff0c;并准备在今年年底全面发布。因TAAL在区块链交易处理方面具备深厚的专业知识&#xff0c;BSV区块链委托TAAL进行ARC开源参考落地方案的开发。 ARC是一个…

魔珐科技受邀参与外滩大会:以3D数字人AIGC产品赋能大资管行业,重塑金融服务边界

在人工智能浪潮的推动下&#xff0c;金融行业正经历着前所未有的场景革命。2024年Inclusion外滩大会作为行业交流的盛会&#xff0c;汇聚了众多学者、专家及企业领袖&#xff0c;共同探讨AI技术在多领域的深度应用&#xff0c;特别是其在金融行业中的革新潜力。 在外滩大会上&…