zoukankan      html  css  js  c++  java
  • 详解S7源码3-COTP and TPKT

    连接 S7COMM简介 https://www.anquanke.com/post/id/186099

    OSI layer    Protocol
    Application Layer    S7 communication
    Presentation Layer    S7 communication(COTP)
    Session Layer    S7 communication(TPKT)
    Transport Layer    ISO-on-TCP (RFC 1006)
    Network Layer    IP
    Data Link Layer    Ethernet
    Physical Layer    Ethernet

    1,TPKT(默认端口号102)

    tpkt主要为

    version 1byte 0x03 3
    resrved 1byte 0x00 0
    length 2byte 0x16 22

    length为包括这4字节+Payload在内的字节数.(Payload)为data包装后数据.

    1,TPKT类,返回TPKT信息和数据包.根据length-4求得data.就是Payload的数据.(解封).

    internal class TPKT
        {
            public byte Version;
            public byte Reserved1;
            public int Length;
            public byte[] Data;
    
            /// <summary>
            /// Reads a TPKT from the socket
            /// </summary>
            /// <param name="stream">The stream to read from</param>
            /// <returns>TPKT Instance</returns>
            public static TPKT Read(Stream stream)
            {
                var buf = new byte[4];
                int len = stream.Read(buf, 0, 4);
                if (len < 4) throw new TPKTInvalidException("TPKT is incomplete / invalid");
                var pkt = new TPKT
                {
                    Version = buf[0],
                    Reserved1 = buf[1],
                    Length = buf[2] * 256 + buf[3] //BigEndian
                };
                if (pkt.Length > 0)
                {
                    pkt.Data = new byte[pkt.Length - 4];
                    len = stream.Read(pkt.Data, 0, pkt.Length - 4);
                    if (len < pkt.Length - 4)
                        throw new TPKTInvalidException("TPKT is incomplete / invalid");
                }
                return pkt;
            }
    
            /// <summary>
            /// Reads a TPKT from the socket Async
            /// </summary>
            /// <param name="stream">The stream to read from</param>
            /// <returns>Task TPKT Instace</returns>
            public static async Task<TPKT> ReadAsync(Stream stream)
            {
                var buf = new byte[4];
                int len = await stream.ReadAsync(buf, 0, 4);
                if (len < 4) throw new TPKTInvalidException("TPKT is incomplete / invalid");
                var pkt = new TPKT
                {
                    Version = buf[0],
                    Reserved1 = buf[1],
                    Length = buf[2] * 256 + buf[3] //BigEndian
                };
                if (pkt.Length > 0)
                {
                    pkt.Data = new byte[pkt.Length - 4];
                    len = await stream.ReadAsync(pkt.Data, 0, pkt.Length - 4);
                    if (len < pkt.Length - 4) throw new TPKTInvalidException("TPKT is incomplete / invalid");
                }
                return pkt;
            }
    
            public override string ToString()
            {
                return string.Format("Version: {0} Length: {1} Data: {2}",
                    Version,
                    Length,
                    BitConverter.ToString(Data)
                    );
            }
        }


    2,TPDU-----Transport protocol data unit---一个基本数据传输单元(COTP)


    第一个byte--标识为length---不包括本身.标识

    第二给byte---标识为PDUType--分为以下几种


    0x0d 连接确认 0x05 拒绝
    0x0e 连接请求 0xf0 数据包
    0x08 断开请求
    0x0c 断开确认

    当其为0xf0的时候:

    第三个byte---flags:其位7标志是否为最后的TPDU,位0-6标识块NUMBER---TPDU NUMBER.


    从stream---->TPKT---->TPDU---->再从TPDU中读取数据TSDU.(减去4个字节的TPKT包裹+headerLength).

     public class TPDU
            {
                public byte HeaderLength;
                public byte PDUType;
                public int TPDUNumber;
                public byte[] Data;
                public bool LastDataUnit;
    
                public TPDU(TPKT tPKT)
                {
                    var br = new BinaryReader(new MemoryStream(tPKT.Data));
                      
                    HeaderLength = br.ReadByte();
                    if (HeaderLength >= 2)
                    {
                        PDUType = br.ReadByte();
                        if (PDUType == 0xf0) //DT Data
                        {
                            var flags = br.ReadByte();
                            TPDUNumber = flags & 0x7F;
                            LastDataUnit = (flags & 0x80) > 0;
                            Data = br.ReadBytes(tPKT.Length - HeaderLength - 4); //4 = TPKT Size
                            return;
                        }
                        //TODO: Handle other PDUTypes
                    }
                    Data = new byte[0];
                }
    
                /// <summary>
                /// Reads COTP TPDU (Transport protocol data unit) from the network stream
                /// See: https://tools.ietf.org/html/rfc905
                /// </summary>
                /// <param name="stream">The socket to read from</param>
                /// <returns>COTP DPDU instance</returns>
                public static TPDU Read(Stream stream)
                {
                    var tpkt = TPKT.Read(stream);
                    if (tpkt.Length > 0) return new TPDU(tpkt);
                    return null;
                }
    
                /// <summary>
                /// Reads COTP TPDU (Transport protocol data unit) from the network stream
                /// See: https://tools.ietf.org/html/rfc905
                /// </summary>
                /// <param name="stream">The socket to read from</param>
                /// <returns>COTP DPDU instance</returns>
                public static async Task<TPDU> ReadAsync(Stream stream)
                {
                    var tpkt = await TPKT.ReadAsync(stream);
                    if (tpkt.Length > 0) return new TPDU(tpkt);
                    return null;
                }
    
                public override string ToString()
                {
                    return string.Format("Length: {0} PDUType: {1} TPDUNumber: {2} Last: {3} Segment Data: {4}",
                        HeaderLength,
                        PDUType,
                        TPDUNumber,
                        LastDataUnit,
                        BitConverter.ToString(Data)
                        );
                }
    
            }

    3,TSDU---服务数据单元---定义了将连续的TPDU转换为一个TSDU单元.

     public class TSDU
            {
                /// <summary>
                /// Reads the full COTP TSDU (Transport service data unit)
                /// See: https://tools.ietf.org/html/rfc905
                /// </summary>
                /// <param name="stream">The stream to read from</param>
                /// <returns>Data in TSDU</returns>
                public static byte[] Read(Stream stream)
                {
                    var segment = TPDU.Read(stream);
                    if (segment == null) return null;
    
                    var output = new MemoryStream(segment.Data.Length);
                    output.Write(segment.Data, 0, segment.Data.Length);
    
                    while (!segment.LastDataUnit)
                    {
                        segment = TPDU.Read(stream);
                        output.Write(segment.Data, (int)output.Position, segment.Data.Length);
                    }
                    return output.GetBuffer().Take((int)output.Position).ToArray();
                }
    
                /// <summary>
                /// Reads the full COTP TSDU (Transport service data unit)
                /// See: https://tools.ietf.org/html/rfc905
                /// </summary>
                /// <param name="stream">The stream to read from</param>
                /// <returns>Data in TSDU</returns>
                public static async Task<byte[]> ReadAsync(Stream stream)
                {
                    var segment = await TPDU.ReadAsync(stream);
                    if (segment == null) return null;
    
                    var output = new MemoryStream(segment.Data.Length);
                    output.Write(segment.Data, 0, segment.Data.Length);
    
                    while (!segment.LastDataUnit)
                    {
                        segment = await TPDU.ReadAsync(stream);
                        output.Write(segment.Data, (int)output.Position, segment.Data.Length);
                    }
                    return output.GetBuffer().Take((int)output.Position).ToArray();
                }
            }
  • 相关阅读:
    watch 监听路由的改变
    三元运算符
    element-ui动态更改el-table某个单元格字体颜色
    vue 跨域问题导致前端无法携带cookie
    Vue 用checkbox实现两两组合多选且禁用第三个。
    表格数据宽度自适应方案
    echarts细节问题
    es6 解构赋值
    学习笔记-Python基础15-持久化-文件、pickle、shelve
    学习笔记-Python基础14-PyCharm调试
  • 原文地址:https://www.cnblogs.com/frogkiller/p/14678615.html
Copyright © 2011-2022 走看看