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

目录

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[])
{//UDPint 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");//TCPint 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);//绑定addrif (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.mzph.cn/news/193137.shtml

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

相关文章

一起学docker系列之十六使用Docker Compose简化容器编排

目录 1 前言2 Docker Compose是什么&#xff1f;3 Docker Compose安装步骤3.1 **下载Compose**3.2 **设置权限**3.3 **创建符号链接&#xff08;可选但建议以便使用&#xff09;** 4 Docker Compose的核心概念4.1 **YAML文件&#xff08;docker-compose.yml&#xff09;**4.2 *…

SpringBootCache缓存——j2cache

文章目录 缓存供应商变更&#xff1a;j2cache 缓存供应商变更&#xff1a;j2cache <!-- https://mvnrepository.com/artifact/net.oschina.j2cache/j2cache-core --><dependency><groupId>net.oschina.j2cache</groupId><artifactId>j2cache-cor…

【数据挖掘】国科大刘莹老师数据挖掘课程作业 —— 第三次作业

Written Part 1. 基于表 1 1 1 回答下列问题&#xff08;min_sup40%, min_conf75%&#xff09;&#xff1a; Transaction IDItems Bought0001{a, d, e}0024{a, b, c, e}0012{a, b, d, e}0031{a, c, d, e}0015{b, c, e}0022{b, d, e}0029{c, d}0040{a, b, c}0033{a, d, e}0038…

[原创]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…

机器视觉最全面试题总结

文章目录 1. 为什么需要做特征归一化、标准化&#xff1f;2. 常用常用的归一化和标准化的方法有哪些&#xff1f;3. 介绍一下空洞卷积的原理和作用4. 怎么判断模型是否过拟合&#xff0c;有哪些防止过拟合的策略&#xff1f;5. 除了SGD和Adam之外&#xff0c;你还知道哪些优化算…

uniapp 使用安卓模拟器运行

uniapp 启动方式有很多种,这里介绍使用模拟器启动uniapp 要使用模拟器启动uniapp肯定少不了安装模拟器(废话) 这里选着浏览器推荐的第一个模拟器mumu模拟器 下载好了mumu安装包后就是安装了,这个过于小白,就不介绍了 2. 查看模拟器的adb端口号, mumu的adb查看端口号与众不同…

6-48.日期类的设计与实现

使用重载运算符&#xff08;&#xff0c;&#xff0c;<<等&#xff09;实现日期类的操作。功能包括&#xff1a; 1&#xff09;设置日期&#xff0c;如果日期设置不符合实际&#xff0c;则设置为默认日期&#xff08;1900年1月1日&#xff09; 2&#xff09;在日期对象中…

Java 表达式引擎

企业的需求往往是多样化且复杂的&#xff0c;对接不同企业时会有不同的定制化的业务模型和流程。我们在业务系统中使用表达式引擎&#xff0c;集中配置管理业务规则&#xff0c;并实现实时决策和计算&#xff0c;可以提高系统的灵活性和响应能力。 引入规则引擎似乎就能解决这个…

数据结构:堆的实现思路

我们之前写过堆的实现代码&#xff1a;数据结构&#xff1a;堆的实现-CSDN博客 这篇文章我们了解一下堆到底是如何实现的 1.堆向下调整算法 现在我们给出一个数组&#xff0c;逻辑上看做一颗完全二叉树。我们通过从根节点开始的向下调整算法可以把它调整成一个小堆 向下调…

栈的链式存储(详解)

栈的链式存储 栈的链式存储是通过链表来实现的&#xff0c;每个节点包含一个元素和一个指向下一个节点的指针。链式存储的栈不需要提前分配内存空间&#xff0c;可以动态地增加或减少元素。 在链式存储中&#xff0c;栈顶元素通常是链表的头节点&#xff0c;栈底元素是链表的…

高光谱遥感影像分类项目开源

热烈欢迎大家在git上star&#xff01;&#xff01;&#xff01;冲鸭&#xff01;&#xff01;&#xff01; 我研究生期间主要是做高光谱遥感影像分类的&#xff0c;毕业论文也是基于深度学习的高光谱遥感影像分类课题&#xff0c;转眼间已经毕业四年了&#xff0c;如今把这块材…

每天一点python——day84

#每天一点Python——84 #异常处理机制try—except—else #异常处理机制try—except—else如果try块中没有抛出异常&#xff0c;则执行else块&#xff0c;如果try中抛出异常&#xff0c;则执行except块#示例&#xff1a; try:a int(input(请输入第一个整数&#xff1a;))b in…

SpringBootAdmin监控原理Actuator,自定义指标

SpringBootAdmin监控原理Actuator&#xff0c;自定义指标 文章目录 SpringBootAdmin监控原理Actuator&#xff0c;自定义指标actuator自定义info端点信息自定义Health端点信息自定义metrics端点信息端点的自定义 actuator JMX方式就是在cmd控制台输入jconsole&#xff0c;会弹出…

递归实现组合型枚举

递归实现组合型枚举 #include<iostream> #include<vector>int n, m; std::vector<int>res; bool st[30];void Print() {for(int i0;i<res.size();i){printf("%d ",res[i]);}puts(""); }void dfs(int num) {if (res.size() m){Print(…

使用docker切换任意版本cuda使用GPU

1.现存问题 在主机上运行很多下载来的机器学习代码时&#xff0c;这些大都运行在不同版本的tensorflow-gpu或者pytorch等的包下的。但是&#xff0c;运行代码的过程中&#xff0c;存在匹配的问题&#xff1a; 1.不同的版本的tensorflow-gpu或pytorch对cuda版本的适配也不一样…

Upsert 及冲突(GORM)

GORM支持了数据库的upsert操作 upsert操作对于插入一条数据而言的&#xff0c;如果插入数据之前&#xff0c;没有这条数据&#xff0c;则会插入该条数据&#xff1b;如果插入数据之前就存在这条数据&#xff08;索引值&#xff09;&#xff0c;就更新这条记录。 创建结构体 …

html电子签名

html电子签名 html5实现手写签名板&#xff0c;点击保存即可生成base64格式的图片 使用H5自带的canvas&#xff0c;无需引入js无需引入别的js 效果图 html代码 <!DOCTYPE html> <html> <head><meta http-equiv"Content-Type" content"…

VisionPro---PatMaxTool工具使用

CogPMAlignTool PatMax是一种图案位置搜索技术&#xff08;识别定位&#xff09;&#xff0c;PatMax图案不依赖于像素格栅&#xff0c;是基于边缘特征的模板匹配而不是基于像素的模板匹配&#xff0c;支持图像中特征的旋转与缩放&#xff0c;边缘特征表示图像中不同区域间界限…

zookeeper+kafka+ELK+filebeat集群

目录 一、zookeeper概述&#xff1a; 1、zookeeper工作机制&#xff1a; 2、zookeeper主要作用&#xff1a; 3、zookeeper特性&#xff1a; 4、zookeeper的应用场景&#xff1a; 5、领导者和追随者&#xff1a;zookeeper的选举机制 二、zookeeper安装部署&#xff1a; 三…

基于SSH的员工管理系统(一)——包结构

基于SSH的员工管理系统&#xff08;一&#xff09;——包结构 包结构 1、整体包结构 2、action包 3、domain实体包 4、service层 5、dao层 6、util工具包 7、页面层