西门子s7通信协议

news2024/9/24 9:27:35

目录

西门子s7通信协议

S7协议帧结构

s7协议的使用

连接

COTP连接(第一次握手)报文

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协议帧结构

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

s7协议的使用

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

TCP三次握手(TCP连接时进行) => COTP连接(第一次握手连接) => S7连接(第二次握手) => 数据的读写

连接

TCP三次握手(TCP连接时进行) => COTP连接(第一次握手连接) => S7连接(第二次握手) => 数据的读写

COTP连接(第一次握手)报文

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    电压是24vDC
        TcpClient client = new TcpClient();
        client.Connect("192.168.107.202",102); // 连接服务器

        receiveData(client);

        // 2 第一请求连接  发送请求帧为
        // 总共22个字节
        byte[] bs1 = new byte[]
        {
            0x03, // 1字节版本号 默认是03
            0x00, // 1字节 保留值 默认0
            0x00, 0x16, // 2 字节 报文的总长度

            0x11, // 1字节从该字节往后字节个数 十进制是17
            0xE0, // 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字节版本号 默认是03
            0x00, // 1字节 保留值 默认0
            0x00, 0x19, // 2 字节 报文的总长度

            0x02, // 当前字节后的字节数
            0xF0, // PUD类型 数据传输
            0x80, // 最高是十进制128

            0x32, // 协议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)
            {
                // ONE
                int count = tcpClient.GetStream().Read(bytes,0,bytes.Length);
                if (count == 0) return;
                Console.WriteLine(BitConverter.ToString(bytes, 0, count) + "\r\n");

                // TWO
                byte[] 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, // 结构表示 一般默认12
        0x0a, // 往后的字节长度
        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, // 报文总长度36
        0x00,0x27, // 报文总长度39

        // TOPT
        0x02, // 长度
        0xF0, // PDU类型
        0xB0, // 目标引用

        // s7 header
        0x32, // 协议id
        0x01, // 主站开始请求

        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, // 开始写入的地址M14
        0x00,0x3e,0x80, // 开始写入的地址M2000

        0x00,
        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, // 报文总长度36
            0x00,0x27, // 报文总长度39

            // TOPT
            0x02, // 长度
            0xF0, // PDU类型
            0xB0, // 目标引用

            // s7 header
            0x32, // 协议id
            0x01, // 主站开始请求

            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, // 开始写入的地址M14
            0x00,0x3e,0x80, // 开始写入的地址M2000

            0x00,
            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, // 读取操作 为0

            0x04,0x01,
            0x12,0x0a,
            0x10,
            0x02,

            //0x00,0x01, // 读取长度
            0x00,0x04, // M2000 读取四个字节

            0x00,0x00,
            0x83,

            // 0x00, 0x00,0x70
            0x00,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字节版本号 默认是03
            0x00, // 1字节 保留值 默认0
            0x00, 0x16, // 2 字节 报文的总长度

            0x11, // 1字节从该字节往后字节个数 十进制是17
            0xE0, // 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字节版本号 默认是03
            0x00, // 1字节 保留值 默认0
            0x00, 0x19, // 2 字节 报文的总长度

            0x02, // 当前字节后的字节数
            0xF0, // PUD类型 数据传输
            0x80, // 最高是十进制128

            0x32, // 协议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("连接成功");
    }
}

本文部分借鉴于网络,如有侵权请联系删除!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1993422.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

精通C++ STL(四):vector的模拟实现

目录 vector各函数接口总览 vector当中的成员变量介绍 默认成员函数 构造函数1 构造函数2 构造函数3 拷贝构造函数 赋值运算符重载函数 析构函数 迭代器相关函数 begin和end 容量和大小相关函数 size和capacity reserve resize empty 修改容器内容相关函数 push_back po…

pyttsx3自动化脚本经典案例

pyttsx 是一个 Python 库&#xff0c;可以将文本转换为语音。它支持多个语音引擎&#xff0c;并且可以在 Windows、Linux 和 macOS 等不同平台上运行。 pyttsx 可以用来做什么&#xff1f; 将文本转换成语音输出&#xff0c;例如将电子书朗读出来。 在语音助手或者聊天机器人中…

Linux 快速构建LAMP环境

目录 部署方式&#xff1a; 基础环境准备&#xff1a; 1.安装Apache服务 &#xff08;1&#xff09;安装Apache &#xff08;2&#xff09;安装一些Apache的扩展包 2.安装PHP语言 &#xff08;1&#xff09;下载php软件仓库 &#xff08;2&#xff09;指定php安装版本…

Linux-vim编辑器以及权限-04

我们为什么要把这两个单独拎出来讲呢&#xff1f;大家应该需要知道权限是什么,我们的linux是多用户多任务的,所以可能有许多用户可以操作,万一他们把重要的文件删了呢,所以要给他设置权限,而我们的vim编辑器也是非常重要的,用来编辑我们的文本信息,第二章我们讲到了vi,他们两个…

并行训练技术概述

继续开一个新专栏&#xff0c;这里主要收集一些并行训练的相关内容。 文章目录 并行/分布式训练概述为什么需要&#xff1f;如何实施&#xff1f; 并行/分布式训练概述 首先想要说明的是&#xff0c;并行训练和分布式训练的概念其实都能讲&#xff0c;但前者可能更侧重于技术实…

李晨晨的嵌入式学习 DAY21

今天主要也是对昨天学习的进行了补充 一&#xff0c;时间函数 1.time函数 函数原型&#xff1a;time_t time(time_t *tloc); 功能&#xff1a;获取当前时间&#xff08;自1970年1月1日&#xff08;称为Unix纪元或Epoch&#xff09;以来的秒数&#xff0c;即Unix时间戳&#x…

群晖NAS安装Video Station结合内网穿透实现远程访问本地存储的影音文件

文章目录 前言1.使用环境要求&#xff1a;2.下载群晖video station&#xff1a;3.公网访问本地群晖video station&#xff1a;4.公网条件下访问本地群晖video station5.公网条件下使用移动端&#xff08;安卓&#xff0c;ios等系统&#xff09;访问本地群晖video station 前言 …

使用 Arduino 串行绘图仪可视化实时数据

使用 Arduino 串行绘图仪可视化实时数据 Using The Arduino Serial Plotter To Visualize Real Time Data 参考&#xff1a; Arduino Docs: Using the Serial Plotter Tool (IDE v2) Arduino Docs&#xff1a;使用串行绘图仪工具 &#xff08;IDE v2&#xff09; The ADC-10-…

8.1 迭代器的概念与使用:走进 Python 的迭代世界

欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;欢迎订阅相关专栏&#xff1a; 工&#x1f497;重&#x1f497;hao&#x1f497;&#xff1a;野老杂谈 ⭐️ 全网最全IT互联网公司面试宝典&#xff1a;收集整理全网各大IT互联网公司技术、项目、HR面试真题.…

jwt伪造身份组组组合拳艰难通关

前言 现在的攻防演练不再像以往那样一个漏洞直捣黄龙&#xff0c;而是需要各种组合拳才能信手沾来&#xff0c;但是有时候使尽浑身解数也不能诚心如意。 前期信息收集 首先是拿到靶标的清单 访问系统的界面&#xff0c;没有什么能利用的功能点 首先进行目录扫描&#xff0c;…

IO/作业/2024/8/8

1第一题 #include <sys/stat.h> #include <fcntl.h> #include <pthread.h> #include <semaphore.h> #include <wait.h> #include <signal.h> #include <sys/socket.h> #include <arpa/inet.h> #include <sys/socket.h>…

如何禁用外来U盘,保护企业电脑不被“插”一刀?

在数字时代&#xff0c;U盘就像是随身携带的小型保险箱&#xff0c;但它也可能变成一个潜伏的危险品。想象一下&#xff0c;有一天你的同事无意间把一个带有病毒的U盘插进了你的电脑&#xff0c;结果你辛辛苦苦做出来的项目文件就那么瞬间被格式化了…… 为了避免这种情况的发…

CUTLASS 2.x CUTLASS 3.x Intro 学习笔记

CUTLASS GEMM模板中有大量可以调节和设置的模板参数&#xff0c;这些参数的设置会高度影响Kernel性能。这个分享将为大家介绍从2.x到3.x&#xff0c;CUTLASS kernel实现的变化&#xff0c;这些参数的原理和选择的最佳实践。Slides来自BiliBili NVIDIA英伟达频道 上传的《Tensor…

python-热杆上的蚂蚁(赛氪OJ)

[题目描述] 有一个不断升温的杆子&#xff0c;上面有若干个蚂蚁&#xff0c;蚂蚁们需要尽快爬出这个杆子&#xff0c;否则就会因为高温而被烧死。 这里假设每只蚂蚁行走的最大速度是 1cm/s 。 当一只蚂蚁走到杆的尽头时&#xff0c;就会立即从秆上掉落&#xff0c;从而逃离热杆…

两种企业总体业务流程架构模式的比较分析

在之前的关于企业业务流程规划的系列文章中&#xff0c;我们分别对企业业务流程规划的价值、原则&#xff0c;以及如何应用企业的业务流程架构等做了充分的阐述&#xff0c;今天我们将对两种常见的企业总体业务流程架构模式进行比较分析。 我们在辅导企业做业务流程规划和总体…

Python打开JSON/CSV文件的正确方式

前言 我们在使用python的过程中&#xff0c;经常需要它完成一些数据处理的工作&#xff0c;其中尤以json/csv文件为常见。今天&#xff0c;博主针对UnicodeDecodeError异常进行试验&#xff0c;因为这个是新手最容易犯错的地方。 Q&#xff1a;如何应对 UnicodeDecodeError 读…

(el-Time-Picker)操作(不使用 ts):Element-plus 中 TimePicker 组件的使用及输出想要时间格式需求的解决过程

Ⅰ、Element-plus 提供的 TimePicker 时间选择器组件与想要目标情况的对比&#xff1a; 1、Element-plus 提供 TimePicker 组件情况&#xff1a; 其一、Element-ui 自提供的 TimePicker 代码情况为(示例的代码)&#xff1a; // Element-plus 提供的组件代码: <template>…

七、1 ADC模数转换器介绍+有关知识点

目录 1、介绍 &#xff08;1&#xff09;ADC&#xff0c;模拟信号转换为数字信号 &#xff08;2&#xff09;DAC和PWM&#xff0c;数字信号转换为模拟信号 &#xff08;3&#xff09;ADC的两个关键参数 &#xff08;4&#xff09; &#xff08;5&#xff09; &#xff08…

深度学习代码运行RuntimeError:No such operator torchvision::nms解决方案

RuntimeError: No such operator torchvision::nms解决方案 跑代码的时候碰到了"RuntimeError: No such operator torchvision::nms"&#xff0c;找到的资料显示大多是"torch"和"torchvision"版本不匹配&#xff0c;让二者版本一致即可解决。但我…

抱抱脸自动下载模型地址

HuggingFace模型自动下载找保存地址 问题&#xff1a;OSError: Incorrect path_or_model_id: THUDM/cogvlm2-llama3-chat-19B/model.safetensors.index.json. Please provide either the path to a local folder or the repo_id of a model on the Hub. 解决&#xff1a;MODEL_…