手摸手教你撕碎西门子S7通讯协议04--S7COMM哀求

[复制链接]
发表于 2026-2-17 03:03:43 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
1、S7通讯回首

    - (1)创建TCP毗连      Socket.Connect-》已实现
     - (2)发送访问哀求     COTP-》已实现 
     - (3)交换通讯信息     Setup Communication-》本节实现
     - (4)实验相干使用     读、写、PLC启停、时间、上传下载-》待实现

 2、S7COMM哀求先容


在乐成完成上面的socket毗连和COTP哀求后,如今客户端还不能举行读写使用,还须要举行哀求创建通讯,以是客户端须要发送S7 Communicaton报文给服务端,在S7Communicaton报文中包罗“通讯哀求”; 服务端反馈S7 Communicaton报文。同样的报文中每个字段都有寄义,必须严格服从,这个过程是交换通讯信息, 报文信息如下

 留意下面几点:
1》从中可以看到发送是25个字节,相应是27个字节,可以看到发送报文由4个部分构成:TPKT占4个字节0-3,COTP占3个字节4-6,S7-Header占10个字节7-16,S7-Parameter占8个字节17-24,看到了不?但是相应报文27个,也是由4个部分构成,但S7-Header占12个字节,其他一样长度,留意了不?过细看这里,明确了,肯定要搞明确哦。

 2》ROSCTR,它是个摆列值,具体如下:

看这个地方的差别,发送是01,相应是03,对应上面的值就是表现上位机哀求是job,PLC相应是从站相应并带数据 


3》PDU Type,跟上节的不一样,这次是发送吸取都是f0,上节的COTP报文是e0和d0,再来看下

4》Error class已知摆列值,这个出如今相应报文中,可以看到00表现没有错误,这是我们必须看到的,假如不是00表现你的报文有bug
 5》Error code已知摆列值,这个出如今相应报文中,可以看到00表现没有错误,这是我们必须看到的,假如不是00表现你的报文有bug

6》发送报文中的功能码,即17字节,Function已知摆列值,如今知道发送报文为什么是f0了吧


这些东西都要明确,否则写起代码起来就会堕落,就不能象我一样举行云云的分析和手撕,来个超等的啵啵啵。

3、开搞S7COMM通讯  



 完备代码 
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Net.Sockets;
  5. using System.Text;
  6. using System.Threading;
  7. using System.Threading.Tasks;
  8. namespace west.siemenscomm
  9. {
  10.     internal class Program
  11.     {
  12.         /// <summary>
  13.         /// plc的ip地址
  14.         /// </summary>
  15.         static string _ip = "192.168.1.66";
  16.         /// <summary>
  17.         /// 端口号
  18.         /// </summary>
  19.         static int _port = 102;
  20.         /// <summary>
  21.         /// 机柜号,插槽号
  22.         /// </summary>
  23.         static byte _rack = 0, _slot = 1;
  24.         /// <summary>
  25.         /// socket对象
  26.         /// </summary>
  27.         static Socket socket = null;
  28.         /// <summary>
  29.         /// 时间事件
  30.         /// </summary>
  31.         static ManualResetEvent TimeoutObject = new ManualResetEvent(false);
  32.         /// <summary>
  33.         /// 连接状态
  34.         /// </summary>
  35.         static bool connectState = false;
  36.         /// <summary>
  37.         /// 通讯连接的pdu长度
  38.         /// </summary>
  39.         static short _pduSize = 240;
  40.         static void Main(string[] args)
  41.         {
  42.             Connect();
  43.             if (connectState)
  44.             {
  45.                 COTPConnection();
  46.                 if (connectState)
  47.                 {
  48.                     SetupCommunication();
  49.                 }
  50.             }
  51.             Console.ReadKey();
  52.         }
  53.         /// <summary>
  54.         /// 通讯连接
  55.         /// </summary>
  56.         /// <exception cref="NotImplementedException"></exception>
  57.         private static bool SetupCommunication()
  58.         {
  59.             //s7comm连接包括4个部分,共25个字节,即25=4+3+10+8
  60.             byte[] setupBytes = new byte[] {
  61.                 // 1)TPKT包括4个字节
  62.                 0x03,//版本默认3
  63.                 0x00,//保留默认0
  64.                 0x00,//整个请求字节数高位
  65.                 0x19,//整个请求字节数低位,0x19转换成10进制就是25
  66.                //2)COTP包括3个字节
  67.                 0x02,//当前字节以后的字节数(不包括自已,0x02转换成10进制就是2),注意这个“当前字节以后的字节数”是指COTP这部分,而不是整个字节部分
  68.                 0xf0,//PDU Type,0xe0 连接请求,0xd0 连接确认,0x08 断开请求,0x0c 断开确认,0x05 拒绝访问,0x01 加急数据,0x02 加急数据确认,0x04 用户数据,0x07 TPDU错误,0xf0 数据传输
  69.                 0x80,//TPDU number,固定值
  70.                 // 3)Header包括10个字节
  71.                 0x32,//默认值,协议id
  72.                 0x01,//ROSCTR,0x01 Job request。主站发送请求,0x02 Ack。从站响应请求不带数据,0x03 Ack_Data。从站响应请求并带有数据,0x07 Userdata。原始协议的扩展。读取编程/调试、SZL读取、安全功能、时间设置等
  73.                 0x00,//Redundancy Identification (Reserved)固定值,占2个字节
  74.                 0x00,
  75.                 0x00,//Protocol Data Unit Reference固定值,占2个字节
  76.                 0x00,
  77.                 0x00,//Parameter length参数长度,占2个字节
  78.                 0x08,
  79.                 0x00,//Data length数据长度,占2个字节
  80.                 0x00,
  81.                 // 4)Parameter包括8个字节
  82.                 0xf0,//Function功能码,具体是:0x00 CPU服务,0xF0 设置通信,0x04 读取变量,0x05 写变量,0x1A 请求下载,0x1B 下载块,0x1C 下载结束,0x1D 开始上传,0x1E 上传,0x1F 结束上传,0x28 PLC 控制,0x29 PLC 停止
  83.                 0x00,//保留默认值
  84.                 0x00,//Max AmQ(parallel jobs with ack) calling,占2个字节
  85.                 0x03,
  86.                 0x00,//Max AmQ(parallel jobs with ack) called,占2个字节
  87.                 0x03,
  88.                 0x03,//PDU length,占2个字节,0x03co转换成10进制就是960
  89.                 0xc0
  90.             };
  91.             try
  92.             {
  93.                 socket.Send(setupBytes);
  94.                 //响应报文的长度就是固定的27个字节
  95.                 byte[] respBytes = new byte[27];
  96.                 int count = socket.Receive(respBytes);
  97.                 // 拿到PDU长度   后续进行报文组装和接收的时候可以参考
  98.                 byte[] pdu_size = new byte[2];
  99.                 pdu_size[0] = respBytes[26];
  100.                 pdu_size[1] = respBytes[25];
  101.                 _pduSize = BitConverter.ToInt16(pdu_size,0);
  102.                 if (respBytes[17] != 0x00&& respBytes[18] != 0x00)
  103.                 {
  104.                     Console.WriteLine("粗问题,COMM连接响应异常");
  105.                     connectState = false;
  106.                 }
  107.                 else
  108.                 {
  109.                     Console.WriteLine("太好了,COMM连接响应正常");
  110.                     connectState = true;
  111.                 }
  112.             }
  113.             catch (Exception ex)
  114.             {
  115.                 Console.WriteLine("Setup通信未建立!" + ex.Message);
  116.                 connectState = false;
  117.             }
  118.             return connectState;
  119.         }
  120.         /// <summary>
  121.         /// cotp连接
  122.         /// </summary>
  123.         private static bool COTPConnection()
  124.         {
  125.             //COTP连接包括2个部分,共22个字节),22=4+18
  126.             byte[] cotpBytes = new byte[] {
  127.                 //1)TPKT包括4个字节
  128.                 0x03,//版本号,版本默认3
  129.                 0x00,//默认保留为0
  130.                 0x00,//整个请求字节高位
  131.                 0x16,//整个请求字节低位(0x16转换成为10进制就是22)
  132.                 //2)COTP包括18个字节
  133.                 0x11,//当前字节以后的字节数(不包括自已,0x11转换成10进制就是17)
  134.                 0xe0,//PDU type,0xe0 连接请求,0xd0 连接确认,0x08 断开请求,0x0c 断开确认,0x05 拒绝访问,0x01 加急数据,0x02 加急数据确认,0x04 用户数据,0x07 TPDU错误,0xf0 数据传输
  135.                 0x00,//DST reference(2个字节)
  136.                 0x00,//
  137.                 0x00,//SRC reference(2个字节)
  138.                 0x00,//
  139.                 0x00,//class(固定的)
  140.                 0xc1, //Parameter-code  src-tsap 上位机
  141.                 0x02,  //Parameter-Len   
  142.                 0x10 ,   //Source TSAP:01->PG;02->OP;03->S7单边(服务器模式);0x10->S7双边通
  143.                 0x00,   //机架与插槽号为0   
  144.                 0xc2,//Parameter-code  dst-tsap PLC
  145.                 0x02,//Parameter len  
  146.                 0x03,//Destination TSAP  
  147.                 (byte)(_rack*32+_slot),//机架与插槽号:
  148.                 0xc0,  //    Parameter code:tpdu-size
  149.                 0x01,   //   Parameter length
  150.                 0x0a  //    TPDU size
  151.             };
  152.             try
  153.             {
  154.                 socket.Send(cotpBytes);
  155.                 //响应报文的长度是固定的22个字节
  156.                 byte[] respBytes = new byte[22];
  157.                 int count = socket.Receive(respBytes, 0, 22, SocketFlags.None);
  158.                 //第5个字节是pdu type,具体是:0xe0 连接请求,0xd0 连接确认,0x08 断开请求,0x0c 断开确认,0x05 拒绝访问,0x01 加急数据,0x02 加急数据确认,0x04 用户数据,0x07 TPDU错误,0xf0 数据传输
  159.                 if (respBytes[5] != 0xd0)
  160.                 {
  161.                     Console.WriteLine("粗问题,COTP连接响应异常");
  162.                     connectState = false;
  163.                 }
  164.                 else
  165.                 {
  166.                     Console.WriteLine("太好了,COTP连接响应正常");
  167.                     connectState = true;
  168.                 }
  169.             }
  170.             catch (Exception ex)
  171.             {
  172.                 Console.WriteLine("COTP连接未建立!" + ex.Message);
  173.                 connectState = false;
  174.             }
  175.             return connectState;
  176.         }
  177.         /// <summary>
  178.         /// tcp连接
  179.         /// </summary>
  180.         /// <param name="timeout"></param>
  181.         private static void Connect(int timeout = 50)
  182.         {
  183.             TimeoutObject.Reset();
  184.             try
  185.             {
  186.                 socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
  187.                 socket.BeginConnect(_ip, _port, callback =>
  188.                 {
  189.                     connectState = false;
  190.                     var cbSocket = callback.AsyncState as Socket;
  191.                     if (cbSocket != null)
  192.                     {
  193.                         connectState = cbSocket.Connected;
  194.                         if (cbSocket.Connected)
  195.                             cbSocket.EndConnect(callback);
  196.                     }
  197.                     TimeoutObject.Set();
  198.                 }, socket);
  199.                 TimeoutObject.WaitOne(2000, false);
  200.             }
  201.             catch (SocketException ex)
  202.             {
  203.                 if (ex.ErrorCode == 10060)
  204.                     Console.WriteLine(ex.Message);
  205.             }
  206.             if (socket == null || !socket.Connected || ((socket.Poll(200, SelectMode.SelectRead) && (socket.Available == 0))))
  207.             {
  208.                 Console.WriteLine("网络连接失败");
  209.             }
  210.             Console.WriteLine(connectState == true ? "连接成功" : "连接失败");
  211.         }
  212.     }
  213. }
复制代码
4、运行测试


5、小结

这就是第3个流程,创建comm毗连,它是通过socket对象通讯实现的,小同伴们,明确了不?下节继承屌起来。
原创不易,打字截图不易,走过途经,不要错过,欢迎点赞,收藏,转载,复制,抄袭,留言,动动你的金手指,早日实现财政自由


回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表