当前位置: 首页 > news >正文

西门子s7通信协议

目录

西门子s7通信协议

s7协议的使用

S7协议帧结构

使用tcp五次握手进行连接

数据的读取

接收数据的响应

数据的写入

完整代码


西门子s7通信协议

S7Comm(S7 Communication)是西门子专有的协议,是西门子S7通讯协议簇里的一种。 S7通信协议是西门子S7系列PLC内部集成的一种通信协议,是S7系列PLC的精髓所在。 它是一种运行在传输层之上的(会话层/表示层/应用层)、经过特殊优化的通信协议,其信息传输可以基于MPI网络、 PROFIBUS网络或者以太网 s7在TCP连接上后还需要进行两次握手 S7协议的TCP/IP实现依赖于面向块的ISO传输服务。S7协议被封装在TPKT和ISO-COTP协议中,这使得PDU(协议数据单元) 能够通过TCP传送。

s7协议的使用

使用tcp连接时需要进行五次握手,其中有三次是tcp客户端与服务器的基有链接,然后需要再通过s7协议发送两次请求连接,共为五次握手。

S7协议帧结构

1 TPKT 会话层  主要设置版本号 预留号 报文总长度
2  COPT 表示层  设置PDU类型
3  s7协议 应用层 设置协议头和协议参数等

使用tcp五次握手进行连接

public partial class Form1 : Form
{public Form1(){InitializeComponent();}/// <summary>/// 五次握手/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void button1_Click(object sender, EventArgs e){// s7协议// 1 需要通过socket三次握手,不用写握手过程// 目前提供设备型号s71200  cpu:1212c    电压是24vDCTcpClient client = new TcpClient();client.Connect("192.168.107.202",102); // 连接服务器receiveData(client);// 2 第一请求连接  发送请求帧为// 总共22个字节byte[] bs1 = new byte[]{0x03, // 1字节版本号 默认是030x00, // 1字节 保留值 默认00x00, 0x16, // 2 字节 报文的总长度0x11, // 1字节从该字节往后字节个数 十进制是170xE0, // PDU 类型0x00,0x00, // DST引用 默认值0x00,0x01, // src引用0x00, // 采用默认值0xc1, // 上位机擦书0x02, // 上位机长度0x10,0x00, // 0x01代表双边通信 0x00机架号和插槽号0xC2, // plc参数0x02, // 长度0x03,0x01, // 0x01和0x00 共同控制机架号和插槽0xC0,0x01,0x0a};client.GetStream().Write(bs1,0,bs1.Length); // 发送第一次请求帧 // 3 第二次请求连接 发送请求帧为bs1 = new byte[]{0x03, // 1字节版本号 默认是030x00, // 1字节 保留值 默认00x00, 0x19, // 2 字节 报文的总长度0x02, // 当前字节后的字节数0xF0, // PUD类型 数据传输0x80, // 最高是十进制1280x32, // 协议ID,固定值0x01, // 工作类型 0x01 主站发送请求0x00,0x00,0x00,0x00,0x00,0x08, // 参数长度0x00,0x00, // 数据长度0xF0, // 功能码0x00, // Reserved保留值0x00,0x03, // 允许操作最大工作队列0x00,0x03, 0x03,0xc0, // 允许处理最大字节数组};client.GetStream().Write(bs1,0,bs1.Length);MessageBox.Show("连接成功");}/// <summary>/// 接收响应数据集/// </summary>/// <param name="tcpClient"></param>public void receiveData(TcpClient tcpClient){Task.Run(() =>{byte[] bytes = new byte[1024];while (tcpClient.Connected){// ONEint count = tcpClient.GetStream().Read(bytes,0,bytes.Length);if (count == 0) return;Console.WriteLine(BitConverter.ToString(bytes, 0, count) + "\r\n");// TWObyte[] s = new byte[count];Array.Copy(bytes,s,count);Console.WriteLine(string.Join(",",s));}});}
}


数据的读取

/// <summary>
/// 读取M区
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button2_Click(object sender, EventArgs e)
{// 发送请求帧 请求M区地址从00开始 读取一个数据// 读取数据时的请求帧byte[] data = new byte[] {// TPKT: 版本号 预留号 总字节长度0x03, // 版本号 0x00, // 预留号0x00,0x01F, // 总字节长度// COTP: 0x02, // 往下的长度0xF0, // PDU类型0x80, // 目标引用// s7-header s7头0x32, // 协议ID 默认0x01, // 主站开始发请求0x00,0x00, // 预留位置0x03,0x7b, // 随机生成的数字 每次在基础之上递增0x00,0x0e, // 参数长度0x00,0x00, // 数据长度// s7-参数部分0x04, // 功能码 读取功能                 重点0x01, // 如果涉及多读时候 设置为1,0x12, // 结构表示 一般默认120x0a, // 往后的字节长度0x10, // 寻址模式0x02, // 读取的数据类型 02是字节类型0x00,0x01, // 读取长度                   重点0x00,0x00, // 读取不是DB区               重点0x83, // 0x83 M存储区,0x84DB块          重点0x00,0x00,0x70, // 开始数据起始地址      重点// 列如M30000, 实际地址是30000*8=24 00000,把转成山歌字节,转成16进制3a980 对应三个地址,0x03,0xA9 0x80// 列如数据DB块的数据,DB21234,4000,其中DB号是21234 转成16进制0x52F2,DB区改为0x52,0xF2// 40000*8=,2000,转成16进制7d00 转成三个字节变成 0x00,0x7d,0x00};socket.Send(data);
}

接收数据的响应

/// <summary>
/// 接收响应数据
/// </summary>
void startReceive()
{Task.Run(() =>{byte[] bytes = new byte[1024];while (true){int count = socket.Receive(bytes);if (count == 0) break;// 转为16进制的字符串Console.WriteLine("十六进制打印:"+BitConverter.ToString(bytes,0,count));// 转为十进制打印byte[] datas = new byte[count];Array.Copy(bytes,datas,count);Console.WriteLine("十进制打印:" + string.Join(",",datas));Invoke(new Action(() =>{try{this.label1.Text = datas[25].ToString();}catch (Exception ex){Console.WriteLine(ex);}}));/* 响应的数据* 连接的响应* 03-00-00-16-11-D0-00-01-00-08-00-C0-01-0A-C1-02-10-00-C2-02-03-01* 03-00-00-1B-02-F0-80-32-03-00-00-00-00-00-08-00-00-00-00-F0-00-00-03-00-03-00-F0* * 读取数据的响应* 03-00-00-1A-02-F0-80-32-03-00-00-03-7B-00-02-00-05-00-00-04-01-FF(读取成功的标志)-04(读取的数据类型)-00-08(数据的长度)-0C(数据)*/}});
}

数据的写入

/// <summary>
/// 写入M14
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button1_Click(object sender, EventArgs e)
{byte[] value = BitConverter.GetBytes(uint.Parse(textBox1.Text));// 生成写的报文byte[] bs = new byte[]{// TPKT部分0x03, // 版本号0x00, // 预留号// 0x00,0x24, // 报文总长度360x00,0x27, // 报文总长度39// TOPT0x02, // 长度0xF0, // PDU类型0xB0, // 目标引用// s7 header0x32, // 协议id0x01, // 主站开始请求0x00,0x00, // 预留部分0x03,0x7d, // 随机生成0x00,0x0E, // 参数长度0x00,0x08, // 参数数据长度// s7 参数0x05, // 05代表写入,04代表读取0x01, // 通信项数 可以支持多写0x12, // 变量指定0x0A, // 后面的长度0x10,0x02, // 传输数据类型 字节// 0x00,0x01, // 操作数据的长度0x00,0x04, // 操作数据的长度0x00,0x00, // M区 不是DB区0x83, // M区// 0x00,0x00,0x70, // 开始写入的地址M140x00,0x3e,0x80, // 开始写入的地址M20000x00,0x04, // 字节类型// 0x00,0x80, // 写入的长度  8位=1字节0x00,0x20, // 写入的长度  8位=1字节 (写入4个数据 4*8=32转16进制为20)//byte.Parse(textBox1.Text)value[3],value[2],value[1],value[0]};tcp.Send(bs);Type = RequestType.Write;
}

完整代码

public partial class Form1 : Form
{TcpClientHelper tcp;public Form1(){InitializeComponent();}private void Tcp_OnClose(TcpClientHelper obj){MessageBox.Show("客户端关闭");}enum RequestType{Write,  // 写入请求Read,   // 读取请求Connect // 连接的请求}RequestType Type;private void Tcp_OnMessage(byte[] arg1, TcpClientHelper arg2){// 获取数据即可 自动触发BeginInvoke(new Action(() =>{switch (Type){case RequestType.Write:// 写入数据的响应Console.WriteLine("写入数据的响应"+BitConverter.ToString(arg1) );break;case RequestType.Read:// 读取数据的响应Console.WriteLine("读取数据的响应" + BitConverter.ToString(arg1));this.label1.Text = arg1[arg1.Length-1].ToString();break;case RequestType.Connect:Console.WriteLine("连接时的响应" + BitConverter.ToString(arg1));break;}}));}/// <summary>/// 写入M14/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void button1_Click(object sender, EventArgs e){byte[] value = BitConverter.GetBytes(uint.Parse(textBox1.Text));// 生成写的报文byte[] bs = new byte[]{// TPKT部分0x03, // 版本号0x00, // 预留号// 0x00,0x24, // 报文总长度360x00,0x27, // 报文总长度39// TOPT0x02, // 长度0xF0, // PDU类型0xB0, // 目标引用// s7 header0x32, // 协议id0x01, // 主站开始请求0x00,0x00, // 预留部分0x03,0x7d, // 随机生成0x00,0x0E, // 参数长度0x00,0x08, // 参数数据长度// s7 参数0x05, // 05代表写入,04代表读取0x01, // 通信项数 可以支持多写0x12, // 变量指定0x0A, // 后面的长度0x10,0x02, // 传输数据类型 字节// 0x00,0x01, // 操作数据的长度0x00,0x04, // 操作数据的长度0x00,0x00, // M区 不是DB区0x83, // M区// 0x00,0x00,0x70, // 开始写入的地址M140x00,0x3e,0x80, // 开始写入的地址M20000x00,0x04, // 字节类型// 0x00,0x80, // 写入的长度  8位=1字节0x00,0x20, // 写入的长度  8位=1字节 (写入4个数据 4*8=32转16进制为20)//byte.Parse(textBox1.Text)value[3],value[2],value[1],value[0]};tcp.Send(bs);Type = RequestType.Write;}/// <summary>/// 读取M14/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void button2_Click(object sender, EventArgs e){// 读取的报文31字节byte[] bs = new byte[]{0x03,0x00,0x00,0x1f,0x02,0xf0,0xb0,// 协议参数0x32,0x01,0x00,0x00,0x03,0x7d,0x00,0x0e,0x00,0x00, // 读取操作 为00x04,0x01,0x12,0x0a,0x10,0x02,//0x00,0x01, // 读取长度0x00,0x04, // M2000 读取四个字节0x00,0x00,0x83,// 0x00, 0x00,0x700x00,0x3e,0x80,};tcp.Send(bs);Type = RequestType.Read;}/// <summary>/// 连接/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void Form1_Load(object sender, EventArgs e){// 1 创建客户端对象tcp = new TcpClientHelper();tcp.Connect("192.168.107.202", 102); // 连接服务器// 2 获取数据tcp.OnMessage += Tcp_OnMessage;// 3 关闭连接tcp.OnClose += Tcp_OnClose;// 第一次连接请求byte[] bs = new byte[]{0x03, // 1字节版本号 默认是030x00, // 1字节 保留值 默认00x00, 0x16, // 2 字节 报文的总长度0x11, // 1字节从该字节往后字节个数 十进制是170xE0, // PDU 类型0x00,0x00, // DST引用 默认值0x00,0x01, // src引用0x00, // 采用默认值0xc1, // 上位机擦书0x02, // 上位机长度0x10,0x00, // 0x01代表双边通信 0x00机架号和插槽号0xC2, // plc参数0x02, // 长度0x03,0x01, // 0x01和0x00 共同控制机架号和插槽0xC0,0x01,0x0a};tcp.Send(bs);// 第二次请求连接bs = new byte[]{0x03, // 1字节版本号 默认是030x00, // 1字节 保留值 默认00x00, 0x19, // 2 字节 报文的总长度0x02, // 当前字节后的字节数0xF0, // PUD类型 数据传输0x80, // 最高是十进制1280x32, // 协议ID,固定值0x01, // 工作类型 0x01 主站发送请求0x00,0x00,0x00,0x00,0x00,0x08, // 参数长度0x00,0x00, // 数据长度0xF0, // 功能码0x00, // Reserved保留值0x00,0x03, // 允许操作最大工作队列0x00,0x03,0x03,0xc0, // 允许处理最大字节数组};tcp.Send(bs);Type = RequestType.Connect;MessageBox.Show("连接成功");}
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • C++类别之Static成员
  • 代码分支管理规范
  • 远程桌面管理软件,如何使用远程桌面管理软件来远程控制服务器
  • C++:类和对象(上)
  • sysinternals工具包
  • 《Nginx核心技术》第08章:为Nginx动态添加模块
  • 第6章>>实验7:PS(ARM)端Linux RT与PL端FPGA之间(通过Memory存储器进行通信和交互)《LabVIEW ZYNQ FPGA宝典》
  • Android SurfaceFlinger——信号同步原理(五十一)
  • 探索WebKit的奥秘:塑造高效、兼容的现代网页应用
  • maxscript循环中提高性能
  • 记一次对加密后pythonEXP的解密以及分析
  • SS9283403 sqlite3交叉编译并部署到SS928(六)
  • Together规则引擎 金融解决方案
  • 八股文”在实际工作中的作用:敲门砖还是空谈?
  • supermap制作发布二三维地图服务
  • Akka系列(七):Actor持久化之Akka persistence
  • Android 架构优化~MVP 架构改造
  • Android系统模拟器绘制实现概述
  • ECS应用管理最佳实践
  • Essential Studio for ASP.NET Web Forms 2017 v2,新增自定义树形网格工具栏
  • Fastjson的基本使用方法大全
  • Intervention/image 图片处理扩展包的安装和使用
  • java2019面试题北京
  • Java的Interrupt与线程中断
  • Making An Indicator With Pure CSS
  • Perseus-BERT——业内性能极致优化的BERT训练方案
  • python 装饰器(一)
  • Quartz初级教程
  • REST架构的思考
  • SQL 难点解决:记录的引用
  • SQLServer之创建显式事务
  • 从0实现一个tiny react(三)生命周期
  • 从tcpdump抓包看TCP/IP协议
  • 从零开始的webpack生活-0x009:FilesLoader装载文件
  • 更好理解的面向对象的Javascript 1 —— 动态类型和多态
  • 官方解决所有 npm 全局安装权限问题
  • 买一台 iPhone X,还是创建一家未来的独角兽?
  • 实战|智能家居行业移动应用性能分析
  • 通过获取异步加载JS文件进度实现一个canvas环形loading图
  • 微信支付JSAPI,实测!终极方案
  • 我的zsh配置, 2019最新方案
  • 学习ES6 变量的解构赋值
  • 一个6年java程序员的工作感悟,写给还在迷茫的你
  • 一文看透浏览器架构
  • 积累各种好的链接
  • 数据可视化之下发图实践
  • ​渐进式Web应用PWA的未来
  • (1)Nginx简介和安装教程
  • (11)MATLAB PCA+SVM 人脸识别
  • (4) openssl rsa/pkey(查看私钥、从私钥中提取公钥、查看公钥)
  • (php伪随机数生成)[GWCTF 2019]枯燥的抽奖
  • (补充)IDEA项目结构
  • (顶刊)一个基于分类代理模型的超多目标优化算法
  • (二)延时任务篇——通过redis的key监听,实现延迟任务实战
  • (十五)devops持续集成开发——jenkins流水线构建策略配置及触发器的使用