Skip to content

Commit

Permalink
重构TPKT和COTP数据包解析
Browse files Browse the repository at this point in the history
  • Loading branch information
nnhy committed Dec 28, 2023
1 parent 2a0e79b commit 504f1aa
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 93 deletions.
28 changes: 0 additions & 28 deletions NewLife.Siemens/Common/InvalidDataException.cs

This file was deleted.

80 changes: 39 additions & 41 deletions NewLife.Siemens/Common/StreamExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,52 +1,50 @@
namespace NewLife.Siemens.Common
namespace NewLife.Siemens.Common;

/// <summary>数据流扩展</summary>
public static class StreamExtensions
{
/// <summary>
/// Extensions for Streams
/// Reads bytes from the stream into the buffer until exactly the requested number of bytes (or EOF) have been read
/// </summary>
public static class StreamExtensions
/// <param name="stream">the Stream to read from</param>
/// <param name="buffer">the buffer to read into</param>
/// <param name="offset">the offset in the buffer to read into</param>
/// <param name="count">the amount of bytes to read into the buffer</param>
/// <returns>returns the amount of read bytes</returns>
public static Int32 ReadExact(this Stream stream, Byte[] buffer, Int32 offset, Int32 count)
{
/// <summary>
/// Reads bytes from the stream into the buffer until exactly the requested number of bytes (or EOF) have been read
/// </summary>
/// <param name="stream">the Stream to read from</param>
/// <param name="buffer">the buffer to read into</param>
/// <param name="offset">the offset in the buffer to read into</param>
/// <param name="count">the amount of bytes to read into the buffer</param>
/// <returns>returns the amount of read bytes</returns>
public static Int32 ReadExact(this Stream stream, Byte[] buffer, Int32 offset, Int32 count)
var read = 0;
Int32 received;
do
{
var read = 0;
Int32 received;
do
{
received = stream.Read(buffer, offset + read, count - read);
read += received;
}
while (read < count && received > 0);

return read;
received = stream.Read(buffer, offset + read, count - read);
read += received;
}
while (read < count && received > 0);

/// <summary>
/// Reads bytes from the stream into the buffer until exactly the requested number of bytes (or EOF) have been read
/// </summary>
/// <param name="stream">the Stream to read from</param>
/// <param name="buffer">the buffer to read into</param>
/// <param name="offset">the offset in the buffer to read into</param>
/// <param name="count">the amount of bytes to read into the buffer</param>
/// <returns>returns the amount of read bytes</returns>
public static async Task<Int32> ReadExactAsync(this Stream stream, Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken)
{
var read = 0;
Int32 received;
do
{
received = await stream.ReadAsync(buffer, offset + read, count - read, cancellationToken).ConfigureAwait(false);
read += received;
}
while (read < count && received > 0);
return read;
}

return read;
/// <summary>
/// Reads bytes from the stream into the buffer until exactly the requested number of bytes (or EOF) have been read
/// </summary>
/// <param name="stream">the Stream to read from</param>
/// <param name="buffer">the buffer to read into</param>
/// <param name="offset">the offset in the buffer to read into</param>
/// <param name="count">the amount of bytes to read into the buffer</param>
/// <param name="cancellationToken"></param>
/// <returns>returns the amount of read bytes</returns>
public static async Task<Int32> ReadExactAsync(this Stream stream, Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken)
{
var read = 0;
Int32 received;
do
{
received = await stream.ReadAsync(buffer, offset + read, count - read, cancellationToken).ConfigureAwait(false);
read += received;
}
while (read < count && received > 0);

return read;
}
}
54 changes: 40 additions & 14 deletions NewLife.Siemens/Protocols/COTP.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,19 @@

namespace NewLife.Siemens.Protocols;

internal class COTP
/// <summary>面向连接的传输协议(Connection-Oriented Transport Protocol)</summary>
public class COTP
{
/// <summary>PDU类型</summary>
public enum PduType : Byte
{
/// <summary>数据帧</summary>
Data = 0xf0,

/// <summary>CR连接请求帧</summary>
ConnectionRequest = 0xe0,

/// <summary>CC连接确认帧</summary>
ConnectionConfirmed = 0xd0
}

Expand All @@ -15,28 +23,44 @@ public enum PduType : Byte
/// </summary>
public class TPDU
{
public TPKT TPkt { get; }
public Byte HeaderLength;
public PduType PDUType;
public Int32 TPDUNumber;
public Byte[] Data;
public Boolean LastDataUnit;
///// <summary>TPKT头</summary>
//public TPKT TPkt { get; }

///// <summary>头部长度</summary>
//public Byte HeaderLength { get; set; }

/// <summary>包类型</summary>
public PduType PDUType { get; set; }

/// <summary>编码</summary>
public Int32 Number { get; set; }

/// <summary>数据</summary>
public Byte[] Data { get; set; }

/// <summary>是否最后数据单元</summary>
public Boolean LastDataUnit { get; set; }

/// <summary>实例化</summary>
/// <param name="tPKT"></param>
public TPDU(TPKT tPKT)
{
TPkt = tPKT;
//TPkt = tPKT;

HeaderLength = tPKT.Data[0]; // Header length excluding this length byte
if (HeaderLength >= 2)
var len = tPKT.Data[0]; // Header length excluding this length byte
if (len >= 2)
{
PDUType = (PduType)tPKT.Data[1];

// 解析不同数据帧
if (PDUType == PduType.Data) //DT Data
{
var flags = tPKT.Data[2];
TPDUNumber = flags & 0x7F;
Number = flags & 0x7F;
LastDataUnit = (flags & 0x80) > 0;
Data = new Byte[tPKT.Data.Length - HeaderLength - 1]; // substract header length byte + header length.
Array.Copy(tPKT.Data, HeaderLength + 1, Data, 0, Data.Length);
Data = new Byte[tPKT.Data.Length - len - 1]; // substract header length byte + header length.
Array.Copy(tPKT.Data, len + 1, Data, 0, Data.Length);

return;
}
//TODO: Handle other PDUTypes
Expand All @@ -59,7 +83,9 @@ public static async Task<TPDU> ReadAsync(Stream stream, CancellationToken cancel
return new TPDU(tpkt);
}

public override String ToString() => $"Length: {HeaderLength} PDUType: {PDUType} TPDUNumber: {TPDUNumber} Last: {LastDataUnit} Segment Data: {BitConverter.ToString(Data)}";
/// <summary>已重载。</summary>
/// <returns></returns>
public override String ToString() => $"[{PDUType}] TPDUNumber: {Number} Last: {LastDataUnit} Segment Data: {BitConverter.ToString(Data)}";
}

/// <summary>
Expand Down
5 changes: 2 additions & 3 deletions NewLife.Siemens/Protocols/S7PLC.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System.Net.Sockets;
using NewLife.Siemens.Common;
using NewLife.Siemens.Models;
using InvalidDataException = NewLife.Siemens.Common.InvalidDataException;

namespace NewLife.Siemens.Protocols;

Expand Down Expand Up @@ -111,7 +110,7 @@ private async Task RequestConnection(Stream stream, CancellationToken cancellati

if (response.PDUType != COTP.PduType.ConnectionConfirmed)
{
throw new InvalidDataException("Connection request was denied", response.TPkt.Data, 1, 0x0d);
throw new InvalidDataException($"Connection request was denied (PDUType={response.PDUType})");
}
}

Expand Down Expand Up @@ -179,7 +178,7 @@ private async Task SetupConnection(Stream stream, CancellationToken cancellation

//Check for S7 Ack Data
if (s7data[1] != 0x03)
throw new InvalidDataException("Error reading Communication Setup response", s7data, 1, 0x03);
throw new InvalidDataException("Error reading Communication Setup response");

if (s7data.Length < 20)
throw new WrongNumberOfBytesException("Not enough data received in response to Communication Setup");
Expand Down
34 changes: 27 additions & 7 deletions NewLife.Siemens/Protocols/TPKT.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,35 @@

namespace NewLife.Siemens.Protocols;

internal class TPKT
/// <summary>ISO Transport Service ontop of the TCP</summary>
/// <remarks>
/// 通过TCP的传输服务。介于TCP和COTP之间。属于传输服务类的协议,它为上层的COTP和下层TCP进行了过渡。
/// 功能为在COTP和TCP之间建立桥梁,其内容包含了上层协议数据包的长度。
/// 一般与COTP一起发送,当作Header段。
/// 我们常用的RDP协议(remote desktop protocol,windows的远程桌面协议)也是基于TPKT的,TPKT的默认TCP端口为102(RDP为3389)
/// </remarks>
public class TPKT
{
public Byte Version;
public Byte Reserved1;
public Int32 Length;
public Byte[] Data;
/// <summary>版本</summary>
public Byte Version { get; set; }

/// <summary>保留</summary>
public Byte Reserved1 { get; set; }

/// <summary>长度</summary>
public UInt16 Length { get; set; }

/// <summary>数据</summary>
public Byte[] Data { get; set; }

/// <summary>实例化</summary>
public TPKT() { }

private TPKT(Byte version, Byte reserved1, Int32 length, Byte[] data)
{
Version = version;
Reserved1 = reserved1;
Length = length;
Length = (UInt16)length;
Data = data;
}

Expand All @@ -24,14 +42,16 @@ private TPKT(Byte version, Byte reserved1, Int32 length, Byte[] data)
/// <returns>Task TPKT Instace</returns>
public static async Task<TPKT> ReadAsync(Stream stream, CancellationToken cancellationToken)
{
// 读取4字节头部
var buf = new Byte[4];
var len = await stream.ReadExactAsync(buf, 0, 4, cancellationToken).ConfigureAwait(false);
if (len < 4) throw new TPKTInvalidException("TPKT is incomplete / invalid");

var version = buf[0];
var reserved1 = buf[1];
var length = buf[2] * 256 + buf[3]; //BigEndian
var length = buf[2] * 256 + buf[3]; // 大端字节序

// 根据长度读取数据
var data = new Byte[length - 4];
len = await stream.ReadExactAsync(data, 0, data.Length, cancellationToken).ConfigureAwait(false);
if (len < data.Length)
Expand Down

0 comments on commit 504f1aa

Please sign in to comment.