初識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 + "\r\n"); };  
  13.                 if (data[7] == 0x02) { showMsg02(stringdata + "\r\n"); };  
  14.                 if (data[7] == 0x03) { showMsg03(stringdata + "\r\n"); };  
  15.                 if (data[7] == 0x05) { showMsg05(stringdata + "\r\n"); };  
  16.                 if (data[7] == 0x06) { showMsg06(stringdata + "\r\n"); };  
  17.                 if (data[7] == 0x0F) { showMsg0F(stringdata + "\r\n"); };  
  18.                 if (data[7] == 0x10) { showMsg10(stringdata + "\r\n"); };  
  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響應




我們的軟件所收到的數據






免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM