基于UDP组播的无线搜索IP功能实现以及代码封装

基于UDP组播的无线搜索功能

一、主要工作内容

1、接口调用

学习如何调用共享库已经封装好的代码接口,根据一些的demo文件代码实现对该功能模块的调用。
①根据共享库封装好的函数接口来选择调用内容和传入信息;
②注意回调函数的定义与声明,头文件的调用以及相应函数的声明;

2、函数封装

将本地所实现功能代码封装成带有接口的代码模块,声明文件,实现模块以及可以在本地运行的demo。
①如何将本地代码进行封装处理
②回调函数的设置以及声明
③接口的初始化处理以及头文件的声明和调用

3、表头设置与CRC16校验

学习了何为表头结构以及怎么将表头与内容进行拼接,使用CRC校验来验证表头数据是否一致。

4、cJSON格式

如何将得到的信息转换为cJSON格式并且可以进行传输。

5、回调函数的使用

在封装好的函数中留下回调函数接口,方便外部用户调用所封装好的函数。

二、代码复现

1、服务器端:

(1).h文件(头文件声明以及其他文件引用)
/*************************************************************************
无线查找接口文件
  @File Name: search_device.h
@Author: hanhaofeng
@Created Time: 2023年03月23日 星期四
************************************************************************/
#ifndef _NETWORK_MULTICAST_H_  //防止重复声明,不会反复编译同一个文件
#define _NETWORK_MULTICAST_H_  
#ifdef __cplusplus
extern "C" {
#endif
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>    
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include "cJSON.h"
#include "os_network_multicast.h"

// NetworkMulticast_S network_info;
// typedef void *(*HandleData)(void *recv_info);
// typedef void *(*HandleInfo)(void *recv_data);

typedef struct os_searchdevicemulticast
{

    int nmcast_port;    // 组播端口
    char amcast_ip[16]; // 组播地址

    // HandleInfo sdhandkedata // 数据处理函数
} SearchdeviceMulticast_S;
SearchdeviceMulticast_S network_data;
/*组播初始化,初始化后会自动接受数据并处理
 *@network_info  传入传出参数,用于保存初始化网络数据
 *返回值: 成功:0    失败:-1
 * */
int search_device_init(SearchdeviceMulticast_S *network_data);
#ifdef __cplusplus
}
#endif
#endif

函数要提前声明;
头文件不能缺少;

(2).c文件(代码功能实现文件)
/*************************************************************************
无线查找接口文件
  @File Name: search_device.c
@Author: hanhaofeng
@Created Time: 2023年03月23日 星期四
************************************************************************/
#include <stdlib.h>
#include <stdio.h>
#include "os_network_multicast.h"
#include "search_device.h"
#include "cJSON.h"

#pragma pack(1)
typedef struct _DATA_HANDLE_
{
    /* 公司名称 BL*/
    char u8CompanyName[2];
    /* 项目代码 */
    unsigned short u16ProjectCode;
    /* 数据长度 */
    unsigned short u16DataLen;
    /* 扩展字段,代表头要扩展几个字节,1代表可扩展1字节,最多可扩展256字节,不用填0即可 */
    unsigned char u8ExpansionLen;
    /* crc校验值,一下所有数据相加 */
    unsigned short u16CrcData;
    /* 唯一标识符:默认为本机mac地址(6字节)+资源序号(1字节) */
    char u8OnlyIdentifier[7];
    /* 过程标识符:单次操作中唯一信息,不能为0.同一操作 */
    unsigned short u16CourseIdentidier;
    /* 本机MAC地址 */
    char u8MACaddr[6];
    /* 本机ip地址 */
    char u8IPaddr[4];
    /* 设备类型 */
    char u8DeviceType;
    /* 协议类型:0:请求 1:应答 */
    unsigned char u8AgreementType;
    /* 保留字段 */
    char u8Reserve[4];
    /* 操作代码 */
    int nActionCode;
} SearchDeviceDataHead_S;
#pragma pack()

NetworkMulticast_S network_info;
// SearchdeviceMulticast_S network_data;
typedef void *(*HandleData)(void *recv_info);
unsigned short do_crc(unsigned char *ptr, int len);

/* 处理接收数据 */
void *deal_data(void *pRecvInfo)
{
    UserRecv_S *pRecv = (UserRecv_S *)pRecvInfo;
    if (pRecv == NULL || pRecv->data == NULL)
    {
        return -1;
    }
    SearchDeviceDataHead_S *pHeadInfo = (SearchDeviceDataHead_S *)pRecv->data;
    /* 校验表头信息 */
    if (pHeadInfo->u8CompanyName[0] != 'B' || pHeadInfo->u8CompanyName[1] != 'L')
    {
        printf("公司名称错误\n");
        return -1;
    }
    if (pHeadInfo->u16ProjectCode != 0x66)
    {
        printf("项目代码错误\n");
        return -1;
    }
    char *pData = (char *)(pRecv->data + sizeof(SearchDeviceDataHead_S));
    int nCrc = do_crc(pData, pHeadInfo->u16DataLen);
    if (nCrc != pHeadInfo->u16CrcData)
    {
        printf("CRC校验错误\n");
        return -1;
    }
    switch (pHeadInfo->nActionCode)
    {
    /* 获取设备IP */
    case 1000:
        printf("得到的应答信息:%s\n", pData);
        if(network_info.sdhandkedata != NULL)
        {
            network_info.sdhandkedata(IP)
        }
        
        break;
    default:
        break;
    }
    return pData;
    return NULL;
}

/* Crc校验 */
unsigned short do_crc(unsigned char *ptr, int nlen)
{
    if (ptr == NULL)
    {
        return -1;
    }
    unsigned int i;
    unsigned short nCrc = 0xFFFF; // crc16位寄存器初始值
    while (len--)
    {
        nCrc ^= *ptr;
        for (i = 0; i < 8; ++i)
        {
            if (nCrc & 1)
            {
                nCrc = (nCrc >> 1);
                nCrc ^= 0xA001; // A001是8005按位颠倒后的结果
            }
            else
            {
                nCrc = (nCrc >> 1);
            }
        }
        ptr++;
    }
    return nCrc;
}

/* 初始化表头以及发送请求信息 */
int data_send(void *pBody, size_t nSize, int nActionCode)
{
    SearchDeviceDataHead_S stHeadInfo;
    SearchDeviceDataHead_S* pstHeadInfo = &stHeadInfo;
    memset(pstHeadInfo, 0, sizeof(pstHeadInfo));//结构体名称
    stHeadInfo.u8CompanyName[0] = 'B';
    stHeadInfo.u8CompanyName[1] = 'L';
    stHeadInfo.u16ProjectCode = 0x66;
    stHeadInfo.u16CourseIdentidier = 0;
    stHeadInfo.u8AgreementType = 0;
    stHeadInfo.nActionCode = nActionCode;
    stHeadInfo.u16CrcData = do_crc(pBody, nSize);

    /* server发送的请求信息 */
    stHeadInfo.u16DataLen = nSize;
    int nDataLen = sizeof(stHeadInfo) + stHeadInfo.u16DataLen;
    char *pData = (char *)malloc(nDataLen);
    /* 填充表头 */
    memcpy(pData, &stHeadInfo, sizeof(stHeadInfo));
    /* 将表头与发送数据拼接 */
    memcpy(pData + sizeof(stHeadInfo), pBody, stHeadInfo.u16DataLen);//指针位移
    int mark = os_networkmulticast_send(pData, nDataLen, &network_info);
    if (mark == -1)
    {
        printf("send failed\n");
        return -1;
    }
    if (pData)
    {
        free(pData);
        pData = NULL;
    }
    return 0;
}

/* 组播初始化 */
int search_device_init(SearchdeviceMulticast_S *network_data)
{
    memset(&network_info, 0, sizeof(network_info));

    network_info.entype = MULTICAST;
    network_info.stsrc_ip_info.port = network_data->nmcast_port;
    strcpy(network_info.amcast_ip, network_data->amcast_ip);
    network_info.fnhandledata = deal_data;
    int ret = os_networkmulticast_init(&network_info);
    if (ret == -1)
    {
        printf("search_device_init fail\n");
        return -1;
    }
    char *pDataBody = "这个是server发出的请求信息";
    // data_send(pDataBody, strlen(pDataBody) + 1, 1000);
    return data_send(pDataBody, strlen(pDataBody) + 1, 1000);
}

在这里插入图片描述
回溯函数的调用,其中deal_data是自己来编写的,具体实现什么功能;
函数初始化要传入指定的参数。

(3)本地运行demo文件
#include <stdlib.h>
#include <stdio.h>
#include "search_device.h"
#include "cJSON.h"
#include "os_network_multicast.h"

int main()
{
    SearchdeviceMulticast_S network_data;
    memset(&(network_data), 0, sizeof(network_data));
    network_data.nmcast_port = 9999;
    strcpy(network_data.amcast_ip, "224.0.0.1");
    // network_data.sdhandledata = data_handle;
    // data_send(pDataBody, strlen(pDataBody) + 1, 1000);
    while (1)
    {
        int ret1 = search_device_init(&network_data);
        if (ret1 == -1)
        {
            printf("search_device_init fail\n");
            return -1;
        }
        // else
        // {
        //     printf("初始化成功\n");
        // }
        // data_send(pDataBody, strlen(pDataBody) + 1, 1000);
        printf("发送请求数据\n");
        sleep(2);
    }
    return 0;
}

2、客户端:

(1).h文件(头文件声明以及其他文件引用)
/*************************************************************************
无线查找设备端接口文件
  @File Name: search_device.h
@Author: hanhaofeng
@Created Time: 2023年03月23日 星期四 
************************************************************************/
#ifndef _NETWORK_MULTICAST_H_
#define _NETWORK_MULTICAST_H_//防止重复编译
// #ifdef __cplusplus
//  extern "C" {
// #endif
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>    
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include "cJSON.h"
#include "os_network_multicast.h"

#define CD_MAX_IPV4_SIZE (16)
typedef void* (*HandleInfo)(void* recv_data);

typedef struct os_searchdevicemulticast
{
    int nmcast_port;        // 组播端口         命名规范
    char amcast_ip[CD_MAX_IPV4_SIZE];     // 组播地址  命名规范
    HandleInfo sdhandkedata // 数据处理函数
} SearchdeviceMulticast_S;

// SearchdeviceMulticast_S network_data

/*组播初始化,初始化后会自动接受数据并处理
 *@network_info  传入传出参数,用于保存初始化网络数据
 *返回值: 成功:0    失败:-1
 * */
int search_device_init(SearchdeviceMulticast_S* network_data);
#endif

(2).c文件(代码功能实现文件)
/*************************************************************************
无线查找设备端接口文件
  @File Name: search_device.c
@Author: hanhaofeng
@Created Time: 2023年03月23日 星期四
************************************************************************/
#include "os_network_multicast.h"
#include <stdlib.h>
#include <stdio.h>
#include "cJSON.h"
#include "search_device.h"

/* zIP大小 */

// typedef void *(*HandleData)(void *recv_info);
unsigned short do_crc(unsigned char *ptr, int len);
#define DATA_1 ('B')
#define DATA_2 ('L')

#pragma pack(1)
typedef struct _DATA_HANDLE_
{
    /* 公司名称 BL*/
    char u8CompanyName[2];
    /* 项目代码 */
    unsigned short u16ProjectCode;
    /* 数据长度 */
    unsigned short u16DataLen;
    /* 扩展字段,代表头要扩展几个字节,1代表可扩展1字节,最多可扩展256字节,不用填0即可 */
    unsigned char u8ExpansionLen;
    /* crc校验值,一下所有数据相加 */
    unsigned short u16CrcData;
    /* 唯一标识符:默认为本机mac地址(6字节)+资源序号(1字节) */
    char u8OnlyIdentifier[7];
    /* 过程标识符:单次操作中唯一信息,不能为0.同一操作 */
    unsigned short u16CourseIdentidier;
    /* 本机MAC地址 */
    char u8MACaddr[6];
    /* 本机ip地址 */
    char u8IPaddr[4];
    /* 设备类型 */
    char u8DeviceType;
    /* 协议类型:0:请求 1:应答 */
    unsigned char u8AgreementType;
    /* 保留字段 */
    char u8Reserve[4];
    /* 操作代码 */
    int nActionCode;
} SearchDeviceDataHead_S;
#pragma pack()

NetworkMulticast_S g_stNetworkInfo;

void *deal_data(void *recv_info)
{

    UserRecv_S *pRecv = (UserRecv_S *)recv_info;
    SearchDeviceDataHead_S *pHeadInfo = (SearchDeviceDataHead_S *)pRecv->data;
    /* 校验表头信息 */
    if (pHeadInfo->u8CompanyName[0] != DATA_1 || pHeadInfo->u8CompanyName[1] != DATA_2)
    {
        printf("公司名称错误\n");
        return -1;
    }
    if (pHeadInfo->u16ProjectCode != 0x66)
    {
        printf("项目代码错误\n");
        return -1;
    }
    char *pData = (char *)(pRecv->data + sizeof(SearchDeviceDataHead_S));
    unsigned short nCrc = 0;
    nCrc = do_crc(pData, pHeadInfo->u16DataLen);
    if (nCrc != pHeadInfo->u16CrcData)
    {
        printf("CRC校验错误\n");
        return -1;
    }
    char strDeviceIp[CD_MAX_IPV4_SIZE];
    memset(strDeviceIp, 0, sizeof(strDeviceIp));
    switch (pHeadInfo->nActionCode)
    {
    /* 获取设备IP */
    case 1000:
        web_get_localIp(pHeadInfo->nActionCode);
        // data_send(web_get_localIp(pRecv));
        break;
    default:
        break;
    }
    return NULL;
}

/* 获取设备地址 */
int web_get_localIp(int nActionCode)
{
    char pLocalIp[CD_MAX_IPV4_SIZE];
    memset(pLocalIp, 0, CD_MAX_IPV4_SIZE);
    int mark = get_localip(pLocalIp);
    if (mark == -1)
    {
        printf("getIP failed\n");
    }
    else
    {
        printf("getIP success\n");
    }
    /* 创建头指针 */
    cJSON *cJsonHead = NULL;
    char *strInfo = NULL; // 将cJSON格式数据转化为字符串形式
    /* 创建一个cJSON数据对象(链表头结点) */
    cJsonHead = cJSON_CreateObject();
    if (cJsonHead == NULL)
    {
        return -1;
    }
    /* 添加一条字符串类型的cJSON数据(添加一个链表节点)*/
    cJSON_AddStringToObject(cJsonHead, "ip", pLocalIp);
    strInfo = cJSON_Print(cJsonHead);
    int ret = data_send(strInfo, strlen(strInfo) + 1, nActionCode);
    if (ret == -1)
    {
    }
    if (strInfo)
    {
        free(strInfo);
        strInfo = NULL;
    }
    if (cJsonHead)
    {
        cJSON_Delete(cJsonHead);
        cJsonHead = NULL;
    }
    return 0;
}
/* client端应答 */
int data_send(void *pBody, size_t nSize, int nActionCode)
{

    SearchDeviceDataHead_S stHeadInfo;
    memset(&stHeadInfo, 0, sizeof(stHeadInfo));
    stHeadInfo.u8CompanyName[0] = 'B';
    stHeadInfo.u8CompanyName[1] = 'L';
    stHeadInfo.u16ProjectCode = 0x66;
    stHeadInfo.u16CourseIdentidier = 0;
    stHeadInfo.u8AgreementType = 1;
    stHeadInfo.u16CrcData = do_crc(pBody, nSize);

    /* 这个是client发出的应答信息 */
    stHeadInfo.nActionCode = nActionCode;
    stHeadInfo.u16DataLen = nSize;
    unsigned short nDataLen = sizeof(stHeadInfo) + stHeadInfo.u16DataLen;
    char *pData = (char *)malloc(nDataLen);
    if (pData == NULL)
    {
        printf("申请内存错误\n");
        return -1;
    }
    memcpy(pData, &stHeadInfo, sizeof(stHeadInfo));
    memcpy(pData + sizeof(stHeadInfo), pBody, stHeadInfo.u16DataLen);
    int mark = os_networkmulticast_send(pData, nDataLen, &network_info);
    if (pData)
    {
        free(pData);
        pData = NULL;
    }
    if (mark == -1)
    {
        printf("send failed\n");
        return -1;
    }
    // else
    // {
    //     printf("send success\n");
    // }
    return 0;
}
/* Crc校验 */
unsigned short do_crc(unsigned char *ptr, int len)
{
    if (ptr == NULL)
    {
        return -1;
    }
    unsigned int i;
    unsigned short nCrc = 0xFFFF; // crc16位寄存器初始值
    while (len--)
    {
        nCrc ^= *ptr;
        for (i = 0; i < 8; ++i)
        {
            if (nCrc & 1)
            {
                nCrc = (nCrc >> 1);
                nCrc ^= 0xA001; // A001是8005按位颠倒后的结果
            }
            else
            {
                nCrc = (nCrc >> 1);
            }
        }
        ptr++;
    }
    return nCrc;
}
/*组播初始化,初始化后会自动接受数据并处理*/
int search_device_init(SearchdeviceMulticast_S *network_data)
{
    memset(&network_info, 0, sizeof(network_info));
    // strcpy(network_info.stsrc_ip_info.ip, "172.16.25.68");
    network_info.entype = MULTICAST;
    network_info.stsrc_ip_info.port = network_data->nmcast_port;
    strcpy(network_info.amcast_ip, network_data->amcast_ip);
    network_info.fnhandledata = deal_data;
    int ret = os_networkmulticast_init(&network_info);
    if (ret == -1)
    {
        printf("search_device_init fail\n");
        return -1;
    }
    // else
    // {
    //     printf("组播初始化成功\n");
    // }
    return 0;
}
(3)本地运行demo文件
#include <stdlib.h>
#include <stdio.h>
#include "cJSON.h"
#include "os_network_multicast.h"
#include "search_device.h"

int main()
{
    SearchdeviceMulticast_S network_data;
    memset(&(network_data), 0, sizeof(network_data));
    network_data.nmcast_port = 9999;
    strcpy(network_data.amcast_ip, "224.0.0.1");
    // data_send(pDataBody, strlen(pDataBody) + 1, 1000);
    int ret=search_device_init(&network_data);
    if (ret == -1)
    {
        printf("search_device_init fail\n");
        return -1;
    }
    // else
    // {
    //     printf("主函数初始化\n");
    // }
    while(1)
    {
        // data_send(pDataBody, strlen(pDataBody) + 1, 1000);
        // sleep(2);
    }
    return 0;
}

三、实验结果

1、服务器端运行结果

在这里插入图片描述
获得客户端的IP

2、客户器端运行结果

在这里插入图片描述
客户端建立socket连接,发送IP成功

四、总结

此次项目主要是内容是基于UDP的组播功能实现服务器端指令的组播发送,客户端收到指令并且根据指令信息返回服务器端所需要的内容。
代码实现了以下功能:
1、UDP组播;
2、表头的定义以及校验;
3、表头内容与传送信息的拼接;
4、回溯函数的定义以及使用方法;
5、关于cJson格式信息的封装;
同时在本地实现的基础上实现了代码的封装,共享库接口的调用以及本地demo的编码和本地运行。
此次文章内容主要目的是记载这半个月来的学习以及工作情况。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值