zoukankan      html  css  js  c++  java
  • 初识Modbus TCP/IP-------------C#编写Modbus TCP客户端程序(一)

    转自:http://blog.csdn.net/thebestleo/article/details/52269999

    首先我要说明一下,本人新手一枚,本文仅为同样热爱学习的同学提供参考,有不

    对的地方还请大家热心指出,本文只起到一个抛砖引玉的作用,希望看到本文的同学可

    以从中学习到少许知识,也希望可以跟各位读者成为朋友,多多交流,使学习不再孤单

    寂寞。


    由于本文太长,顾分为两部分,第二部分连接

    初识Modbus TCP/IP-------------C#编写Modbus TCP客户端程序(二)


    http://blog.csdn.net/thebestleo/article/details/52331976


    废话少说,我们直接上干的,学习知识,第一个是收集和查阅资料,这个是必须的。


    1、Modbus官方网站:http://www.modbus.org/

    2、Modbus协议规范英文原版:

       http://download.csdn.net/download/thebestleo/9609480

    3、Modbus协议规范中文版:

       http://download.csdn.net/download/thebestleo/9609620

    4、Modbus通讯的TCP实现指南:

       http://download.csdn.net/download/thebestleo/9609646  

    5、Modbu TCP服务器测试工具:

       http://download.csdn.net/download/thebestleo/9609665

    6、Modbu TCP客户端测试工具:

       http://download.csdn.net/download/thebestleo/9609676

    7、网络数据分析软件Wireshark:

       http://download.csdn.net/download/thebestleo/9613131

    8、文章中Modbus Slave的设置文件,打包下载一下,便于你的测试

       http://download.csdn.net/detail/thebestleo/9614679

    9、本文最终所写成的C#的Modbus TCP客户端程序

         http://download.csdn.net/download/thebestleo/9614682


    下面传一张modbus官网上的一张图片,是一个Modbus TCP的工具包,跟我上面给出的类似


    

        看见没,上述资料价值500美元,好了,我的工作到此结束,剩下的不给钱不说了大笑

        开个玩笑,我们继续。说实话,上述的资料我也没有特别仔细的看过,

       (等找个时间好好看看)

        这里我只是简单的理解一下Modbus TCP/IP协议的内容,就是去掉了modbus协议

    本身的CRC校验,增加了MBAP 报文头。(这里只是简单的理解,深入之后可能会有更

    多的东西需要学习,但为了可以快速入门,我们先按照这个思路往下走)。

    我们首先来看一下,MBAP 报文头都包括了哪些信息和内容



    事务元标识符(2个字节):用于事务处理配对。在响应中,MODBUS服务器复制请求的事务处理标识符。

    这里在以太网传输中存在一个问题,就是先发后至,我们可以利用这个事务处理

    标识符做一个TCP序列号,来防止这种情况所造成的数据收发错乱(这里我们先不

    讨论这种情况,这个事务处理标识符我们统一使用0x00,0x01)

    协议标识符(2个字节):modbus协议标识符为0x00,0x00

    长度(2个字节):长度域是下一个域的字节数,包括单元标识符和数据域。

    单元标识符(1个字节):这个好像是个站号,文档中的说明没怎么看懂,有明白的

    可以留言告诉我。

    根据上面的思路很容易理解,在modbus报文前,加上表的MBAP报文头,再去掉modbus

    报文中的CRC校验就可以形成modbus TCP的报文,那么modbus报文格式是什么样的呢?

    modbus报文时根据不同的功能码,报文格式的形式是不同的,下面我们具体用一个C#

    的例程来说明一下Modbus TCP报文的数据组成和传输方法。(这里很多同学会说,我

    对modbus不了解,对C#更是知道的更少了,不要紧,只要你有一点C语言和串口通信的

    基础,其他的你尽管抄袭过来,日后慢慢的消化理解,很多老师在教育学生的时候总是

    鼓励什么独立思考,严禁抄袭什么的,再我看来抄别人的并没有什么错,学习吗,就是

    站在前人的肩膀上看世界,很多东西你没有那个时间去研究,还有很多东西即使你有那

    个时间你也研究不出来,老师上课教的是啥,不都是抄袭前人的科研成果吗,要是什么

    都需要自己研究,还用老师教什么。)

    所以我在这里以一种开放的态度,撰写了本文,希望大家能相互学习进步。

    言归正传,我们来用C#写一个Modbus TCP的客户端程序,并使用Modbus Slave

    这个软件对程序的功能进行测试

    1、首先,作为客户端程序,我们要先针对服务器IP和端口建立一个连接,IP地址根据

       Modbus Slave,所在电脑的IP来确定,Modbus TCP的端口号是众所周知的502

     (为了保持程序的完整性,我把第一步的整个程序都贴出来,避免造成歧义。)

    [csharp] view plain copy
    1. using System;  
    2. using System.Windows.Forms;  
    3. using System.Net.Sockets;  
    4. using System.Threading;  
    5. using System.Net;  
    6.   
    7. namespace Modbus_TCP_Client  
    8. {  
    9.     public partial class Form1 : Form  
    10.     {  
    11.         public Socket newclient;  
    12.         public bool Connected;  
    13.         public Thread myThread;  
    14.         public delegate void MyInvoke(string str);  
    15.         public Form1()  
    16.         {  
    17.             InitializeComponent();  
    18.         }  
    19.   
    20.         private void exit_Click(object sender, EventArgs e)  
    21.         {  
    22.             Application.Exit();  
    23.         }  
    24.   
    25.         public void Connect()  
    26.         {  
    27.             byte[] data = new byte[1024];  
    28.   
    29.             string ipadd = serverIP.Text.Trim();//将服务器IP地址存放在字符串 ipadd中  
    30.             int port = Convert.ToInt32(serverPort.Text.Trim());//将端口号强制为32位整型,存放在port中  
    31.   
    32.             //创建一个套接字   
    33.   
    34.             IPEndPoint ie = new IPEndPoint(IPAddress.Parse(ipadd), port);  
    35.             newclient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);  
    36.   
    37.   
    38.             //将套接字与远程服务器地址相连  
    39.             try  
    40.             {  
    41.                 newclient.Connect(ie);  
    42.                 connect.Enabled = false;//使连接按钮变成虚的,无法点击  
    43.                 Connected = true;  
    44.   
    45.             }  
    46.             catch (SocketException e)  
    47.             {  
    48.                 MessageBox.Show("连接服务器失败  " + e.Message);  
    49.                 return;  
    50.             }  
    51.   
    52.             ThreadStart myThreaddelegate = new ThreadStart(ReceiveMsg);  
    53.             myThread = new Thread(myThreaddelegate);  
    54.             myThread.Start();  
    55.             tmSend.Enabled = true;//增加定时发送需要将此功能打开  
    56.   
    57.         }  
    58.   
    59.         private void connect_Click_1(object sender, EventArgs e)  
    60.         {  
    61.             Connect();  
    62.         }  
    63.     }  
    64. }  



    2、为了避免连接服务器发生超时掉线,我们这里做一个定时发送的函数,保证

       在掉线时间范围内连续向服务器发送数据,注意,需要在连接函数中增加

       timersend.Enabled = true;,在连接服务器的同时来触发定时发送。

    [csharp] view plain copy
    1. private void timersend_Tick(object sender, EventArgs e)  
    2. {  
    3.        int isecond = 5000;//以毫秒为单位  
    4.        timersend.Interval = isecond;//5秒触发一次  
    5.        byte[] data = new byte[] { 0x00, 0x0f, 0x00, 0x00, 0x00, 0x06, 0x01, 0x04, 0x00, 0x00, 0x00, 0x01 };  
    6.        newclient.Send(data);  
    7. }  



    通过上面的两步,一个Modbus TCP的客户端连接已经建立起来了,下面我们就来分析

    Modbus TCP协议的具体内容与实现方式了。

    3、我们根据Modbus协议规范中文版中的内容,来写几个功能码的程序。

       我们直接用实例来说明:


    1)、01(0x01)功能码--------读线圈

    请求与响应格式




    这是一个请求读离散量输出20-38 的实例:




    由上图可以,我们来编程发送数据(这里需要注意一下,上述图片是

    从modbus协议文档上截取的,起始地址要根据你的服务器来具体分析,

    有的是从0开始的,有的是从1开始,所以起始地址应该是14)

    0x00,0x01,0x00,0x00,0x00,0x06,0x01,0x01,0x00,0x14,0x00,0x13

    [csharp] view plain copy
    1. private void send01_Click(object sender, EventArgs e)  
    2. {  
    3.     byte[] data = new byte[] { 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x01, 0x01, 0x00, 0x14, 0x00, 0x13;  
    4.     newclient.Send(data);  
    5. }  



    接收数据为:

    0x00,0x01,0x00,0x00,0x00,0x06,0x01,0x01,0x03,0xCD,0x6B,0x05

    这里还需要做一个接收函数和现实接收数据的文本框

    [csharp] view plain copy
    1. public void ReceiveMsg()  
    2.         {  
    3.             while (true)  
    4.             {  
    5.                 byte[] data = new byte[1024];//定义数据接收数组  
    6.                 newclient.Receive(data);//接收数据到data数组  
    7.                 int length = data[5];//读取数据长度  
    8.                 Byte[] datashow = new byte[length + 6];//定义所要显示的接收的数据的长度  
    9.                 for (int i = 0; i <= length + 5; i++)//将要显示的数据存放到数组datashow中  
    10.                     datashow[i] = data[i];  
    11.                 string stringdata = BitConverter.ToString(datashow);//把数组转换成16进制字符串  
    12.                 if (data[7] == 0x01) { showMsg01(stringdata + " "); };  
    13.                 if (data[7] == 0x02) { showMsg02(stringdata + " "); };  
    14.                 if (data[7] == 0x03) { showMsg03(stringdata + " "); };  
    15.                 if (data[7] == 0x05) { showMsg05(stringdata + " "); };  
    16.                 if (data[7] == 0x06) { showMsg06(stringdata + " "); };  
    17.                 if (data[7] == 0x0F) { showMsg0F(stringdata + " "); };  
    18.                 if (data[7] == 0x10) { showMsg10(stringdata + " "); };  
    19.             }  
    20.         }  


    [csharp] view plain copy
    1. public void showMsg01(string msg)  
    2. {  
    3.   
    4.     //在线程里以安全方式调用控件  
    5.     if (receiveMsg01.InvokeRequired)  
    6.     {  
    7.         MyInvoke _myinvoke = new MyInvoke(showMsg01);  
    8.         receiveMsg01.Invoke(_myinvoke, new object[] { msg });  
    9.     }  
    10.     else  
    11.      {  
    12.         receiveMsg01.AppendText(msg);  
    13.      }  
    14.   
    15. }  


    下面我来介绍1个测试工具,modbus slave,打开软件,点击Connection来建立一个

    modbus tcp服务器,如下图所示



    下面我们再来设置一下该软件,这里我们首先测试的是0x01功能码,所以我们点击Setup,设置如下图





    我们0x01功能码的例子是一个请求读离散量输出20-38,这里注意一下分析,

    0xCD是27-20,0x6B是35-28,0x05是43-36,这里从39到43用0来补齐

    下面我们在Modbus Slave中填写数据,如图所示







    下面,我们运行我们用C#编写的软件,并打开Wireshark来截取封包进行分析,

    截取封包如下图

    Modbus TCP请求




    Modbus TCP相应:




    再看一下我们软件接收到的数据




    2)、02(0x02)功能码--------读离散量输入

    请求与应答PDU



    这是一个请求读取离散量输入197-218 的实例:




    发送数据为:

    0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x01, 0x02, 0x00, 0xC5, 0x00, 0x16

    程序如下:

    [csharp] view plain copy
    1. private void send02_Click(object sender, EventArgs e)  
    2. {  
    3.     byte[] data = new byte[] { 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x01, 0x02, 0x00, 0xC5, 0x00, 0x16 };  
    4.     newclient.Send(data);  
    5. }  


    接收数据为:

    0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x01, 0x02, 0x03, 0xAC, 0xDB, 0x35

    程序如下:

    [csharp] view plain copy
    1. public void showMsg02(string msg)  
    2. {  
    3.   
    4.     //在线程里以安全方式调用控件  
    5.     if (receive0x01.InvokeRequired)  
    6.     {  
    7.         MyInvoke _myinvoke = new MyInvoke(showMsg02);  
    8.         receive0x02.Invoke(_myinvoke, new object[] { msg });  
    9.     }  
    10.     else  
    11.     {  
    12.         receive0x02.AppendText(msg);  
    13.     }  
    14.   
    15. }  


    我们再来看一下Modbus Slave设置







    我们再看一下Wireshark截取封包

    Modbus TCP请求



    Modbus TCP响应




    我们的软件所收到的数据





  • 相关阅读:
    Call KernelIoControl in user space in WINCE6.0
    HOW TO:手工删除OCS在AD中的池和其他属性
    关于新版Windows Server 2003 Administration Tools Pack
    关于SQL2008更新一则
    微软发布3款SQL INJECTION攻击检测工具
    HyperV RTM!
    OCS 2007 聊天记录查看工具 OCSMessage
    CoreConfigurator 图形化的 Server Core 配置管理工具
    OC 2007 ADM 管理模板和Live Meeting 2007 ADM 管理模板发布
    Office Communications Server 2007 R2 即将发布
  • 原文地址:https://www.cnblogs.com/liyanwei/p/7272475.html
Copyright © 2011-2022 走看看