基于UDP组播的无线搜索功能
- 基于UDP组播的无线搜索功能
- 一、主要工作内容
- 1、接口调用
- 2、函数封装
- 3、表头设置与CRC16校验
- 4、cJSON格式
- 5、回调函数的使用
- 二、代码复现
- 1、服务器端:
- (1).h文件(头文件声明以及其他文件引用)
- (2).c文件(代码功能实现文件)
- (3)本地运行demo文件
- 2、客户端:
- (1).h文件(头文件声明以及其他文件引用)
- (2).c文件(代码功能实现文件)
- (3)本地运行demo文件
- 三、实验结果
- 1、服务器端运行结果
- 2、客户器端运行结果
- 四、总结
基于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的编码和本地运行。
此次文章内容主要目的是记载这半个月来的学习以及工作情况。