基于搜索协议实现工业设备升级

目录

1、背景引入

2、技术分析

 3、过程概述

4、服务器端流程

5、客户端流程

6、效果展示 

7、源码 

7.1 master(主控)

7.2 device(设备)

8、注意事项


1、背景引入

        在工业生产中,设备的升级和维护是非常重要的环节。随着技术的不断进步,工厂设备通常需要定期进行软件或固件的升级,以确保其性能、安全性和功能的持续改进。在这一过程中,文件传输是一个关键的步骤,因为升级文件的准确、高效地传输直接影响着设备升级的成功与否。

        在工业环境中,设备通常会连接到一个局域网中,并且可能分布在不同的位置。因此,为了实现设备升级,需要一种可靠的网络通信机制,能够让设备发现服务器上的升级文件,并将文件安全地传输到设备中进行应用。

        UDP(用户数据报协议)和TCP(传输控制协议)是两种常用的网络通信协议,它们分别适用于不同的场景。UDP适用于广播和简单的数据传输,而TCP适用于建立可靠的连接并进行大规模数据传输。

        因此,基于UDP和TCP模拟工厂设备升级过程中的文件传输,有助于理解和展示工业设备升级过程中的网络通信原理和流程。

2、技术分析

  • UDP:UDP(用户数据报协议)用于在局域网内广播消息,以便设备能够发现服务器并获取升级文件的信息。
  • TCP:TCP(传输控制协议)用于建立可靠的连接,并传输升级文件的数据。
  • 文件IO:通过文件I/O,程序能够从文件中读取数据,或者将数据写入文件。可以将程序中的数据持久化到文件中,以便在程序关闭后能够保存数据。
  • 并发服务:三种方法使得TCP具有并发处理的能力。分别是:多线程、多进程、IO多路复用(select、poll、epoll)。
  •  数据库:嵌入式常用的轻量级小型数据库(sqlite3),用来存储每次升级的设备信息以及每次升级的时间信息等,便于后期管理以及维护。

 3、过程概述

  1. 服务器端创建UDP套接字并设置允许广播;
  2. 服务器端发送广播消息,告知设备有升级文件可用;
  3. 客户端接收到广播消息后,提取服务器地址和端口信息,并使用TCP连接请求连接服务器;
  4. 服务器端并发接受客户端的连接请求,并开始传输文件;
  5. 客户端接收文件数据,并应用升级。
图3-1 过程简易描述

4、服务器端流程

  1. 创建UDP套接字并设置允许广播;
  2. 件可发送广播消息,告知设备有升级文用;
  3. 创建TCP套接字并绑定端口;
  4. 监听客户端连接请求;
  5. 接受客户端连接请求;
  6. 读取升级文件并发送给客户端;
  7. 关闭客户端连接(心跳包)。

5、客户端流程

  1. 创建UDP套接字并绑定端口;
  2. 接收广播消息;
  3. 创建TCP套接字并连接服务器;
  4. 接收文件数据并保存到本地文件;
  5. 关闭TCP连接。

6、效果展示 

图6-1 并发处理升级设备
图6-2 可视化数据库

7、源码 

7.1 master(主控)

#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <fcntl.h>
#include <pthread.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sqlite3.h>
#include <time.h>
#include <sys/stat.h>

int data_base(char *);

//线程函数处理与每一台设备进行数据发送
void *handler(void *arg)
{
	//获取文件描述符
	int acceptfd = (int)arg;
	printf("acceptfd = %d\n", acceptfd);
	int fd;
	//打开要发送的文件,(要是想通过命令行传参,你可以给线程传入结构体)
	if ((fd = open("new.txt", O_RDONLY)) < 0)
	{
		perror("open:");
		return NULL;
	}
	char buf[64] = {0};
	while (1)
	{
		int n = read(fd, buf, 64);
		if (n < 0)
		{
			perror("read err");
			return NULL;
		}
		if (n == 0)
			break;
		//发送数据给设备
		if (send(acceptfd, buf, n, 0) < 0)
		{
			perror("send err");
			return NULL;
		}
	}
	printf("线程处理结束\n");
	close(acceptfd);
	close(fd);
	pthread_exit(NULL);
}

int main(int argc, char *argv[])
{
	//UDP
	int sockfd;
	//搜索协议套接字
	if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
	{
		perror("socket err:");
		return -1;
	}
	int tv = 1;
	//发送方设置为发送广播
	if (setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &tv, sizeof(tv)) < 0)
	{
		perror("setsockopt err:");
		return -1;
	}
	struct sockaddr_in brandaddr;
	//填充结构体
	brandaddr.sin_family = AF_INET;
	brandaddr.sin_port = htons(8888);
	brandaddr.sin_addr.s_addr = inet_addr("192.168.0.255");
	int len_bra = sizeof(brandaddr);

	printf("我已经准备发送\n");
	int ret;
	//以广播的形式发送这条信息。
	ret = sendto(sockfd, "connect", 8, 0, (struct sockaddr *)&brandaddr, len_bra);
	if (ret < 0)
	{
		perror("sendto err:");
		return -1;
	}
	ssize_t num;
	printf("我已经发送完成\n");

	//TCP
	int decfd;
	//流式套接字
	if ((decfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
	{
		perror("socket err:");
		return -1;
	}
	int optval = 1;
	//设置地址重用
	setsockopt(decfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
	//绑定
	struct sockaddr_in ser_addr;
	//填充服务器的结构体
	ser_addr.sin_family = AF_INET;
	ser_addr.sin_port = htons(8888); //
	ser_addr.sin_addr.s_addr = inet_addr("0.0.0.0");
	int len_ser = sizeof(ser_addr);

	//绑定
	if (bind(decfd, (struct sockaddr *)&ser_addr, len_ser) < 0)
	{
		perror("bind err is:");
		return -1;
	}
	//启动监听,变为被动打开
	if (listen(decfd, 5) < 0)
	{
		perror("listen err:");
		return -1;
	}
	int dat = data_base(argv[1]);
	if(dat)//判断数据库的操作
	{
		printf("database is err");
		return -1;
	}
	struct sockaddr_in dev_addr;
	while (1)
	{
		char buf[4] = {};
		//接收确认信息
		num = recvfrom(sockfd, buf, 4, 0, (struct sockaddr *)&dev_addr, &len_bra);
		if (strncmp(buf, "yes", 3) == 0)
		{
			printf("wait accept\n");
			int acceptfd;
			//链接建立之后的信息交互文件描述符
			if ((acceptfd = accept(decfd, (struct sockaddr *)&dev_addr, &len_ser)) < 0)
			{
				perror("accept err:");
				return -1;
			}
			printf("链接成功 %s %s %d %p\n", inet_ntoa(dev_addr.sin_addr), buf, acceptfd, &acceptfd);
			pthread_t tid;
			if (pthread_create(&tid, NULL, handler, (void *)acceptfd))
			{
				perror("pthread create err:");
				break;
			}
		}
	}
	close(sockfd);
	close(decfd);
	return 0;
}


int data_base(char *file)
{
	sqlite3 *db = NULL; //防止野指针
	if (sqlite3_open("./my.db", &db) != 0)
	{
		//不能用perror, perror只能打印操作系统级别的错误  sqlite是独立的
		//fprintf代表 格式化输出到流内
		fprintf(stderr, "sqlite_open is err: %s\n", sqlite3_errmsg(db));
		//printf("sqlite3_open is err: %s\n",sqlite3_errmsg(db);
		return -1;
	}
	printf("create is success\n");
	char *errmsg = NULL;
	if (sqlite3_exec(db, "create table version(time char,file_name char,size int);",
					 NULL, NULL, &errmsg) != SQLITE_OK)
	{
		fprintf(stderr, "create table is err: %s\n", errmsg);
	}
	printf("table is success\n");

	time_t tim;
	struct tm *localtim = NULL;
	tim = time(NULL);
	localtim = localtime(&tim);
	int year = localtim->tm_year + 1900;
	int mon = localtim->tm_mon;
	int day = localtim->tm_mday;
	int hour = localtim->tm_hour;
	int min = localtim->tm_min;
	int sec = localtim->tm_sec;
	char timebuf[32]={0};
	sprintf(timebuf, "%d-%02d-%02d %02d:%02d:%02d", year, mon, day, hour, min, sec);
	struct stat buf;
	stat(file, &buf);
	char sql[128] = {0};
	sprintf(sql, "insert into version values(\"%s\",\"%s\",%ld);", timebuf, file, buf.st_size);
	if (sqlite3_exec(db, sql, NULL, NULL, &errmsg) != SQLITE_OK)
	{
		fprintf(stderr, "create table is err: %s\n", errmsg);
		return -1;
	}
	return 0;
}

7.2 device(设备)

 #include <stdio.h>
#include <string.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
 
int main(int argc, char *argv[])
{
	//当作搜索协议使用
	int sockfd;
	if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)//先打开数据包式套接字
	{
		perror("socket err:");
		return -1;
	}
    int optval = 1;
	//用于本地测试并发的。主要是设置地址重用
	setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
	
	struct sockaddr_in addr;
	//填充结构体
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = inet_addr("192.168.0.255");
	// addr.sin_addr.s_addr = INADDR_ANY;
	addr.sin_port = htons(8888);
	socklen_t addrlen = sizeof(addr);
	//绑定addr
	if (bind(sockfd, (struct sockaddr *)&addr, addrlen) < 0)
	{
		perror("bind err:");
		goto err;
	}
	ssize_t num;
	char buf[8] = {0};
	//接受对端ip、port等的结构体
	struct sockaddr_in clientaddr;
	//接受内容,之后比较接受到的内容
	num = recvfrom(sockfd, buf, 8, 0, (struct sockaddr *)&clientaddr, &addrlen);
    if(num <0)
    {
        perror("recvfrom err");
		goto err;
    }
	if (strncmp(buf, "connect", 7) != 0)
	{
		printf("quit\n");
		goto err;
	}
    
	sendto(sockfd, "yes", 4, 0, (struct sockaddr *)&clientaddr, addrlen);
	printf("回复完成\n");
	//搜索协议生命周期结束
    close(sockfd);

	int decfd;
	//新的流式套接字
	if ((decfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
	{
		perror("socket err:");
		return -1;
	}
    int optva = 1;
	//设置地址重用,为了测试并发
	setsockopt(decfd, SOL_SOCKET, SO_REUSEADDR, &optva, sizeof(optval));
	//定义结构体,用来接受服务器发送的文件信息等。保证可靠。
	struct sockaddr_in dev_addr;
	dev_addr.sin_family = AF_INET;
	dev_addr.sin_port = htons(8888); //
	dev_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
	int len = sizeof(dev_addr);
    //请求链接服务器
	if (connect(decfd, (struct sockaddr *)&dev_addr, len) < 0)
	{
		perror("connect err:");
		close(decfd);
		return -1;
	}
	printf("connet success\n");
	int fd;
	//打开文件,用于后续接收数据
	if ((fd = open(argv[1], O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0)
	{
		perror("open:");
		close(decfd);
		return -1;
	}
	char buff[64] = {0};
	while (1)
	{
		int ret;
		ret = recv(decfd, buff, 64, 0);
		if (ret < 0)
		{
			perror("recv err:");
			return -1;
		}
		else if (ret > 0)
		{
			//写入文件
			write(fd, buff, ret);
			memset(buff, 0, 64);
		}
		else
		{
			break;
		}
	}
	printf("Upgrade successful\n");
    close(decfd);
	close(fd);
	return 0;
err:
	close(sockfd);
	return 0;
}

8、注意事项

        使用多线程的时候,因为线程的特性,不得不使用线程栈保存文件描述符来保证并发的完成。但是线程传参的时候,传入地址导致传输的地址是同一个。这是因为变量的定义在局部中,结束之后销毁,再次创建变量,编译器可能会再次为变量分配这块内存地址。导致问题一直无法解决。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/213077.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Gossip 协议

Gossip 协议 背景 在分布式系统中&#xff0c;不同的节点进行数据/信息共享是一个基本的需求。 一种比较简单粗暴的方法就是 集中式发散消息&#xff0c;简单来说就是一个主节点同时共享最新信息给其他所有节点&#xff0c;比较适合中心化系统。这种方法的缺陷也很明显&…

GOLAND搭建GIN框架以及基础框架搭建

创建GO环境文件夹 终端输入安装GIN go get -u github.com/gin-gonic/gin如果遇到超时错误 package golang.org/x/net/html: unrecognized import path "golang.org/x/net/html": https fetch: Get "https://golang.org/x/net/html?go-get1": dial tcp …

理解宏任务和微任务:JavaScript 异步编程的必备知识(上)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

借助SD地图的BEV静态感知

动机与出发点 纯视觉、视觉Lidar的感知系统在复杂城市道路场景下并不能如预期那般表现稳定&#xff0c;其中遮挡就是一个巨大挑战。现在的BEV静态感知方案多采用多趟重建的方式获取&#xff0c;这就导致无论前方是否有车辆、建筑物、绿化带等&#xff0c;只要能投影到BEV空间的…

1688买家API接口跨境卖家需要的API接口

1688作为深耕产业带多年的数字供应链平台&#xff0c;近两年不仅在年轻消费群体中热度飙升&#xff0c;在跨境侧也有不俗表现。 11月19日&#xff0c;1688总裁余涌在1688跨境寻源通计划发布会上透露&#xff0c;1688平台拥有100万的源头厂商&#xff0c;每年服务6500万的B类买…

【JavaEE】多线程(3) -- 线程等待 wait 和 notify

目录 1. wait()⽅法 2. notify()⽅法 3. notifyAll()⽅法 4. wait 和 sleep 的对⽐&#xff08;⾯试题&#xff09; 由于线程之间是抢占式执⾏的, 因此线程之间执⾏的先后顺序难以预知. 但是实际开发中有时候我们希望合理的协调多个线程之间的执⾏先后顺序. 完成这个协调⼯…

树莓派4B机器狗的串口通信驱动库pyserial实践

pyserial是一个串口通信驱动库&#xff0c;常用的Windows、Linux、MacOS等都可以安装&#xff0c;这里我使用的是树莓派4B来测试&#xff0c;这块板子还是很强大的&#xff0c;我们可以通过pyserial这个库来操作基于这块板子上的机器狗之类的设备。 1、四足机器狗 本人的是四…

初识Java 18-6 泛型

目录 潜在类型机制 支持潜在类型机制的语言 Python的潜在类型机制 C的潜在类型机制 Java中的直接潜在类型机制 潜在类型机制的替代方案 反射 将方法应用于序列中的每个元素 Java 8的潜在类型机制&#xff08;间接实现&#xff09; 潜在类型机制的使用例&#xff08;S…

条款2:不要滥用宏

文章目录 优先选择编译器而不是预编译器两种特殊情况使用宏替代函数调用总结 优先选择编译器而不是预编译器 假设我们预定义了一个宏#define ASPECT_RATIO 1.653&#xff0c;当我们的程序在这个地方出现错误的时候。可能会出现以下的问题&#xff1a; 符号名称ASPECT_RATIO可能…

MQTT客户端、代理(broker)和连接建立

在前篇文章&#xff08;http://t.csdnimg.cn/IamPz&#xff09;中&#xff0c;介绍了发布/订阅架构和MQTT如何据此交换信息&#xff0c;其中的关键概念是&#xff1a; 发布/订阅架构触耦了负责发布信息的客户端&#xff08;发布者&#xff09;和负责接收信息的客户端&#xff…

C语言-联合和枚举

------------------------------------ --------------- ------ 最慢的步伐不是跬步&#xff0c;而是徘徊&#xff1b; 最快的脚步不是冲刺&#xff0c;而是坚持。 今天来到我们的联合和枚举类型的讲解&#xff1a; 目录 联合体类型 联合体类型的声明 联合体类型的特点 …

Wireshark抓包分析RTMP协议时,出现Unknown问题

进行rtmp推流时&#xff0c;使用wireshark抓包&#xff0c;发现部分包显示Unknown 解决方法&#xff1a; 编辑 -> 首选项 -> Protocols -> RTMPT&#xff0c;这里Maximum packet size默认是32768 将该值调大&#xff0c;比如调成1048576&#xff0c;即可解决该问题。…

ChatGPT 的 18 种玩法,你还不会用吗?

你确定&#xff0c;你会使用 ChatGPT 了吗&#xff1f; 今天给大家整理了 18 种 ChatGPT 的用法&#xff0c;看看有哪些方法是你能得上的。 用之前我们可以打开R5Ai平台&#xff0c;可以免费使用目前所有的大模型 地址&#xff1a;R5Ai.com 语法更正 用途&#xff1a;文章…

改进LiteOS中物理内存分配算法(详细实验步骤+相关源码解读)

一、实验要求 优化TLSF算法&#xff0c;将Best-fit策略优化为Good-fit策略&#xff0c;进一步降低时间复杂度至O(1)。 优化思路&#xff1a; 1.初始化时预先为每个索引中的内存块挂上若干空闲块&#xff0c;在实际分配时避免分割&#xff08;split&#xff09;操作&#xff…

[原创]C++98升级到C++20的复习旅途-从汇编及逆向角度去分析“constexpr“关键字

[简介] 常用网名: 猪头三 出生日期: 1981.XX.XXQQ: 643439947 个人网站: 80x86汇编小站 https://www.x86asm.org 编程生涯: 2001年~至今[共22年] 职业生涯: 20年 开发语言: C/C、80x86ASM、PHP、Perl、Objective-C、Object Pascal、C#、Python 开发工具: Visual Studio、Delphi…

AtCoder Beginner Contest 331 题解 A-E

目录 A - TomorrowB - Buy One Carton of MilkC - Sum of Numbers Greater Than MeD - Tile PatternE - Set Meal A - Tomorrow 原题链接 题目描述 已知一年有M个月D天&#xff0c;求出第y年m月d天的后一天是哪一天。 思路&#xff1a;分类讨论 分别讨论m和d的是否是最后一个月…

基于SpringBoot的旅游信息网【源码好优多】

简介 旅游信息网是一款介绍旅游相关内容的网站&#xff0c;分为前台和后台部分&#xff0c;其中前台用户注册以后可以浏览景点、景点详情、预订景点、酒店、车票、保险、以及浏览旅游攻略、个人信息修改、在线留言等&#xff0c;管理员在后台对景点、攻略、订单信息、酒店信息、…

oj赛氪练习题

数组调整 import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner scanner new Scanner(System.in);int n scanner.nextInt();int k scanner.nextInt();int[] arr new int[n];for (int i 0; i < n; i) {arr[i] scanner.nextIn…

java源码-类与对象

1、面向对象与面向过程 在了解类和对象之前我们先了解一下什么是面向过程和面向对象。 1&#xff09;面向过程编程&#xff1a; C语言就是面向过程编程的&#xff0c;关注的是过程&#xff0c;分析出求解问题的步骤&#xff0c;通过函数调用逐步解决问题。 2&#xff09;面向对…

Redis 发布订阅机制深入探索

Redis 的发布订阅&#xff08;pub/sub&#xff09;机制是一种消息传递模式&#xff0c;允许消息的发送者&#xff08;发布者&#xff09;和消息的接收者&#xff08;订阅者&#xff09;通过一个中介层&#xff08;频道&#xff09;进行通信&#xff0c;而无需彼此直接交互。以下…