連接 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(); } }