手把手教你学习IEC104协议和编程实现 (可删除,见文章 一 起章)

AI摘要

本文介绍了如何使用C#编程实现IEC104协议的网络部分,包括clientsocket类的设计,如connect、close、receivestr、sendstr等关键函数。内容涉及网络接口参数、异常处理和日志记录,为后续深入学习IEC104协议打下基础。

本章可以不看了,已经对格式内容进行了修正和优化.见文章手把手教你学习IEC104协议和编程实现 一 起章-优快云博客

从今天开始,我准备将我编程实现IEC104协议软件的思路给各位分享下,里面会穿插IEC104协议的具体分析,对于学习IEC104协议和编程实现都有一定的意义,也算科普一下吧.

本章仅对网络部分做一个讲解.

众所周知,我们的IEC104是国家电网公司经常使用的一个协议,在电力系统工作的人,通讯这一块有接触的话,都要对这个协议有一个深入的理解.

IEC104协议有三个版本,分别是1997版,2002版,2009版,其中我们使用最多的可能是200版.

各版本在规约处理流程上没有什么变化,不同之处在于:

(1)2002版在1997版的基础上,扩展了遥测、遥信、遥控等信息体基体址。

(2)2009版在2002版的基础上,增加了协议的传输序列和互操作性的改进,以及对冗余连接处理方面的新功能。

具体的内容我们后面会讲到.

我们首先要知道的是IEC104协议传输的介质是网络.所以她是通过socket方式进行传输的,这个就涉及到网络ip和网络端口,这2个参数才能构成一个网络接口参数,缺一不可.

所以今天这一章我们先学习实现网络部分,仅和编程有关系,具体IE104的具体内容见下一章,毕竟我们要循序渐进的,嘿嘿.

clientsocket类

我们先建立一个类,名称为clientsocket,代表客户端的模式下的socket类,毕竟,万一我们以后使用server模式呢.

这个类主要的函数为connect,close,receivestr,sendstr,分别用于链接,断开(关闭),接收数据,发送数据函数.

函数1:connect

/// <summary>
        /// connect  网络连接
        /// </summary>
        /// <param name="ipadd"></param>
        /// <param name="port"></param>
        #region 连接ip,端口号
        public bool Connect(string ipadd, int port)
        {
            byte[] data = new byte[4096];
            newclient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);                   
            IPEndPoint ie = new IPEndPoint(IPAddress.Parse(ipadd), port);
            try
            {
                newclient.Connect(ie);                
                Connected = true;
                
                ThreadStart myThreaddelegate = new ThreadStart(Receivestr);
                myThread = new Thread(myThreaddelegate);
                myThread.Start();                
            }
            catch (Exception ex)
            {
                string str = "对象名称: " + ex.Source + "\n" +
               "函数: " + ex.TargetSite + "\n" +
               "故障点: " + ex.StackTrace.Substring(ex.StackTrace.IndexOf("\\")) + "\n" +
               "错误提示 : " + ex.Message + "\r\n";
                writeLogFile(str);
                Connected = false;
                if(myThread!=null)
                    myThread.Join(0);
                
            }
            
            return Connected;

        }
        #endregion

我们需要先定义变量:

        public Socket newclient;
        public bool Connected;
        public Thread myThread;

我了能够正常使用,我们还需要先包含相关的工作空间

using System.Net;
using System.Net.Sockets;
using System.Threading;

对于catch()部分,这样我们可以在发生异常的时候,将异常文件的函数和行数等信息输出出来,更加精确的分析故障发生的原因.如果不是很理解,这一部分可以删除掉,直接catch()也OK.

这个函数的功能就是使用线程的方式对定义好的网络参数连接成功,开始准备发送和接收数据.

函数2: close

        /// <summary>
        /// close  关闭网络
        /// </summary>
        #region 关闭
        public void close()
        {
            if (Connected)
            {
                newclient.Close();
            }
        }
        #endregion

用于程序结束的时候,需要先关闭网络.

函数3: receivestr

#endregion

        /// <summary>
        /// receivestr   接收字符串
        /// </summary>
        #region 接收数据
        public void Receivestr()
        {
            receive_str = "";
            stringdata = "";
            int recv=0;
            //修正当手动断开连接时,cpu占用过高的问题,原来是while(true)
            while (Connected) 
            {
                Console.WriteLine("receivestr is runing \r\n");
                if (!Connected) return;
                
                try
                {
                    recv = newclient.Receive(buffer);
                    
                    for (int i = 0; i < recv; i++)
                    {
                        data.Add(buffer[i]);
                        
                        receive_str += data[i].ToString("X2");
                        
                    }
                    
                    stringdata = Encoding.UTF8.GetString(buffer, 0, recv);
                }
                catch 
                {
                    Connected = false;  
                     //return;
                }
            }
        }
        #endregion

这个函数需要先定义几个变量

        public string send_str,receive_str;//发送字符串,接收字符串
        public string stringdata="";
        public byte[] buffer = new byte[4096];  //数据存放的缓冲区
        public List<byte> data = new List<byte>();//链表,用于循环组合报文.

函数4:sendstr

/// <summary>
        /// sendstr  发送字符串
        /// </summary>
        /// <param name="str"></param>
        /// <param name="hexflag"></param>
        #region 发送数据
        public void sendstr(string str,int hexflag)
        {
            if (!Connected) return;
            //str = str.Replace(" ", "");
            int m_length = str.Length/2;
            
            byte[] data = new byte[m_length];
            if (hexflag == 0)
            {
                data = Encoding.UTF8.GetBytes(str);
            }
            else
            {
                //在这里我们引入了一个新的函数,作用是将字符串转换成16进制的数组,函数实现在后面
                int len = strtohexarray(str, data);
            }
            //data[4] = Convert.ToByte(sendindex);
            try
            {
                sendlen = newclient.Send(data);
                
            }
            catch 
            {
                //Console.Write(ex.Message);
                Connected = false;
                return;
            }
            
        }
        #endregion

附加函数strtohexarray

/// <summary>
        /// strtohexarray  将字符串转换成byte[]
        /// </summary>
        /// <param name="str"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        #region
        public int strtohexarray(string str,byte[] b)
        {
            int i = 0;
            try
            {
                str = str.Replace(" ", "");
                str = str.Replace("-", "");
                str = str.Replace(">", "");
                if (str.Length % 2 != 0)
                    str = str.Substring(0, str.Length - 1);
                for (i = 0; i < str.Length / 2; i++)
                {
                    b[i] = System.Convert.ToByte(str.Substring(i * 2, 2), 16);
                }
                return i;
            }
            catch
            {
                return i;
            }
        }
        #endregion

这个函数的作用就是将我们的组织好的报文发送出去,具体我们使用的,将报文组织好,直接调用这个函数就OK.其中,可以选择是按照16进制的模式,还是使用字符模式,这2中模式下的发送内容处理方式是不一样.有兴趣的朋友可以深入研究下.

另外为 更好的调试或者运行log,我们设计了辅助函数writelogfile,将收到的报文以数组或者字符串的格式保存到文件中.

函数5:writelog

函数用途将报文写入到log文件,由于我们在处理的过程可能会涉及到不同的模式,所以我们使用2个函数writeLogFile(string str),writeLogFile(byte[] bb,int len)来实现.具体如下:

#region 写log文件
        //将字符串类型的数据存储到log文件中
        public int writeLogFile(string str)
        {            
            if(str=="") return 1;
            string filePath = Form1.form.datadir + @"run.log";//这里文件的路径是当前执行程序下的run.log文件
            //当文件存在,并且大小大于20M的时候,删除文件,这样是为了保证存储log的速度
            if (File.Exists(filePath))
            {
                FileInfo fileInfo = new FileInfo(filePath);
                if (fileInfo.Length > 2000000)
                    File.Delete(filePath);
            }

            try
            {
                FileStream aFile = new FileStream(filePath, FileMode.OpenOrCreate | FileMode.Append);
                StreamWriter sw = new StreamWriter(aFile);
                DateTime tt = DateTime.Now;
                str = "[" + tt.ToString() + "]  " + str;
                sw.WriteLine(str);
                sw.Close();
                aFile.Close();
            }
            catch { }
            return 1;
        }
        #endregion

        #region 写log文件
        //将数组数据保存到log文件中,使用len的目的是我们可以选择从开始的一部分数据来储存
        public int writeLogFile(byte[] bb,int len)
        {
            if (len > bb.Length) len = bb.Length;
            string str = "";
            for (int i = 0; i < len; i++)
            {
                str += bb[i].ToString("X2");
                str += " ";
            }
            string filePath = Form1.form.datadir + @"run.log";
            if (File.Exists(filePath))
            {
                FileInfo fileInfo = new FileInfo(filePath);
                if (fileInfo.Length > 2000000)
                    File.Delete(filePath);
            }
            try
            {
                FileStream aFile = new FileStream(filePath, FileMode.OpenOrCreate | FileMode.Append);
                StreamWriter sw = new StreamWriter(aFile);
                DateTime tt = DateTime.Now;
                str = "[" + tt.ToString() + "]   " + str+"\r\n";
                sw.WriteLine(str);
                sw.Close();
                aFile.Close();
                return 1;
            }
            catch
            {
                return 1;
            }            
        }
        #endregion

这样我们的网络类就完成了,在真正使用这个类的时候,可以直接调用这个类就OK了.

下一章我们就直接讲解IEC104的规约结构了,最好能提前看看,呵呵.

好了就写到这里.

创作不易,点赞不止,您的关注与点赞是我创作的最大动力!

探索IEC104:电力系统监控与远程控制的强大协议 阅读详情

相关推荐

工控CTF之协议分析2——MMS

工控CTF之协议分析2——MMS

一个普普通通的博客 4087

工控CTF之协议分析1——Modbus

工控CTF之协议分析1——Modbus

一个普普通通的博客 1万+

手把手教你学习IEC104协议和编程实现 二

iec104协议结构

sixtome 1157

4、电科院FTU检测标准学习笔记-功能检测2

作者简介:本人从事电力系统多年,岗位包含研发,测试,工程等,具有丰富的经验在配电自动化验收测试以及电科院测试中,本人全程参与,积累了不少现场的经验。

sixtome 791

电力监控系统网络安全监测:从规则引擎到104规约异常检测实践

在工业控制系统安全防护体系中,网络安全监测能力建设已成为等保2.0和电力行业合规要求落地的关键环节。传统边界防护与日志审计主要依赖固定特征匹配,难以识别工控协议畸变报文、慢速暴力破解及组态变更等行为型攻击。为此,需要从协议深度解析、流量行为分析和主机白名单机制等多个维度构建纵深监测体系。IEC 60870-5-104和IEC 61850 MMS等电力专用规约的语义级检测,结合异常流量建模与告警降噪,能够有效弥补现有监测盲区。本文围绕电力监控系统安全防护的工程实践,详细拆解基于流量的威胁检测平台设计、审计溯源

weixin_29163797的博客 269

104协议dll源代码(C#)

此代码为104协议的动态链接库源代码dll,为电力行业的104协议通信使用。

104电力规约的c#部分功能实现

104电力规约的c#部分功能实现 实现了总召\单点数据解析\浮点数据解析,

IEC104规约Server及Client实现

IEC104规约的服务器和客户端实现,遥信遥测遥调遥控都有,学习的实际项目都能用上,VC6.0直接编译运行

保姆级教程:用ESP32和SPH0645麦克风做个无线录音笔(附Python服务端实时播放)

本文提供了一份详细的ESP32无线录音笔开发指南,涵盖从硬件搭建到Python服务端实时回放的全过程。通过SPH0645麦克风采集高质量音频数据,利用ESP32的I2S接口和WiFi功能实现无线传输,并结合Python服务端实现实时播放和WAV文件保存,适合远程音频监控和创意交互装置开发。

weixin_30700099的博客 641

Android BluetoothSocket

BluetoothSocket是Android中用于在蓝牙设备之间进行数据传输的一种Socket。它提供了一种类似于TCP/IP协议的数据传输方式,可以在两个设备之间建立一个虚拟的通信管道,使得两个设备之间可以进行数据的传输和接收。BluetoothSocket可以用于客户端和服务端之间的通信,通过BluetoothSocket可以实现数据的双向传输。

liuning1985622的博客 1477

Linux CentOS7文件系统(/dev/var)的容量扩充

原因是之前安装docker后,再想安装Nessus,系统说home、var文件系统的内存都不够。

JHoanna的博客 3226

Springboot(五十六)SpringBoot3集成SkyWalking

Dskywalking.collector.backend_service:表示skywalking的collector服务的IP及端口。注意上图中红框标注的位置,我们可以根据启动Springboot项目启动时候控制台输出的ID进行筛选。注意上图中红框标注的位置,我们可以根据启动Springboot项目启动时候控制台输出的ID进行筛选。其他的版本的 APM 地址(这个我不需要,我们是在docker中部署的,留给有需要的同学)其他的java 版本的 Agents 地址。

camellia的博客 2601

WGCNA分析中那些没人告诉你的坑:以肺结核差异基因模块筛选为例

本文深入探讨WGCNA分析在肺结核差异基因模块筛选中的七个关键陷阱,包括soft-thresholding选取、样本离群值检测、模块合并阈值调整等。通过实际案例和R代码示例,揭示生信数据挖掘中常被忽视的技术细节,帮助研究者避免常见错误,提升分析结果的可靠性和生物学意义。

weixin_29218509的博客 261

全局–局部傅里叶神经算子(GL-FNO):原理、严格推导与 PINN 混合策略

本文提出了一种全局-局部傅里叶神经算子(GL-FNO)及其与物理信息神经网络(PINN)的结合方法。主要内容包括: 架构设计:GL-FNO包含两个分支 - 全局分支处理低频结构,使用下采样输入;局部分支处理高频细节,使用全分辨率输入。两分支通过自适应门控机制融合。 理论分析:证明了GL-FNO在"低频全局+高频局部"任务上的优势,给出了误差分解和复杂度分析,并推导了与PINN结合时的渐进一致性条件。 实现方法:采用频谱微分计算物理残差,推导了完整的训练流程,包括数据损失与物理损失的联合优

VectorShift的博客 1331

C#图像处理:OpenCVSharp实现简单人脸识别

人脸识别是计算机视觉领域中的一个重要研究方向,它可以通过分析和比对人脸图像,实现识别和验证的功能。希望通过这篇文章的学习,你对OpenCVSharp和人脸识别有了初步的了解,并能够运用这些知识进行自己的项目开发。当我们运行程序时,它会加载人脸识别模型并读取待识别的图像,然后进行人脸检测并在图像上标记出检测到的人脸位置。当然,这只是一个入门级的示例,实际的人脸识别系统还需要更加复杂的算法和大量的训练数据来提高准确性和鲁棒性。最后,我们遍历检测到的人脸,并在原始图像上绘制矩形框,标记出检测到的人脸位置。

WELL_CODER的博客 1129

汽车OTA 测试用例

汽车OTA 测试用例

天赐好车的博客 973

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

上一篇: iec104测试软件更新通知
下一篇: 手把手教你学习IEC104协议和编程实现 二
six2me
博客等级 码龄23年 1万+粉丝 291原创
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

six2me

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值