上位机与下位机交互--让socket不死
需求说明:
下位机是plc,西门子1200
下位机只能做服务器端,监听一个端口,不能主动给客户端发送消息(原计划是上位机也是监听一个端口,供下位机来访问,上传数据,结果现实很骨感)
上位机(pc)充当客户端,可以主动连接下位机交换信息
具体需求:
1,上位机给下位机下达工作数据
(比如下位机是生产纸张,上位机需要发给下位机纸张的尺寸,数量等数据)
2,下位机会在某个特定的时间“发送”数据,供上位机“接收”
(注意 “发送” 是加上引号的,因为服务器(下位机)是不能主动给客户端(上位机,pc)发送数据的,这是此文需要解决的问题)
需求1毫无难度,直接在用户在上位机点击下单,上位机即可创建一个socket将相关数据下传即可
重点来看看需求2;
可以想象为服务器与客户端(浏览器)
不妨以秒杀为例,
客户端(浏览器)提交秒杀请求(相当于这里的上位机给下位机下单),由于秒杀并发很大,不能实时返回数据(一般是异步下单,这里不深究)
客户端如果需要知道秒杀的结果,会在浏览器(客户端)通过轮询查询订单状态(如果有,则表示下单成功)
在需求2中,“客户端” 就指的是上位机创建的“socket”,socket是唯一和下位机沟通的途径,而且下位机不能创建socket,主动发数据给上位机,那么上位机创建得到socket,能否让他成为一个永久的客户端,上位机定时的取这个socket里的数据呢??
答案是肯定的:
思路:
将socket封装成一个单例,需求1和需求2中的客户端(上位机的socket) 使用同一个socket
问题又来了:
问题1:
如果上位机正在给下位机下传工作信息,下位机恰好也在此时完成了一些工作,将工作状态通过socket(由上位机创建的)发送数据,那么会有可能出现冲突,有点乱套
解决:
尝试加锁,让这个socket同一时间只能用在 过程1 ( 上位机给下位机下传工作数据)
或者 过程2(上位机周期读取socket中的数据(也就是下位机返回的工作完成情况这些数据)),
通过加锁:让这一个socket在某一个时刻,只能用于过程1或者过程2
问题2:
如果socket挂掉了?该如何获取与下位机的交互数据的“要道” 呢??
解决:
封装后的socket单例类,在使用socket读、写时,加上try catch 捕捉 SocketException,出现此异常,socket连接就是断开了,重新是用封装好的单例类的getInstance方法获取 socket对象,重建连接
此单例实现代码如下:
其中需要注意的是:
1,Semphore这一锁(信号量),参数指定此锁允许最多同时被多少个线程使用某一个资源(或者是资源的个数)
2,isDead 标记,用来标记当前单例对象是否断开(socket),如果是断开的,在getInstance是对其做了判断
如果没有单例对象,或者单例对象标记为“死亡”,那么就创建新的单例对象
第2点保证了,同一时刻,只能有一个线程使用socket
第2点保证了,socket的持久通信,及时下位机重启或者其他原因导致socket连接中断,也能重新创建连接
/*** socket单例 用于维系上位机与下位机的通信* 需要保证是同一个socket,并且需要注意锁的问题*/
public class SocketSingleton extends Socket{//只允同时一个线程访问此单例public static Semaphore singletonLock=new Semaphore(1);private boolean isLocked;//标记单例是否被锁住,有Semaphore,这里就可以省略了private volatile boolean isDead;//标记单例是否死亡private static SocketSingleton ourInstance=null;private SocketSingleton() throws IOException {super(WINDER_IP,WINDER_PORT);isDead=false;ourInstance=this;isLocked=false;}/*** 获取锁*/public synchronized void getLock(){this.isLocked=true;}/*** 释放锁*/public synchronized void releaseLock(){this.isLocked=false;}/**** @return 检测锁*/public synchronized boolean checkIfLock(){return this.isLocked;}/*** kill 连接异常的socket*/public static void killSingleton(){ourInstance.isDead=true;}/**** @return* @throws IOException* 如果当前单例的socket是dead,就创建新的socket*/public synchronized static SocketSingleton getInstance() throws IOException {return (Objects.isNull(ourInstance)||ourInstance.isDead) ? new SocketSingleton():ourInstance;}
}
使用:
以下位机状态“返回“”为例”
1 先获取锁(获取资源)
2 业务操作
3 再释放锁(释放资源)
@Scheduled(cron = "0/10 * * * * ?")@Transactional(rollbackFor = Exception.class)public void myTestWork() throws IOException, InterruptedException {TubeServiceWithPC2Winder tubeServiceWithPC2Winder = new TubeServiceWithPC2Winder();log.info("定时器启动!{}", new Timestamp(System.currentTimeMillis()));SocketSingleton socket=SocketSingleton.getInstance();try{
//理解为获取锁,或者是获取资源,如果没有,这里会阻塞吗,直到其他线程将资源释放,这里才会往下进行SocketSingleton.singletonLock.acquire();log.info("状态交互获取到锁 singletonLock:{}",new Timestamp(System.currentTimeMillis()));socket.setSoTimeout( SOCKET_TIME_READ_timeout);log.info("状态交互 hashcode:{}",socket.hashCode());InputStream inputStream = socket.getInputStream();OutputStream outputStream = socket.getOutputStream();byte[] dataFromServer = new byte[WORK_FINISHED_BYTES_LENGTH];inputStream.read(dataFromServer);ByteQueue byteQueue = new ByteQueue(dataFromServer);log.info("来自服务端的数据:{}", byteQueue.toString());//数据处理的业务逻辑,此处忽略Tube tubeInfo = resolveData(dataFromServer);}catch(SocketTimeoutException e){log.info("状态交互 read 超时");}catch (SocketException e){//处理连接中断,重新创建单例socketSocketSingleton.killSingleton();SocketSingleton.getInstance();} finally{
// 这里理解为释放锁,或者是释放资源(一共只有1个资源),如果不释放,其他线程将不能获得此资源,比如下一个周期到了,定时器将停在 SocketSingleton.singletonLock.acquire() 无法向下进行SocketSingleton.singletonLock.release();log.info("状态交互 释放成功 singletonLock:{}",new Timestamp(System.currentTimeMillis()));}}
定时任务改进说明:
最初是在定时器里面没有使用单例,每个周期到了,将new Socket(),发现随着执行的任务次数增加,会出现问题
内存资源的问题:
比如我创建2000个socket:
@Testpublic void testCreateMoreSocket() throws IOException, InterruptedException {for(int i=0;i<2000;i++){Socket socket=new Socket(WINDER_IP,WINDER_PORT);log.info("socket:{},hashCode:{}",new Object[]{i,socket.hashCode()});
// Thread.sleep(1*1000);}}
创建之前:
创建之后:变化感觉不大,但是idea电脑卡死将近5秒
定时任务是会一直执行的,如果是4000次又会是怎样的呢?
当创建完1563个的时候已经抛异常了,有一瞬间cpu使用率100%
改下代码,创建socket使用完就将其close,测试结果竟然让调试助手还是无响应了,
@Testpublic void testCreateMoreSocket() throws IOException, InterruptedException {for(int i=0;i<4000;i++){Socket socket=new Socket(WINDER_IP,WINDER_PORT);log.info("socket:{},hashCode:{}",new Object[]{i,socket.hashCode()});
// Thread.sleep(1*1000);socket.close();}}
可能是创建台频繁,那就延时1秒试试,然后喝 1*n 杯咖啡(4000个创建完需要一个小时,暂时就不测试了,毕竟咖啡不够了)
上面的测试过程是为了说明创建socket是需要耗费资源,那么就得减少socket的创建,完成任务就好
上位机与下位机交互--让socket不死相关推荐
- 基于STM32C8T6、ESP8266-01S、JavaWeb、JSP、Html、JavaScript、Android、服务器和客户端设计、上位机和下位机设计等技术融合的物联网智能监控系统设计与实现
系列文章目录 第一章ESP8266的java软件仿真测试 第二章ESP8266硬件与软件测试 第三章ESP8266客户端与Java后台服务器联调 第四章ESP8266客户端与JavaWeb服务器联调 ...
- 上下位机通讯协议_上位机与下位机的区别通讯
上位机是指可以直接发出操控命令的计算机,一般是PC/host computer/master computer/upper computer,屏幕上显示各种信号变化(液压,水位,温度等).下位机是直接 ...
- 上位机和下位机的概念,理解如何实现PC从PLC中读取数据?
市面上的PLC有上百种, 西门子的, 三菱的, 欧姆龙的等等. 上位机和下位机的理解: 上位机是指可以直接发出操控命令的计算机,一般是PC/host computer/master computer/ ...
- 打开单片机世界的大门——上位机控制下位机实例详解
上位机控制下位机实例详解 一.基本概念 上位机与下位机 串口 数据表达 二.下位机程序 三.上位机程序 四.总结 一.基本概念 在开始讲解前,先来看几个基本概念,如果是有基础的大佬,请直接跳到下一节. ...
- 超详细Klipper 上位机与下位机配置
(适用多数Mega2560芯片打印机主板,本文使用香橙派ZERO2作为上位机) 上位机:ZERO2 下位机:打印机主板 下载镜像系统 首先,去Armbian官网下载Buster系统镜像:Armbian ...
- 计算机基础-工控机、上位机、下位机、stm32、单片机
工控机 定义:(Industrial Personal Computer,IPC)即工业控制计算机,主要用于工业生产上. 性能:采用全钢机箱,抗震性能好,抗电磁干扰,抗冲击. 结构:包括CPU.io外 ...
- 什么是上位机、下位机
上位机 上位机是指可以直接发出操控命令的计算机, 一般是PC/host computer/master computer/upper computer, 屏幕上显示各种信号变化(液压,水位,温度等). ...
- java实现上位机与下位机串口通信
串口通信是在工程应用中很常见.在上位机与下位机通讯过程中常通过有线的串口进行通信,在低速传输模式下串口通信得到广泛使用.在说个之前先来简单解释一下上位机与下位机的概念. 上位机与下位机 通常上位机指的 ...
- QT5实现串口收发数据(上位机与下位机通信)
最近帮老师做一个应用程序,通过上位机与下位机进行串口通信,最后实现实时绘图,通过几天努力,成功实现蓝牙串口通信. 参考博客1 注意:代码中一些与串口无关代码,可以忽略掉 一.QT5串口基础知识 1. ...
最新文章
- 这就是我向您推荐使用Thunderbird邮件客户端的理由
- docker安装mysql及相关配置、运行细节和常见报错解决方案
- Angular Component template函数执行上下文的对象
- C语言与Java的深情对话:儿子,还得多练几年啊!老子还是老子
- oracle中的序列 cache,oracle row cache lock 之sequence
- 2012、12、17
- TSC工业型条码打印机的价格的影响因素有哪些呢?
- 为Node.js编写组件的几种方式
- php系列框架的加速器Opcache
- Object.defineProperty()属性设置介绍
- DevExpress chartControl 数据绑定
- 机器视觉可以应用到哪些场景中?
- jquery.blockui示例
- alpha测试和betal测试
- 学习Linux你必须知道的那些事儿
- Pytorch——pytorch的基本数据类型
- 跨时钟域问题(二)(单bit信号跨时钟域 1. 电平同步器 2. 边沿同步器 3. 脉冲检测器)
- 电脑端手机模拟器软件
- python信息检索系统_GitHub - Uyouii/SearchingSystem: python实现的基于倒排索引和向量空间模型实现的信息检索系统...
- 海外邮件发送指南(一)
热门文章
- mysql中查找出生日期_如何在MySQL中根据出生日期记录显示日期名称?
- 大智慧专业财务数据服务器文件,大智慧专业财务数据及代码内容对照表-2
- 初识CMake,如何编写一个CMake工程(上)
- sgu482 Impudent Thief (动态规划)
- 数据研究:淘宝流量与转化率隐藏的那些秘密
- 居于mtk芯片安卓车机系统具体流程
- cad调了比例因子没反应_天正CAD标注比例大小调整方法
- cad注释比例和打印比例不一样_CAD注释性比例该如何设置?看完你就懂了
- python中取对数怎么表示_python中取对数
- 公告丨Dex.top(大力士)上线Opengram (GRAM)