@@ -0,0 +1,63 @@ | |||
############################################################################### | |||
# Set default behavior to automatically normalize line endings. | |||
############################################################################### | |||
* text=auto | |||
############################################################################### | |||
# Set default behavior for command prompt diff. | |||
# | |||
# This is need for earlier builds of msysgit that does not have it on by | |||
# default for csharp files. | |||
# Note: This is only used by command line | |||
############################################################################### | |||
#*.cs diff=csharp | |||
############################################################################### | |||
# Set the merge driver for project and solution files | |||
# | |||
# Merging from the command prompt will add diff markers to the files if there | |||
# are conflicts (Merging from VS is not affected by the settings below, in VS | |||
# the diff markers are never inserted). Diff markers may cause the following | |||
# file extensions to fail to load in VS. An alternative would be to treat | |||
# these files as binary and thus will always conflict and require user | |||
# intervention with every merge. To do so, just uncomment the entries below | |||
############################################################################### | |||
#*.sln merge=binary | |||
#*.csproj merge=binary | |||
#*.vbproj merge=binary | |||
#*.vcxproj merge=binary | |||
#*.vcproj merge=binary | |||
#*.dbproj merge=binary | |||
#*.fsproj merge=binary | |||
#*.lsproj merge=binary | |||
#*.wixproj merge=binary | |||
#*.modelproj merge=binary | |||
#*.sqlproj merge=binary | |||
#*.wwaproj merge=binary | |||
############################################################################### | |||
# behavior for image files | |||
# | |||
# image files are treated as binary by default. | |||
############################################################################### | |||
#*.jpg binary | |||
#*.png binary | |||
#*.gif binary | |||
############################################################################### | |||
# diff behavior for common document formats | |||
# | |||
# Convert binary document formats to text before diffing them. This feature | |||
# is only available from the command line. Turn it on by uncommenting the | |||
# entries below. | |||
############################################################################### | |||
#*.doc diff=astextplain | |||
#*.DOC diff=astextplain | |||
#*.docx diff=astextplain | |||
#*.DOCX diff=astextplain | |||
#*.dot diff=astextplain | |||
#*.DOT diff=astextplain | |||
#*.pdf diff=astextplain | |||
#*.PDF diff=astextplain | |||
#*.rtf diff=astextplain | |||
#*.RTF diff=astextplain |
@@ -0,0 +1,30 @@ | |||
using Microsoft.VisualStudio.TestTools.UnitTesting; | |||
using MQTTnet.Core.Serializer; | |||
namespace MQTTnet.Core.Tests | |||
{ | |||
[TestClass] | |||
public class ByteReaderTests | |||
{ | |||
[TestMethod] | |||
public void ByteReader_ReadToEnd() | |||
{ | |||
var reader = new ByteReader(85); | |||
Assert.IsTrue(reader.Read()); | |||
Assert.IsFalse(reader.Read()); | |||
Assert.IsTrue(reader.Read()); | |||
Assert.IsFalse(reader.Read()); | |||
Assert.IsTrue(reader.Read()); | |||
Assert.IsFalse(reader.Read()); | |||
Assert.IsTrue(reader.Read()); | |||
Assert.IsFalse(reader.Read()); | |||
} | |||
[TestMethod] | |||
public void ByteReader_ReadPartial() | |||
{ | |||
var reader = new ByteReader(15); | |||
Assert.AreEqual(3, reader.Read(2)); | |||
} | |||
} | |||
} |
@@ -0,0 +1,51 @@ | |||
using Microsoft.VisualStudio.TestTools.UnitTesting; | |||
using MQTTnet.Core.Serializer; | |||
namespace MQTTnet.Core.Tests | |||
{ | |||
[TestClass] | |||
public class ByteWriterTests | |||
{ | |||
[TestMethod] | |||
public void ByteWriter_WriteMultipleAll() | |||
{ | |||
var b = new ByteWriter(); | |||
Assert.AreEqual(0, b.Value); | |||
b.Write(3, 2); | |||
Assert.AreEqual(3, b.Value); | |||
} | |||
[TestMethod] | |||
public void ByteWriter_WriteMultiplePartial() | |||
{ | |||
var b = new ByteWriter(); | |||
Assert.AreEqual(0, b.Value); | |||
b.Write(255, 2); | |||
Assert.AreEqual(3, b.Value); | |||
} | |||
[TestMethod] | |||
public void ByteWriter_WriteTo0xFF() | |||
{ | |||
var b = new ByteWriter(); | |||
Assert.AreEqual(0, b.Value); | |||
b.Write(true); | |||
Assert.AreEqual(1, b.Value); | |||
b.Write(true); | |||
Assert.AreEqual(3, b.Value); | |||
b.Write(true); | |||
Assert.AreEqual(7, b.Value); | |||
b.Write(true); | |||
Assert.AreEqual(15, b.Value); | |||
b.Write(true); | |||
Assert.AreEqual(31, b.Value); | |||
b.Write(true); | |||
Assert.AreEqual(63, b.Value); | |||
b.Write(true); | |||
Assert.AreEqual(127, b.Value); | |||
b.Write(true); | |||
Assert.AreEqual(255, b.Value); | |||
} | |||
} | |||
} |
@@ -0,0 +1,434 @@ | |||
using System; | |||
using System.IO; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
using Microsoft.VisualStudio.TestTools.UnitTesting; | |||
using MQTTnet.Core.Channel; | |||
using MQTTnet.Core.Client; | |||
using MQTTnet.Core.Packets; | |||
using MQTTnet.Core.Protocol; | |||
using MQTTnet.Core.Serializer; | |||
namespace MQTTnet.Core.Tests | |||
{ | |||
[TestClass] | |||
public class DefaultMqttV311PacketSerializerTests | |||
{ | |||
[TestMethod] | |||
public void SerializeV311_MqttConnectPacket() | |||
{ | |||
var p = new MqttConnectPacket | |||
{ | |||
ClientId = "XYZ", | |||
Password = "PASS", | |||
Username = "USER", | |||
KeepAlivePeriod = 123, | |||
CleanSession = true | |||
}; | |||
SerializeAndCompare(p, "EBsABE1RVFQEwgB7AANYWVoABFVTRVIABFBBU1M="); | |||
} | |||
[TestMethod] | |||
public void SerializeV311_MqttConnectPacketWithWillMessage() | |||
{ | |||
var p = new MqttConnectPacket | |||
{ | |||
ClientId = "XYZ", | |||
Password = "PASS", | |||
Username = "USER", | |||
KeepAlivePeriod = 123, | |||
CleanSession = true, | |||
WillMessage = new MqttApplicationMessage( | |||
"My/last/will", | |||
Encoding.UTF8.GetBytes("Good byte."), | |||
MqttQualityOfServiceLevel.AtLeastOnce, | |||
true) | |||
}; | |||
SerializeAndCompare(p, "EDUABE1RVFQE7gB7AANYWVoADE15L2xhc3Qvd2lsbAAKR29vZCBieXRlLgAEVVNFUgAEUEFTUw=="); | |||
} | |||
[TestMethod] | |||
public void DeserializeV311_MqttConnectPacket() | |||
{ | |||
var p = new MqttConnectPacket | |||
{ | |||
ClientId = "XYZ", | |||
Password = "PASS", | |||
Username = "USER", | |||
KeepAlivePeriod = 123, | |||
CleanSession = true | |||
}; | |||
DeserializeAndCompare(p, "EBsABE1RVFQEwgB7AANYWVoABFVTRVIABFBBU1M="); | |||
} | |||
[TestMethod] | |||
public void DeserializeV311_MqttConnectPacketWithWillMessage() | |||
{ | |||
var p = new MqttConnectPacket | |||
{ | |||
ClientId = "XYZ", | |||
Password = "PASS", | |||
Username = "USER", | |||
KeepAlivePeriod = 123, | |||
CleanSession = true, | |||
WillMessage = new MqttApplicationMessage( | |||
"My/last/will", | |||
Encoding.UTF8.GetBytes("Good byte."), | |||
MqttQualityOfServiceLevel.AtLeastOnce, | |||
true) | |||
}; | |||
DeserializeAndCompare(p, "EDUABE1RVFQE7gB7AANYWVoADE15L2xhc3Qvd2lsbAAKR29vZCBieXRlLgAEVVNFUgAEUEFTUw=="); | |||
} | |||
[TestMethod] | |||
public void SerializeV311_MqttConnAckPacket() | |||
{ | |||
var p = new MqttConnAckPacket | |||
{ | |||
IsSessionPresent = true, | |||
ConnectReturnCode = MqttConnectReturnCode.ConnectionRefusedNotAuthorized | |||
}; | |||
SerializeAndCompare(p, "IAIBBQ=="); | |||
} | |||
[TestMethod] | |||
public void DeserializeV311_MqttConnAckPacket() | |||
{ | |||
var p = new MqttConnAckPacket | |||
{ | |||
IsSessionPresent = true, | |||
ConnectReturnCode = MqttConnectReturnCode.ConnectionRefusedNotAuthorized | |||
}; | |||
DeserializeAndCompare(p, "IAIBBQ=="); | |||
} | |||
[TestMethod] | |||
public void SerializeV311_MqttDisconnectPacket() | |||
{ | |||
SerializeAndCompare(new MqttDisconnectPacket(), "4AA="); | |||
} | |||
[TestMethod] | |||
public void SerializeV311_MqttPingReqPacket() | |||
{ | |||
SerializeAndCompare(new MqttPingReqPacket(), "wAA="); | |||
} | |||
[TestMethod] | |||
public void SerializeV311_MqttPingRespPacket() | |||
{ | |||
SerializeAndCompare(new MqttPingRespPacket(), "0AA="); | |||
} | |||
[TestMethod] | |||
public void SerializeV311_MqttPublishPacket() | |||
{ | |||
var p = new MqttPublishPacket | |||
{ | |||
PacketIdentifier = 123, | |||
Dup = true, | |||
Retain = true, | |||
Payload = Encoding.ASCII.GetBytes("HELLO"), | |||
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce, | |||
Topic = "A/B/C" | |||
}; | |||
SerializeAndCompare(p, "Ow4ABUEvQi9DAHtIRUxMTw=="); | |||
} | |||
[TestMethod] | |||
public void DeserializeV311_MqttPublishPacket() | |||
{ | |||
var p = new MqttPublishPacket | |||
{ | |||
PacketIdentifier = 123, | |||
Dup = true, | |||
Retain = true, | |||
Payload = Encoding.ASCII.GetBytes("HELLO"), | |||
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce, | |||
Topic = "A/B/C" | |||
}; | |||
DeserializeAndCompare(p, "Ow4ABUEvQi9DAHtIRUxMTw=="); | |||
} | |||
[TestMethod] | |||
public void SerializeV311_MqttPubAckPacket() | |||
{ | |||
var p = new MqttPubAckPacket | |||
{ | |||
PacketIdentifier = 123 | |||
}; | |||
SerializeAndCompare(p, "QAIAew=="); | |||
} | |||
[TestMethod] | |||
public void DeserializeV311_MqttPubAckPacket() | |||
{ | |||
var p = new MqttPubAckPacket | |||
{ | |||
PacketIdentifier = 123 | |||
}; | |||
DeserializeAndCompare(p, "QAIAew=="); | |||
} | |||
[TestMethod] | |||
public void SerializeV311_MqttPubRecPacket() | |||
{ | |||
var p = new MqttPubRecPacket | |||
{ | |||
PacketIdentifier = 123 | |||
}; | |||
SerializeAndCompare(p, "UAIAew=="); | |||
} | |||
[TestMethod] | |||
public void DeserializeV311_MqttPubRecPacket() | |||
{ | |||
var p = new MqttPubRecPacket | |||
{ | |||
PacketIdentifier = 123 | |||
}; | |||
DeserializeAndCompare(p, "UAIAew=="); | |||
} | |||
[TestMethod] | |||
public void SerializeV311_MqttPubRelPacket() | |||
{ | |||
var p = new MqttPubRelPacket | |||
{ | |||
PacketIdentifier = 123 | |||
}; | |||
SerializeAndCompare(p, "YgIAew=="); | |||
} | |||
[TestMethod] | |||
public void DeserializeV311_MqttPubRelPacket() | |||
{ | |||
var p = new MqttPubRelPacket | |||
{ | |||
PacketIdentifier = 123 | |||
}; | |||
DeserializeAndCompare(p, "YgIAew=="); | |||
} | |||
[TestMethod] | |||
public void SerializeV311_MqttPubCompPacket() | |||
{ | |||
var p = new MqttPubCompPacket | |||
{ | |||
PacketIdentifier = 123 | |||
}; | |||
SerializeAndCompare(p, "cAIAew=="); | |||
} | |||
[TestMethod] | |||
public void DeserializeV311_MqttPubCompPacket() | |||
{ | |||
var p = new MqttPubCompPacket | |||
{ | |||
PacketIdentifier = 123 | |||
}; | |||
DeserializeAndCompare(p, "cAIAew=="); | |||
} | |||
[TestMethod] | |||
public void SerializeV311_MqttSubscribePacket() | |||
{ | |||
var p = new MqttSubscribePacket | |||
{ | |||
PacketIdentifier = 123 | |||
}; | |||
p.TopicFilters.Add(new TopicFilter("A/B/C", MqttQualityOfServiceLevel.ExactlyOnce)); | |||
p.TopicFilters.Add(new TopicFilter("1/2/3", MqttQualityOfServiceLevel.AtLeastOnce)); | |||
p.TopicFilters.Add(new TopicFilter("x/y/z", MqttQualityOfServiceLevel.AtMostOnce)); | |||
SerializeAndCompare(p, "ghoAewAFQS9CL0MCAAUxLzIvMwEABXgveS96AA=="); | |||
} | |||
[TestMethod] | |||
public void DeserializeV311_MqttSubscribePacket() | |||
{ | |||
var p = new MqttSubscribePacket | |||
{ | |||
PacketIdentifier = 123 | |||
}; | |||
p.TopicFilters.Add(new TopicFilter("A/B/C", MqttQualityOfServiceLevel.ExactlyOnce)); | |||
p.TopicFilters.Add(new TopicFilter("1/2/3", MqttQualityOfServiceLevel.AtLeastOnce)); | |||
p.TopicFilters.Add(new TopicFilter("x/y/z", MqttQualityOfServiceLevel.AtMostOnce)); | |||
DeserializeAndCompare(p, "ghoAewAFQS9CL0MCAAUxLzIvMwEABXgveS96AA=="); | |||
} | |||
[TestMethod] | |||
public void SerializeV311_MqttSubAckPacket() | |||
{ | |||
var p = new MqttSubAckPacket | |||
{ | |||
PacketIdentifier = 123 | |||
}; | |||
p.SubscribeReturnCodes.Add(MqttSubscribeReturnCode.SuccessMaximumQoS0); | |||
p.SubscribeReturnCodes.Add(MqttSubscribeReturnCode.SuccessMaximumQoS1); | |||
p.SubscribeReturnCodes.Add(MqttSubscribeReturnCode.SuccessMaximumQoS2); | |||
p.SubscribeReturnCodes.Add(MqttSubscribeReturnCode.Failure); | |||
SerializeAndCompare(p, "kAYAewABAoA="); | |||
} | |||
[TestMethod] | |||
public void DeserializeV311_MqttSubAckPacket() | |||
{ | |||
var p = new MqttSubAckPacket | |||
{ | |||
PacketIdentifier = 123 | |||
}; | |||
p.SubscribeReturnCodes.Add(MqttSubscribeReturnCode.SuccessMaximumQoS0); | |||
p.SubscribeReturnCodes.Add(MqttSubscribeReturnCode.SuccessMaximumQoS1); | |||
p.SubscribeReturnCodes.Add(MqttSubscribeReturnCode.SuccessMaximumQoS2); | |||
p.SubscribeReturnCodes.Add(MqttSubscribeReturnCode.Failure); | |||
DeserializeAndCompare(p, "kAYAewABAoA="); | |||
} | |||
[TestMethod] | |||
public void SerializeV311_MqttUnsubscribePacket() | |||
{ | |||
var p = new MqttUnsubscribePacket | |||
{ | |||
PacketIdentifier = 123 | |||
}; | |||
p.TopicFilters.Add("A/B/C"); | |||
p.TopicFilters.Add("1/2/3"); | |||
p.TopicFilters.Add("x/y/z"); | |||
SerializeAndCompare(p, "ohcAewAFQS9CL0MABTEvMi8zAAV4L3kveg=="); | |||
} | |||
[TestMethod] | |||
public void DeserializeV311_MqttUnsubscribePacket() | |||
{ | |||
var p = new MqttUnsubscribePacket | |||
{ | |||
PacketIdentifier = 123 | |||
}; | |||
p.TopicFilters.Add("A/B/C"); | |||
p.TopicFilters.Add("1/2/3"); | |||
p.TopicFilters.Add("x/y/z"); | |||
DeserializeAndCompare(p, "ohcAewAFQS9CL0MABTEvMi8zAAV4L3kveg=="); | |||
} | |||
[TestMethod] | |||
public void SerializeV311_MqttUnsubAckPacket() | |||
{ | |||
var p = new MqttUnsubAckPacket | |||
{ | |||
PacketIdentifier = 123 | |||
}; | |||
SerializeAndCompare(p, "sAIAew=="); | |||
} | |||
[TestMethod] | |||
public void DeserializeV311_MqttUnsubAckPacket() | |||
{ | |||
var p = new MqttUnsubAckPacket | |||
{ | |||
PacketIdentifier = 123 | |||
}; | |||
DeserializeAndCompare(p, "sAIAew=="); | |||
} | |||
public class TestChannel : IMqttTransportChannel | |||
{ | |||
private readonly MemoryStream _stream = new MemoryStream(); | |||
public bool IsConnected { get; } = true; | |||
public TestChannel() | |||
{ | |||
} | |||
public TestChannel(byte[] initialData) | |||
{ | |||
_stream.Write(initialData, 0, initialData.Length); | |||
_stream.Position = 0; | |||
} | |||
public async Task ConnectAsync(MqttClientOptions options) | |||
{ | |||
await Task.FromResult(0); | |||
} | |||
public async Task DisconnectAsync() | |||
{ | |||
await Task.FromResult(0); | |||
} | |||
public async Task WriteAsync(byte[] buffer) | |||
{ | |||
await _stream.WriteAsync(buffer, 0, buffer.Length); | |||
} | |||
public async Task ReadAsync(byte[] buffer) | |||
{ | |||
await _stream.ReadAsync(buffer, 0, buffer.Length); | |||
} | |||
public byte[] ToArray() | |||
{ | |||
return _stream.ToArray(); | |||
} | |||
} | |||
private void SerializeAndCompare(MqttBasePacket packet, string expectedBase64Value) | |||
{ | |||
var serializer = new DefaultMqttV311PacketSerializer(); | |||
var channel = new TestChannel(); | |||
serializer.SerializeAsync(packet, channel).Wait(); | |||
var buffer = channel.ToArray(); | |||
Assert.AreEqual(expectedBase64Value, Convert.ToBase64String(buffer)); | |||
} | |||
private void DeserializeAndCompare(MqttBasePacket packet, string expectedBase64Value) | |||
{ | |||
var serializer = new DefaultMqttV311PacketSerializer(); | |||
var channel1 = new TestChannel(); | |||
serializer.SerializeAsync(packet, channel1).Wait(); | |||
var buffer1 = channel1.ToArray(); | |||
var channel2 = new TestChannel(buffer1); | |||
var deserializedPacket = serializer.DeserializeAsync(channel2).Result; | |||
var buffer2 = channel2.ToArray(); | |||
var channel3 = new TestChannel(buffer2); | |||
serializer.SerializeAsync(deserializedPacket, channel3).Wait(); | |||
Assert.AreEqual(expectedBase64Value, Convert.ToBase64String(channel3.ToArray())); | |||
} | |||
} | |||
} |
@@ -0,0 +1,66 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | |||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> | |||
<PropertyGroup> | |||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> | |||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> | |||
<ProjectGuid>{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}</ProjectGuid> | |||
<OutputType>Library</OutputType> | |||
<AppDesignerFolder>Properties</AppDesignerFolder> | |||
<RootNamespace>MQTTnet.Core.Tests</RootNamespace> | |||
<AssemblyName>MQTTnet.Core.Tests</AssemblyName> | |||
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion> | |||
<FileAlignment>512</FileAlignment> | |||
</PropertyGroup> | |||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> | |||
<DebugSymbols>true</DebugSymbols> | |||
<DebugType>full</DebugType> | |||
<Optimize>false</Optimize> | |||
<OutputPath>bin\Debug\</OutputPath> | |||
<DefineConstants>DEBUG;TRACE</DefineConstants> | |||
<ErrorReport>prompt</ErrorReport> | |||
<WarningLevel>4</WarningLevel> | |||
</PropertyGroup> | |||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> | |||
<DebugType>pdbonly</DebugType> | |||
<Optimize>true</Optimize> | |||
<OutputPath>bin\Release\</OutputPath> | |||
<DefineConstants>TRACE</DefineConstants> | |||
<ErrorReport>prompt</ErrorReport> | |||
<WarningLevel>4</WarningLevel> | |||
</PropertyGroup> | |||
<ItemGroup> | |||
<Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" /> | |||
<Reference Include="System" /> | |||
<Reference Include="System.Core" /> | |||
<Reference Include="System.Xml.Linq" /> | |||
<Reference Include="System.Data.DataSetExtensions" /> | |||
<Reference Include="Microsoft.CSharp" /> | |||
<Reference Include="System.Data" /> | |||
<Reference Include="System.Net.Http" /> | |||
<Reference Include="System.Xml" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<Compile Include="ByteReaderTests.cs" /> | |||
<Compile Include="ByteWriterTests.cs" /> | |||
<Compile Include="DefaultMqttV311PacketSerializerTests.cs" /> | |||
<Compile Include="Properties\AssemblyInfo.cs" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<ProjectReference Include="..\MQTT.NET.Core\MQTTnet.Core.csproj"> | |||
<Project>{99C884F3-B4B9-417D-AA92-DC7DD1C4CFEE}</Project> | |||
<Name>MQTTnet.Core</Name> | |||
</ProjectReference> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" /> | |||
</ItemGroup> | |||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> | |||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. | |||
Other similar extension points exist, see Microsoft.Common.targets. | |||
<Target Name="BeforeBuild"> | |||
</Target> | |||
<Target Name="AfterBuild"> | |||
</Target> | |||
--> | |||
</Project> |
@@ -0,0 +1,15 @@ | |||
using System.Reflection; | |||
using System.Runtime.InteropServices; | |||
[assembly: AssemblyTitle("MQTTnet.Core.Tests")] | |||
[assembly: AssemblyDescription("")] | |||
[assembly: AssemblyConfiguration("")] | |||
[assembly: AssemblyCompany("Christian Kratky")] | |||
[assembly: AssemblyProduct("MQTTnet")] | |||
[assembly: AssemblyCopyright("Copyright © Christian Kratky 2015-2017")] | |||
[assembly: AssemblyTrademark("")] | |||
[assembly: AssemblyCulture("")] | |||
[assembly: ComVisible(false)] | |||
[assembly: Guid("a7ff0c91-25de-4ba6-b39e-f54e8dadf1cc")] | |||
[assembly: AssemblyVersion("2.1.0.4")] | |||
[assembly: AssemblyFileVersion("2.1.0.4")] |
@@ -0,0 +1,18 @@ | |||
using System; | |||
using System.Threading.Tasks; | |||
using MQTTnet.Core.Client; | |||
using MQTTnet.Core.Packets; | |||
namespace MQTTnet.Core.Adapter | |||
{ | |||
public interface IMqttAdapter | |||
{ | |||
Task ConnectAsync(MqttClientOptions options, TimeSpan timeout); | |||
Task DisconnectAsync(); | |||
Task SendPacketAsync(MqttBasePacket packet, TimeSpan timeout); | |||
Task<MqttBasePacket> ReceivePacket(); | |||
} | |||
} |
@@ -0,0 +1,72 @@ | |||
using System; | |||
using System.Threading.Tasks; | |||
using MQTTnet.Core.Channel; | |||
using MQTTnet.Core.Client; | |||
using MQTTnet.Core.Diagnostics; | |||
using MQTTnet.Core.Exceptions; | |||
using MQTTnet.Core.Packets; | |||
using MQTTnet.Core.Serializer; | |||
namespace MQTTnet.Core.Adapter | |||
{ | |||
public class MqttChannelAdapter : IMqttAdapter | |||
{ | |||
private readonly IMqttPacketSerializer _serializer; | |||
private readonly IMqttTransportChannel _channel; | |||
public MqttChannelAdapter(IMqttTransportChannel channel, IMqttPacketSerializer serializer) | |||
{ | |||
if (channel == null) throw new ArgumentNullException(nameof(channel)); | |||
if (serializer == null) throw new ArgumentNullException(nameof(serializer)); | |||
_channel = channel; | |||
_serializer = serializer; | |||
} | |||
public async Task ConnectAsync(MqttClientOptions options, TimeSpan timeout) | |||
{ | |||
var task = _channel.ConnectAsync(options); | |||
if (await Task.WhenAny(Task.Delay(timeout), task) != task) | |||
{ | |||
throw new MqttCommunicationTimedOutException(); | |||
} | |||
} | |||
public async Task DisconnectAsync() | |||
{ | |||
await _channel.DisconnectAsync(); | |||
} | |||
public async Task SendPacketAsync(MqttBasePacket packet, TimeSpan timeout) | |||
{ | |||
MqttTrace.Information(nameof(MqttChannelAdapter), $"Sending with timeout {timeout} >>> {packet}"); | |||
bool hasTimeout; | |||
try | |||
{ | |||
var task = _serializer.SerializeAsync(packet, _channel); | |||
hasTimeout = await Task.WhenAny(Task.Delay(timeout), task) != task; | |||
} | |||
catch (Exception exception) | |||
{ | |||
throw new MqttCommunicationException(exception); | |||
} | |||
if (hasTimeout) | |||
{ | |||
throw new MqttCommunicationTimedOutException(); | |||
} | |||
} | |||
public async Task<MqttBasePacket> ReceivePacket() | |||
{ | |||
var mqttPacket = await _serializer.DeserializeAsync(_channel); | |||
if (mqttPacket == null) | |||
{ | |||
throw new MqttProtocolViolationException("Received malformed packet."); | |||
} | |||
return mqttPacket; | |||
} | |||
} | |||
} |
@@ -0,0 +1,16 @@ | |||
using MQTTnet.Core.Exceptions; | |||
using MQTTnet.Core.Protocol; | |||
namespace MQTTnet.Core.Adapter | |||
{ | |||
public class MqttConnectingFailedException : MqttCommunicationException | |||
{ | |||
public MqttConnectingFailedException(MqttConnectReturnCode returnCode) | |||
: base($"Connecting with MQTT server failed ({returnCode}).") | |||
{ | |||
ReturnCode = returnCode; | |||
} | |||
public MqttConnectReturnCode ReturnCode { get; } | |||
} | |||
} |
@@ -0,0 +1,16 @@ | |||
using System.Threading.Tasks; | |||
using MQTTnet.Core.Client; | |||
namespace MQTTnet.Core.Channel | |||
{ | |||
public interface IMqttTransportChannel | |||
{ | |||
Task ConnectAsync(MqttClientOptions options); | |||
Task DisconnectAsync(); | |||
Task WriteAsync(byte[] buffer); | |||
Task ReadAsync(byte[] buffer); | |||
} | |||
} |
@@ -0,0 +1,350 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Threading; | |||
using System.Threading.Tasks; | |||
using MQTTnet.Core.Adapter; | |||
using MQTTnet.Core.Diagnostics; | |||
using MQTTnet.Core.Exceptions; | |||
using MQTTnet.Core.Packets; | |||
using MQTTnet.Core.Protocol; | |||
namespace MQTTnet.Core.Client | |||
{ | |||
public class MqttClient | |||
{ | |||
private readonly Dictionary<ushort, MqttPublishPacket> _pendingExactlyOncePublishPackets = new Dictionary<ushort, MqttPublishPacket>(); | |||
private readonly HashSet<ushort> _processedPublishPackets = new HashSet<ushort>(); | |||
private readonly MqttPacketDispatcher _packetDispatcher = new MqttPacketDispatcher(); | |||
private readonly MqttClientOptions _options; | |||
private readonly IMqttAdapter _adapter; | |||
private int _latestPacketIdentifier; | |||
private CancellationTokenSource _cancellationTokenSource; | |||
public MqttClient(MqttClientOptions options, IMqttAdapter adapter) | |||
{ | |||
if (options == null) throw new ArgumentNullException(nameof(options)); | |||
if (adapter == null) throw new ArgumentNullException(nameof(adapter)); | |||
_options = options; | |||
_adapter = adapter; | |||
} | |||
public event EventHandler Connected; | |||
public event EventHandler Disconnected; | |||
public event EventHandler<MqttApplicationMessageReceivedEventArgs> ApplicationMessageReceived; | |||
public bool IsConnected { get; private set; } | |||
public async Task ConnectAsync(MqttApplicationMessage willApplicationMessage = null) | |||
{ | |||
MqttTrace.Verbose(nameof(MqttClient), "Trying to connect."); | |||
if (IsConnected) | |||
{ | |||
throw new MqttProtocolViolationException("It is not allowed to connect with a server after the connection is established."); | |||
} | |||
var connectPacket = new MqttConnectPacket | |||
{ | |||
ClientId = _options.ClientId, | |||
Username = _options.UserName, | |||
Password = _options.Password, | |||
KeepAlivePeriod = (ushort)_options.KeepAlivePeriod.TotalSeconds, | |||
WillMessage = willApplicationMessage | |||
}; | |||
await _adapter.ConnectAsync(_options, _options.DefaultCommunicationTimeout); | |||
MqttTrace.Verbose(nameof(MqttClient), "Connection with server established."); | |||
_cancellationTokenSource = new CancellationTokenSource(); | |||
_latestPacketIdentifier = 0; | |||
_processedPublishPackets.Clear(); | |||
_packetDispatcher.Reset(); | |||
IsConnected = true; | |||
Task.Factory.StartNew(async () => await ReceivePackets( | |||
_cancellationTokenSource.Token), _cancellationTokenSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default).Forget(); | |||
var response = await SendAndReceiveAsync<MqttConnAckPacket>(connectPacket, p => true); | |||
if (response.ConnectReturnCode != MqttConnectReturnCode.ConnectionAccepted) | |||
{ | |||
throw new MqttConnectingFailedException(response.ConnectReturnCode); | |||
} | |||
if (_options.KeepAlivePeriod != TimeSpan.Zero) | |||
{ | |||
Task.Factory.StartNew(async () => await SendKeepAliveMessagesAsync( | |||
_cancellationTokenSource.Token), _cancellationTokenSource.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default).Forget(); | |||
} | |||
Connected?.Invoke(this, EventArgs.Empty); | |||
} | |||
public async Task DisconnectAsync() | |||
{ | |||
await SendAsync(new MqttDisconnectPacket()); | |||
await DisconnectInternalAsync(); | |||
} | |||
private void ThrowIfNotConnected() | |||
{ | |||
if (!IsConnected) | |||
{ | |||
throw new MqttCommunicationException("The client is not connected."); | |||
} | |||
} | |||
public async Task<IList<MqttSubscribeResult>> SubscribeAsync(IList<TopicFilter> topicFilters) | |||
{ | |||
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters)); | |||
if (!topicFilters.Any()) throw new MqttProtocolViolationException("At least one topic filter must be set [MQTT-3.8.3-3]."); | |||
ThrowIfNotConnected(); | |||
var subscribePacket = new MqttSubscribePacket | |||
{ | |||
PacketIdentifier = GetNewPacketIdentifier(), | |||
TopicFilters = topicFilters | |||
}; | |||
Func<MqttSubAckPacket, bool> packetSelector = p => p.PacketIdentifier == subscribePacket.PacketIdentifier; | |||
var response = await SendAndReceiveAsync(subscribePacket, packetSelector); | |||
if (response.SubscribeReturnCodes.Count != topicFilters.Count) | |||
{ | |||
throw new MqttProtocolViolationException("The return codes are not matching the topic filters [MQTT-3.9.3-1]."); | |||
} | |||
var result = new List<MqttSubscribeResult>(); | |||
for (var i = 0; i < topicFilters.Count; i++) | |||
{ | |||
result.Add(new MqttSubscribeResult(topicFilters[i], response.SubscribeReturnCodes[i])); | |||
} | |||
return result; | |||
} | |||
public async Task Unsubscribe(IList<string> topicFilters) | |||
{ | |||
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters)); | |||
if (!topicFilters.Any()) throw new MqttProtocolViolationException("At least one topic filter must be set [MQTT-3.10.3-2]."); | |||
ThrowIfNotConnected(); | |||
var unsubscribePacket = new MqttUnsubscribePacket | |||
{ | |||
PacketIdentifier = GetNewPacketIdentifier(), | |||
TopicFilters = topicFilters | |||
}; | |||
Func<MqttUnsubAckPacket, bool> packetSelector = p => p.PacketIdentifier == unsubscribePacket.PacketIdentifier; | |||
await SendAndReceiveAsync(unsubscribePacket, packetSelector); | |||
} | |||
public async Task PublishAsync(MqttApplicationMessage applicationMessage) | |||
{ | |||
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage)); | |||
ThrowIfNotConnected(); | |||
var publishPacket = new MqttPublishPacket | |||
{ | |||
Topic = applicationMessage.Topic, | |||
Payload = applicationMessage.Payload, | |||
QualityOfServiceLevel = applicationMessage.QualityOfServiceLevel, | |||
Retain = applicationMessage.Retain, | |||
Dup = false | |||
}; | |||
if (publishPacket.QualityOfServiceLevel != MqttQualityOfServiceLevel.AtMostOnce) | |||
{ | |||
publishPacket.PacketIdentifier = GetNewPacketIdentifier(); | |||
} | |||
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtLeastOnce) | |||
{ | |||
if (!publishPacket.PacketIdentifier.HasValue) throw new InvalidOperationException(); | |||
Func<MqttPubAckPacket, bool> packageSelector = p => p.PacketIdentifier == publishPacket.PacketIdentifier.Value; | |||
await SendAndReceiveAsync(publishPacket, packageSelector); | |||
} | |||
else if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.ExactlyOnce) | |||
{ | |||
if (!publishPacket.PacketIdentifier.HasValue) throw new InvalidOperationException(); | |||
Func<MqttPubRecPacket, bool> packageSelector = p => p.PacketIdentifier == publishPacket.PacketIdentifier.Value; | |||
await SendAndReceiveAsync(publishPacket, packageSelector); | |||
await SendAsync(new MqttPubCompPacket { PacketIdentifier = publishPacket.PacketIdentifier.Value }); | |||
} | |||
} | |||
private async Task DisconnectInternalAsync() | |||
{ | |||
try | |||
{ | |||
await _adapter.DisconnectAsync(); | |||
} | |||
catch | |||
{ | |||
} | |||
finally | |||
{ | |||
_cancellationTokenSource?.Cancel(); | |||
IsConnected = false; | |||
Disconnected?.Invoke(this, EventArgs.Empty); | |||
} | |||
} | |||
private async void ProcessIncomingPacket(MqttBasePacket mqttPacket) | |||
{ | |||
var publishPacket = mqttPacket as MqttPublishPacket; | |||
if (publishPacket != null) | |||
{ | |||
await ProcessReceivedPublishPacket(publishPacket); | |||
return; | |||
} | |||
var pingReqPacket = mqttPacket as MqttPingReqPacket; | |||
if (pingReqPacket != null) | |||
{ | |||
await SendAsync(new MqttPingRespPacket()); | |||
return; | |||
} | |||
var pubRelPacket = mqttPacket as MqttPubRelPacket; | |||
if (pubRelPacket != null) | |||
{ | |||
await ProcessReceivedPubRelPacket(pubRelPacket); | |||
return; | |||
} | |||
_packetDispatcher.Dispatch(mqttPacket); | |||
} | |||
private void FireApplicationMessageReceivedEvent(MqttPublishPacket publishPacket) | |||
{ | |||
if (publishPacket.QualityOfServiceLevel != MqttQualityOfServiceLevel.AtMostOnce) | |||
{ | |||
if (publishPacket.PacketIdentifier == null) throw new InvalidOperationException(); | |||
_processedPublishPackets.Add(publishPacket.PacketIdentifier.Value); | |||
} | |||
var applicationMessage = new MqttApplicationMessage( | |||
publishPacket.Topic, | |||
publishPacket.Payload, | |||
publishPacket.QualityOfServiceLevel, | |||
publishPacket.Retain | |||
); | |||
ApplicationMessageReceived?.Invoke(this, new MqttApplicationMessageReceivedEventArgs(applicationMessage)); | |||
} | |||
private async Task ProcessReceivedPubRelPacket(MqttPubRelPacket pubRelPacket) | |||
{ | |||
var originalPublishPacket = _pendingExactlyOncePublishPackets.Take(pubRelPacket.PacketIdentifier); | |||
if (originalPublishPacket == null) throw new MqttCommunicationException(); | |||
await SendAsync(new MqttPubCompPacket { PacketIdentifier = pubRelPacket.PacketIdentifier }); | |||
FireApplicationMessageReceivedEvent(originalPublishPacket); | |||
} | |||
private async Task ProcessReceivedPublishPacket(MqttPublishPacket publishPacket) | |||
{ | |||
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce) | |||
{ | |||
FireApplicationMessageReceivedEvent(publishPacket); | |||
} | |||
else | |||
{ | |||
if (!publishPacket.PacketIdentifier.HasValue) { throw new InvalidOperationException(); } | |||
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtLeastOnce) | |||
{ | |||
FireApplicationMessageReceivedEvent(publishPacket); | |||
await SendAsync(new MqttPubAckPacket { PacketIdentifier = publishPacket.PacketIdentifier.Value }); | |||
} | |||
else if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.ExactlyOnce) | |||
{ | |||
_pendingExactlyOncePublishPackets.Add(publishPacket.PacketIdentifier.Value, publishPacket); | |||
await SendAsync(new MqttPubRecPacket { PacketIdentifier = publishPacket.PacketIdentifier.Value }); | |||
} | |||
} | |||
} | |||
private async Task SendAsync(MqttBasePacket packet) | |||
{ | |||
await _adapter.SendPacketAsync(packet, _options.DefaultCommunicationTimeout); | |||
} | |||
private async Task<TResponsePacket> SendAndReceiveAsync<TResponsePacket>( | |||
MqttBasePacket requestPacket, Func<TResponsePacket, bool> responsePacketSelector) where TResponsePacket : MqttBasePacket | |||
{ | |||
Func<MqttBasePacket, bool> selector = p => | |||
{ | |||
var p1 = p as TResponsePacket; | |||
return p1 != null && responsePacketSelector(p1); | |||
}; | |||
return (TResponsePacket)await SendAndReceiveAsync(requestPacket, selector); | |||
} | |||
private async Task<MqttBasePacket> SendAndReceiveAsync(MqttBasePacket requestPacket, Func<MqttBasePacket, bool> responsePacketSelector) | |||
{ | |||
var waitTask = _packetDispatcher.WaitForPacketAsync(responsePacketSelector, _options.DefaultCommunicationTimeout); | |||
await _adapter.SendPacketAsync(requestPacket, _options.DefaultCommunicationTimeout); | |||
return await waitTask; | |||
} | |||
private ushort GetNewPacketIdentifier() | |||
{ | |||
return (ushort)Interlocked.Increment(ref _latestPacketIdentifier); | |||
} | |||
private async Task SendKeepAliveMessagesAsync(CancellationToken cancellationToken) | |||
{ | |||
MqttTrace.Information(nameof(MqttClient), "Start sending keep alive packets."); | |||
try | |||
{ | |||
while (!cancellationToken.IsCancellationRequested) | |||
{ | |||
await Task.Delay(_options.KeepAlivePeriod, cancellationToken); | |||
await SendAndReceiveAsync<MqttPingRespPacket>(new MqttPingReqPacket(), p => true); | |||
} | |||
} | |||
catch (Exception exception) | |||
{ | |||
MqttTrace.Error(nameof(MqttClient), exception, "Error while sending keep alive packets."); | |||
} | |||
finally | |||
{ | |||
MqttTrace.Information(nameof(MqttClient), "Stopped sending keep alive packets."); | |||
} | |||
} | |||
private async Task ReceivePackets(CancellationToken cancellationToken) | |||
{ | |||
MqttTrace.Information(nameof(MqttClient), "Start receiving packets."); | |||
try | |||
{ | |||
while (!cancellationToken.IsCancellationRequested) | |||
{ | |||
var mqttPacket = await _adapter.ReceivePacket(); | |||
MqttTrace.Information(nameof(MqttChannelAdapter), $"Received <<< {mqttPacket}"); | |||
Task.Run(() => ProcessIncomingPacket(mqttPacket), cancellationToken).Forget(); | |||
} | |||
} | |||
catch (Exception exception) | |||
{ | |||
MqttTrace.Error(nameof(MqttClient), exception, "Error while receiving packets."); | |||
await DisconnectInternalAsync(); | |||
} | |||
finally | |||
{ | |||
MqttTrace.Information(nameof(MqttClient), "Stopped receiving packets."); | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,21 @@ | |||
using System; | |||
namespace MQTTnet.Core.Client | |||
{ | |||
public class MqttClientOptions | |||
{ | |||
public string Server { get; set; } | |||
public int Port { get; set; } = 1883; | |||
public string UserName { get; set; } | |||
public string Password { get; set; } | |||
public string ClientId { get; set; } = Guid.NewGuid().ToString().Replace("-", string.Empty); | |||
public TimeSpan KeepAlivePeriod { get; set; } = TimeSpan.FromSeconds(5); | |||
public TimeSpan DefaultCommunicationTimeout { get; set; } = TimeSpan.FromSeconds(10); | |||
} | |||
} |
@@ -0,0 +1,7 @@ | |||
namespace MQTTnet.Core.Client | |||
{ | |||
public class MqttClientStatistics | |||
{ | |||
public int SentPackets { get; set; } | |||
} | |||
} |
@@ -0,0 +1,32 @@ | |||
using System; | |||
using System.Threading.Tasks; | |||
using MQTTnet.Core.Packets; | |||
namespace MQTTnet.Core.Client | |||
{ | |||
public class MqttPacketAwaiter | |||
{ | |||
private readonly TaskCompletionSource<MqttBasePacket> _taskCompletionSource = new TaskCompletionSource<MqttBasePacket>(); | |||
private readonly Func<MqttBasePacket, bool> _packetSelector; | |||
public MqttPacketAwaiter(Func<MqttBasePacket, bool> packetSelector) | |||
{ | |||
if (packetSelector == null) throw new ArgumentNullException(nameof(packetSelector)); | |||
_packetSelector = packetSelector; | |||
} | |||
public Task<MqttBasePacket> Task => _taskCompletionSource.Task; | |||
public bool CheckPacket(MqttBasePacket packet) | |||
{ | |||
if (!_packetSelector(packet)) | |||
{ | |||
return false; | |||
} | |||
_taskCompletionSource.SetResult(packet); | |||
return true; | |||
} | |||
} | |||
} |
@@ -0,0 +1,84 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Threading.Tasks; | |||
using MQTTnet.Core.Diagnostics; | |||
using MQTTnet.Core.Exceptions; | |||
using MQTTnet.Core.Packets; | |||
namespace MQTTnet.Core.Client | |||
{ | |||
public class MqttPacketDispatcher | |||
{ | |||
private readonly List<MqttPacketAwaiter> _packetAwaiters = new List<MqttPacketAwaiter>(); | |||
public async Task<MqttBasePacket> WaitForPacketAsync(Func<MqttBasePacket, bool> selector, TimeSpan timeout) | |||
{ | |||
if (selector == null) throw new ArgumentNullException(nameof(selector)); | |||
var waitHandle = new MqttPacketAwaiter(selector); | |||
AddPacketAwaiter(waitHandle); | |||
var hasTimeout = await Task.WhenAny(Task.Delay(timeout), waitHandle.Task) != waitHandle.Task; | |||
RemovePacketAwaiter(waitHandle); | |||
if (hasTimeout) | |||
{ | |||
MqttTrace.Error(nameof(MqttPacketDispatcher), $"Timeout while waiting for packet."); | |||
throw new MqttCommunicationTimedOutException(); | |||
} | |||
return waitHandle.Task.Result; | |||
} | |||
public void Dispatch(MqttBasePacket packet) | |||
{ | |||
if (packet == null) throw new ArgumentNullException(nameof(packet)); | |||
var packetDispatched = false; | |||
foreach (var packetAwaiter in GetPacketAwaiters()) | |||
{ | |||
if (packetAwaiter.CheckPacket(packet)) | |||
{ | |||
packetDispatched = true; | |||
} | |||
} | |||
if (!packetDispatched) | |||
{ | |||
MqttTrace.Warning(nameof(MqttPacketDispatcher), $"Received packet '{packet}' not dispatched."); | |||
} | |||
} | |||
private List<MqttPacketAwaiter> GetPacketAwaiters() | |||
{ | |||
lock (_packetAwaiters) | |||
{ | |||
return new List<MqttPacketAwaiter>(_packetAwaiters); | |||
} | |||
} | |||
private void AddPacketAwaiter(MqttPacketAwaiter packetAwaiter) | |||
{ | |||
lock (_packetAwaiters) | |||
{ | |||
_packetAwaiters.Add(packetAwaiter); | |||
} | |||
} | |||
private void RemovePacketAwaiter(MqttPacketAwaiter packetAwaiter) | |||
{ | |||
lock (_packetAwaiters) | |||
{ | |||
_packetAwaiters.Remove(packetAwaiter); | |||
} | |||
} | |||
public void Reset() | |||
{ | |||
lock (_packetAwaiters) | |||
{ | |||
_packetAwaiters.Clear(); | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,18 @@ | |||
using MQTTnet.Core.Packets; | |||
using MQTTnet.Core.Protocol; | |||
namespace MQTTnet.Core.Client | |||
{ | |||
public class MqttSubscribeResult | |||
{ | |||
public MqttSubscribeResult(TopicFilter topicFilter, MqttSubscribeReturnCode returnCode) | |||
{ | |||
TopicFilter = topicFilter; | |||
ReturnCode = returnCode; | |||
} | |||
public TopicFilter TopicFilter { get; } | |||
public MqttSubscribeReturnCode ReturnCode { get; } | |||
} | |||
} |
@@ -0,0 +1,39 @@ | |||
using System; | |||
namespace MQTTnet.Core.Diagnostics | |||
{ | |||
public static class MqttTrace | |||
{ | |||
public static event EventHandler<MqttTraceMessagePublishedEventArgs> TraceMessagePublished; | |||
public static void Verbose(string source, string message) | |||
{ | |||
TraceMessagePublished?.Invoke(null, new MqttTraceMessagePublishedEventArgs(Environment.CurrentManagedThreadId, source, MqttTraceLevel.Verbose, message, null)); | |||
} | |||
public static void Information(string source, string message) | |||
{ | |||
TraceMessagePublished?.Invoke(null, new MqttTraceMessagePublishedEventArgs(Environment.CurrentManagedThreadId, source, MqttTraceLevel.Information, message, null)); | |||
} | |||
public static void Warning(string source, string message) | |||
{ | |||
TraceMessagePublished?.Invoke(null, new MqttTraceMessagePublishedEventArgs(Environment.CurrentManagedThreadId, source, MqttTraceLevel.Warning, message, null)); | |||
} | |||
public static void Warning(string source, Exception exception, string message) | |||
{ | |||
TraceMessagePublished?.Invoke(null, new MqttTraceMessagePublishedEventArgs(Environment.CurrentManagedThreadId, source, MqttTraceLevel.Warning, message, exception)); | |||
} | |||
public static void Error(string source, string message) | |||
{ | |||
TraceMessagePublished?.Invoke(null, new MqttTraceMessagePublishedEventArgs(Environment.CurrentManagedThreadId, source, MqttTraceLevel.Error, message, null)); | |||
} | |||
public static void Error(string source, Exception exception, string message) | |||
{ | |||
TraceMessagePublished?.Invoke(null, new MqttTraceMessagePublishedEventArgs(Environment.CurrentManagedThreadId, source, MqttTraceLevel.Error, message, exception)); | |||
} | |||
} | |||
} |
@@ -0,0 +1,10 @@ | |||
namespace MQTTnet.Core.Diagnostics | |||
{ | |||
public enum MqttTraceLevel | |||
{ | |||
Verbose, | |||
Information, | |||
Warning, | |||
Error | |||
} | |||
} |
@@ -0,0 +1,26 @@ | |||
using System; | |||
namespace MQTTnet.Core.Diagnostics | |||
{ | |||
public class MqttTraceMessagePublishedEventArgs : EventArgs | |||
{ | |||
public MqttTraceMessagePublishedEventArgs(int threadId, string source, MqttTraceLevel level, string message, Exception exception) | |||
{ | |||
ThreadId = threadId; | |||
Source = source; | |||
Level = level; | |||
Message = message; | |||
Exception = exception; | |||
} | |||
public int ThreadId { get; } | |||
public string Source { get; } | |||
public MqttTraceLevel Level { get; } | |||
public string Message { get; } | |||
public Exception Exception { get; } | |||
} | |||
} |
@@ -0,0 +1,21 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
namespace MQTTnet.Core | |||
{ | |||
public static class DictionaryExtensions | |||
{ | |||
public static TValue Take<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key) | |||
{ | |||
if (dictionary == null) throw new ArgumentNullException(nameof(dictionary)); | |||
TValue value; | |||
if (dictionary.TryGetValue(key, out value)) | |||
{ | |||
dictionary.Remove(key); | |||
} | |||
return value; | |||
} | |||
} | |||
} |
@@ -0,0 +1,21 @@ | |||
using System; | |||
namespace MQTTnet.Core.Exceptions | |||
{ | |||
public class MqttCommunicationException : Exception | |||
{ | |||
public MqttCommunicationException() | |||
{ | |||
} | |||
public MqttCommunicationException(Exception innerException) | |||
: base(innerException.Message, innerException) | |||
{ | |||
} | |||
public MqttCommunicationException(string message) | |||
: base(message) | |||
{ | |||
} | |||
} | |||
} |
@@ -0,0 +1,6 @@ | |||
namespace MQTTnet.Core.Exceptions | |||
{ | |||
public class MqttCommunicationTimedOutException : MqttCommunicationException | |||
{ | |||
} | |||
} |
@@ -0,0 +1,12 @@ | |||
using System; | |||
namespace MQTTnet.Core.Exceptions | |||
{ | |||
public class MqttProtocolViolationException : Exception | |||
{ | |||
public MqttProtocolViolationException(string message) : base(message) | |||
{ | |||
} | |||
} | |||
} |
@@ -0,0 +1,96 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | |||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> | |||
<PropertyGroup> | |||
<MinimumVisualStudioVersion>10.0</MinimumVisualStudioVersion> | |||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> | |||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> | |||
<ProjectGuid>{99C884F3-B4B9-417D-AA92-DC7DD1C4CFEE}</ProjectGuid> | |||
<OutputType>Library</OutputType> | |||
<AppDesignerFolder>Properties</AppDesignerFolder> | |||
<RootNamespace>MQTTnet.Core</RootNamespace> | |||
<AssemblyName>MQTTnet.Core</AssemblyName> | |||
<DefaultLanguage>en-US</DefaultLanguage> | |||
<FileAlignment>512</FileAlignment> | |||
<ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> | |||
<TargetFrameworkProfile>Profile111</TargetFrameworkProfile> | |||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion> | |||
</PropertyGroup> | |||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> | |||
<DebugSymbols>true</DebugSymbols> | |||
<DebugType>full</DebugType> | |||
<Optimize>false</Optimize> | |||
<OutputPath>bin\Debug\</OutputPath> | |||
<DefineConstants>DEBUG;TRACE</DefineConstants> | |||
<ErrorReport>prompt</ErrorReport> | |||
<WarningLevel>4</WarningLevel> | |||
</PropertyGroup> | |||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> | |||
<DebugType>pdbonly</DebugType> | |||
<Optimize>true</Optimize> | |||
<OutputPath>bin\Release\</OutputPath> | |||
<DefineConstants>TRACE</DefineConstants> | |||
<ErrorReport>prompt</ErrorReport> | |||
<WarningLevel>4</WarningLevel> | |||
</PropertyGroup> | |||
<ItemGroup> | |||
<!-- A reference to the entire .NET Framework is automatically included --> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<Compile Include="Adapter\IMqttAdapter.cs" /> | |||
<Compile Include="Adapter\MqttConnectingFailedException.cs" /> | |||
<Compile Include="Adapter\MqttChannelAdapter.cs" /> | |||
<Compile Include="Channel\IMqttTransportChannel.cs" /> | |||
<Compile Include="Client\MqttClientStatistics.cs" /> | |||
<Compile Include="Client\MqttPacketDispatcher.cs" /> | |||
<Compile Include="Client\MqttSubscribeResult.cs" /> | |||
<Compile Include="Client\MqttPacketAwaiter.cs" /> | |||
<Compile Include="Diagnostics\MqttTrace.cs" /> | |||
<Compile Include="Diagnostics\MqttTraceLevel.cs" /> | |||
<Compile Include="Diagnostics\MqttTraceMessagePublishedEventArgs.cs" /> | |||
<Compile Include="DictionaryExtensions.cs" /> | |||
<Compile Include="Exceptions\MqttCommunicationException.cs" /> | |||
<Compile Include="Exceptions\MqttCommunicationTimedOutException.cs" /> | |||
<Compile Include="Exceptions\MqttProtocolViolationException.cs" /> | |||
<Compile Include="MqttApplicationMessageReceivedEventArgs.cs" /> | |||
<Compile Include="Packets\MqttConnAckPacket.cs" /> | |||
<Compile Include="Packets\MqttBasePacket.cs" /> | |||
<Compile Include="Packets\MqttConnectPacket.cs" /> | |||
<Compile Include="Packets\MqttDisconnectPacket.cs" /> | |||
<Compile Include="MqttApplicationMessage.cs" /> | |||
<Compile Include="Client\MqttClient.cs" /> | |||
<Compile Include="Client\MqttClientOptions.cs" /> | |||
<Compile Include="Packets\MqttPingReqPacket.cs" /> | |||
<Compile Include="Packets\MqttPubCompPacket.cs" /> | |||
<Compile Include="Packets\MqttUnsubAckPacket.cs" /> | |||
<Compile Include="Packets\MqttSubAckPacket.cs" /> | |||
<Compile Include="Packets\MqttPubRelPacket.cs" /> | |||
<Compile Include="Packets\MqttPingRespPacket.cs" /> | |||
<Compile Include="Packets\MqttPubAckPacket.cs" /> | |||
<Compile Include="Packets\MqttPublishPacket.cs" /> | |||
<Compile Include="Packets\MqttPubRecPacket.cs" /> | |||
<Compile Include="Packets\MqttUnsubscribe.cs" /> | |||
<Compile Include="Packets\MqttSubscribePacket.cs" /> | |||
<Compile Include="Packets\TopicFilter.cs" /> | |||
<Compile Include="Properties\AssemblyInfo.cs" /> | |||
<Compile Include="Protocol\MqttConnectReturnCode.cs" /> | |||
<Compile Include="Protocol\MqttControlPacketType.cs" /> | |||
<Compile Include="Protocol\MqttQualityOfServiceLevel.cs" /> | |||
<Compile Include="Protocol\MqttSubscribeReturnCode.cs" /> | |||
<Compile Include="Serializer\ByteReader.cs" /> | |||
<Compile Include="Serializer\ByteWriter.cs" /> | |||
<Compile Include="Serializer\DefaultMqttV311PacketSerializer.cs" /> | |||
<Compile Include="Serializer\IMqttPacketSerializer.cs" /> | |||
<Compile Include="Serializer\MqttPacketReader.cs" /> | |||
<Compile Include="Serializer\MqttPacketWriter.cs" /> | |||
<Compile Include="TaskExtensions.cs" /> | |||
</ItemGroup> | |||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" /> | |||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. | |||
Other similar extension points exist, see Microsoft.Common.targets. | |||
<Target Name="BeforeBuild"> | |||
</Target> | |||
<Target Name="AfterBuild"> | |||
</Target> | |||
--> | |||
</Project> |
@@ -0,0 +1,27 @@ | |||
using System; | |||
using MQTTnet.Core.Protocol; | |||
namespace MQTTnet.Core | |||
{ | |||
public class MqttApplicationMessage | |||
{ | |||
public MqttApplicationMessage(string topic, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel, bool retain) | |||
{ | |||
if (topic == null) throw new ArgumentNullException(nameof(topic)); | |||
if (payload == null) throw new ArgumentNullException(nameof(payload)); | |||
Topic = topic; | |||
Payload = payload; | |||
QualityOfServiceLevel = qualityOfServiceLevel; | |||
Retain = retain; | |||
} | |||
public string Topic { get; } | |||
public byte[] Payload { get; } | |||
public MqttQualityOfServiceLevel QualityOfServiceLevel { get; } | |||
public bool Retain { get; } | |||
} | |||
} |
@@ -0,0 +1,16 @@ | |||
using System; | |||
namespace MQTTnet.Core | |||
{ | |||
public class MqttApplicationMessageReceivedEventArgs : EventArgs | |||
{ | |||
public MqttApplicationMessageReceivedEventArgs(MqttApplicationMessage applicationMessage) | |||
{ | |||
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage)); | |||
ApplicationMessage = applicationMessage; | |||
} | |||
public MqttApplicationMessage ApplicationMessage { get; } | |||
} | |||
} |
@@ -0,0 +1,6 @@ | |||
namespace MQTTnet.Core.Packets | |||
{ | |||
public abstract class MqttBasePacket | |||
{ | |||
} | |||
} |
@@ -0,0 +1,16 @@ | |||
using MQTTnet.Core.Protocol; | |||
namespace MQTTnet.Core.Packets | |||
{ | |||
public class MqttConnAckPacket : MqttBasePacket | |||
{ | |||
public bool IsSessionPresent { get; set; } | |||
public MqttConnectReturnCode ConnectReturnCode { get; set; } | |||
public override string ToString() | |||
{ | |||
return $"{nameof(MqttConnAckPacket)}: [ConnectReturnCode={ConnectReturnCode}] [IsSessionPresent={IsSessionPresent}]"; | |||
} | |||
} | |||
} |
@@ -0,0 +1,17 @@ | |||
namespace MQTTnet.Core.Packets | |||
{ | |||
public class MqttConnectPacket: MqttBasePacket | |||
{ | |||
public string ClientId { get; set; } | |||
public string Username { get; set; } | |||
public string Password { get; set; } | |||
public ushort KeepAlivePeriod { get; set; } | |||
public bool CleanSession { get; set; } | |||
public MqttApplicationMessage WillMessage { get; set; } | |||
} | |||
} |
@@ -0,0 +1,6 @@ | |||
namespace MQTTnet.Core.Packets | |||
{ | |||
public class MqttDisconnectPacket : MqttBasePacket | |||
{ | |||
} | |||
} |
@@ -0,0 +1,12 @@ | |||
using System.Xml; | |||
namespace MQTTnet.Core.Packets | |||
{ | |||
public class MqttPingReqPacket : MqttBasePacket | |||
{ | |||
public override string ToString() | |||
{ | |||
return nameof(MqttPingReqPacket); | |||
} | |||
} | |||
} |
@@ -0,0 +1,10 @@ | |||
namespace MQTTnet.Core.Packets | |||
{ | |||
public class MqttPingRespPacket : MqttBasePacket | |||
{ | |||
public override string ToString() | |||
{ | |||
return nameof(MqttPingRespPacket); | |||
} | |||
} | |||
} |
@@ -0,0 +1,7 @@ | |||
namespace MQTTnet.Core.Packets | |||
{ | |||
public class MqttPubAckPacket : MqttBasePacket | |||
{ | |||
public ushort PacketIdentifier { get; set; } | |||
} | |||
} |
@@ -0,0 +1,7 @@ | |||
namespace MQTTnet.Core.Packets | |||
{ | |||
public class MqttPubCompPacket : MqttBasePacket | |||
{ | |||
public ushort PacketIdentifier { get; set; } | |||
} | |||
} |
@@ -0,0 +1,7 @@ | |||
namespace MQTTnet.Core.Packets | |||
{ | |||
public class MqttPubRecPacket : MqttBasePacket | |||
{ | |||
public ushort PacketIdentifier { get; set; } | |||
} | |||
} |
@@ -0,0 +1,7 @@ | |||
namespace MQTTnet.Core.Packets | |||
{ | |||
public class MqttPubRelPacket : MqttBasePacket | |||
{ | |||
public ushort PacketIdentifier { get; set; } | |||
} | |||
} |
@@ -0,0 +1,19 @@ | |||
using MQTTnet.Core.Protocol; | |||
namespace MQTTnet.Core.Packets | |||
{ | |||
public class MqttPublishPacket : MqttBasePacket | |||
{ | |||
public ushort? PacketIdentifier { get; set; } | |||
public bool Retain { get; set; } | |||
public MqttQualityOfServiceLevel QualityOfServiceLevel { get; set; } | |||
public bool Dup { get; set; } | |||
public string Topic { get; set; } | |||
public byte[] Payload { get; set; } | |||
} | |||
} |
@@ -0,0 +1,12 @@ | |||
using System.Collections.Generic; | |||
using MQTTnet.Core.Protocol; | |||
namespace MQTTnet.Core.Packets | |||
{ | |||
public class MqttSubAckPacket : MqttBasePacket | |||
{ | |||
public ushort PacketIdentifier { get; set; } | |||
public List<MqttSubscribeReturnCode> SubscribeReturnCodes { get; set; } = new List<MqttSubscribeReturnCode>(); | |||
} | |||
} |
@@ -0,0 +1,11 @@ | |||
using System.Collections.Generic; | |||
namespace MQTTnet.Core.Packets | |||
{ | |||
public class MqttSubscribePacket : MqttBasePacket | |||
{ | |||
public ushort PacketIdentifier { get; set; } | |||
public IList<TopicFilter> TopicFilters { get; set; } = new List<TopicFilter>(); | |||
} | |||
} |
@@ -0,0 +1,7 @@ | |||
namespace MQTTnet.Core.Packets | |||
{ | |||
public class MqttUnsubAckPacket : MqttBasePacket | |||
{ | |||
public ushort PacketIdentifier { get; set; } | |||
} | |||
} |
@@ -0,0 +1,11 @@ | |||
using System.Collections.Generic; | |||
namespace MQTTnet.Core.Packets | |||
{ | |||
public class MqttUnsubscribePacket : MqttBasePacket | |||
{ | |||
public ushort PacketIdentifier { get; set; } | |||
public IList<string> TopicFilters { get; set; } = new List<string>(); | |||
} | |||
} |
@@ -0,0 +1,17 @@ | |||
using MQTTnet.Core.Protocol; | |||
namespace MQTTnet.Core.Packets | |||
{ | |||
public class TopicFilter | |||
{ | |||
public TopicFilter(string topic, MqttQualityOfServiceLevel qualityOfServiceLevel) | |||
{ | |||
Topic = topic; | |||
QualityOfServiceLevel = qualityOfServiceLevel; | |||
} | |||
public string Topic { get; } | |||
public MqttQualityOfServiceLevel QualityOfServiceLevel { get; } | |||
} | |||
} |
@@ -0,0 +1,15 @@ | |||
using System.Reflection; | |||
using System.Runtime.InteropServices; | |||
[assembly: AssemblyTitle("MQTTnet.Core")] | |||
[assembly: AssemblyDescription("")] | |||
[assembly: AssemblyConfiguration("")] | |||
[assembly: AssemblyCompany("Christian Kratky")] | |||
[assembly: AssemblyProduct("MQTTnet")] | |||
[assembly: AssemblyCopyright("Copyright © Christian Kratky 2016-2017")] | |||
[assembly: AssemblyTrademark("")] | |||
[assembly: AssemblyCulture("")] | |||
[assembly: ComVisible(false)] | |||
[assembly: Guid("0e10d0ea-8e4b-4903-ab9e-93a187d07922")] | |||
[assembly: AssemblyVersion("2.1.0.4")] | |||
[assembly: AssemblyFileVersion("2.1.0.4")] |
@@ -0,0 +1,12 @@ | |||
namespace MQTTnet.Core.Protocol | |||
{ | |||
public enum MqttConnectReturnCode | |||
{ | |||
ConnectionAccepted = 0x00, | |||
ConnectionRefusedUnacceptableProtocolVersion = 0x01, | |||
ConnectionRefusedIdentifierRejected = 0x02, | |||
ConnectionRefusedServerUnavailable = 0x03, | |||
ConnectionRefusedBadUsernameOrPassword = 0x04, | |||
ConnectionRefusedNotAuthorized = 0x05 | |||
} | |||
} |
@@ -0,0 +1,20 @@ | |||
namespace MQTTnet.Core.Protocol | |||
{ | |||
public enum MqttControlPacketType | |||
{ | |||
Connect = 1, | |||
ConnAck = 2, | |||
Publish = 3, | |||
PubAck = 4, | |||
PubRec = 5, | |||
PubRel = 6, | |||
PubComp = 7, | |||
Subscribe = 8, | |||
SubAck = 9, | |||
Unsubscibe = 10, | |||
UnsubAck = 11, | |||
PingReq = 12, | |||
PingResp = 13, | |||
Disconnect = 14 | |||
} | |||
} |
@@ -0,0 +1,9 @@ | |||
namespace MQTTnet.Core.Protocol | |||
{ | |||
public enum MqttQualityOfServiceLevel | |||
{ | |||
AtMostOnce = 0x00, | |||
AtLeastOnce = 0x01, | |||
ExactlyOnce = 0x02 | |||
} | |||
} |
@@ -0,0 +1,10 @@ | |||
namespace MQTTnet.Core.Protocol | |||
{ | |||
public enum MqttSubscribeReturnCode | |||
{ | |||
SuccessMaximumQoS0 = 0x00, | |||
SuccessMaximumQoS1 = 0x01, | |||
SuccessMaximumQoS2 = 0x02, | |||
Failure = 0x80 | |||
} | |||
} |
@@ -0,0 +1,42 @@ | |||
using System; | |||
namespace MQTTnet.Core.Serializer | |||
{ | |||
public class ByteReader | |||
{ | |||
private int _index; | |||
private readonly int _byte; | |||
public ByteReader(byte @byte) | |||
{ | |||
_byte = @byte; | |||
} | |||
public bool Read() | |||
{ | |||
if (_index >= 8) | |||
{ | |||
throw new InvalidOperationException("End of the byte reached."); | |||
} | |||
var result = ((1 << _index) & _byte) > 0; | |||
_index++; | |||
return result; | |||
} | |||
public byte Read(int count) | |||
{ | |||
var result = 0; | |||
for (var i = 0; i < count; i++) | |||
{ | |||
if (Read()) | |||
{ | |||
result |= 1 << i; | |||
} | |||
} | |||
return (byte)result; | |||
} | |||
} | |||
} |
@@ -0,0 +1,36 @@ | |||
using System; | |||
namespace MQTTnet.Core.Serializer | |||
{ | |||
public class ByteWriter | |||
{ | |||
private int _index; | |||
private int _byte; | |||
public byte Value => (byte)_byte; | |||
public void Write(byte @byte, int count) | |||
{ | |||
for (var i = 0; i < count; i++) | |||
{ | |||
var value = ((1 << i) & @byte) > 0; | |||
Write(value); | |||
} | |||
} | |||
public void Write(bool bit) | |||
{ | |||
if (_index >= 8) | |||
{ | |||
throw new InvalidOperationException("End of the byte reached."); | |||
} | |||
if (bit) | |||
{ | |||
_byte |= 1 << _index; | |||
} | |||
_index++; | |||
} | |||
} | |||
} |
@@ -0,0 +1,632 @@ | |||
using System; | |||
using System.Linq; | |||
using System.Net; | |||
using System.Text; | |||
using System.Text.RegularExpressions; | |||
using System.Threading.Tasks; | |||
using MQTTnet.Core.Channel; | |||
using MQTTnet.Core.Exceptions; | |||
using MQTTnet.Core.Packets; | |||
using MQTTnet.Core.Protocol; | |||
namespace MQTTnet.Core.Serializer | |||
{ | |||
public class DefaultMqttV311PacketSerializer : IMqttPacketSerializer | |||
{ | |||
public async Task SerializeAsync(MqttBasePacket packet, IMqttTransportChannel destination) | |||
{ | |||
if (packet == null) throw new ArgumentNullException(nameof(packet)); | |||
if (destination == null) throw new ArgumentNullException(nameof(destination)); | |||
var connectPacket = packet as MqttConnectPacket; | |||
if (connectPacket != null) | |||
{ | |||
await SerializeAsync(connectPacket, destination); | |||
return; | |||
} | |||
var connAckPacket = packet as MqttConnAckPacket; | |||
if (connAckPacket != null) | |||
{ | |||
await SerializeAsync(connAckPacket, destination); | |||
return; | |||
} | |||
var disconnectPacket = packet as MqttDisconnectPacket; | |||
if (disconnectPacket != null) | |||
{ | |||
await SerializeAsync(disconnectPacket, destination); | |||
return; | |||
} | |||
var pingReqPacket = packet as MqttPingReqPacket; | |||
if (pingReqPacket != null) | |||
{ | |||
await SerializeAsync(pingReqPacket, destination); | |||
return; | |||
} | |||
var pingRespPacket = packet as MqttPingRespPacket; | |||
if (pingRespPacket != null) | |||
{ | |||
await SerializeAsync(pingRespPacket, destination); | |||
return; | |||
} | |||
var publishPacket = packet as MqttPublishPacket; | |||
if (publishPacket != null) | |||
{ | |||
await SerializeAsync(publishPacket, destination); | |||
return; | |||
} | |||
var pubAckPacket = packet as MqttPubAckPacket; | |||
if (pubAckPacket != null) | |||
{ | |||
await SerializeAsync(pubAckPacket, destination); | |||
return; | |||
} | |||
var pubRecPacket = packet as MqttPubRecPacket; | |||
if (pubRecPacket != null) | |||
{ | |||
await SerializeAsync(pubRecPacket, destination); | |||
return; | |||
} | |||
var pubRelPacket = packet as MqttPubRelPacket; | |||
if (pubRelPacket != null) | |||
{ | |||
await SerializeAsync(pubRelPacket, destination); | |||
return; | |||
} | |||
var pubCompPacket = packet as MqttPubCompPacket; | |||
if (pubCompPacket != null) | |||
{ | |||
await SerializeAsync(pubCompPacket, destination); | |||
return; | |||
} | |||
var subscribePacket = packet as MqttSubscribePacket; | |||
if (subscribePacket != null) | |||
{ | |||
await SerializeAsync(subscribePacket, destination); | |||
return; | |||
} | |||
var subAckPacket = packet as MqttSubAckPacket; | |||
if (subAckPacket != null) | |||
{ | |||
await SerializeAsync(subAckPacket, destination); | |||
return; | |||
} | |||
var unsubscribePacket = packet as MqttUnsubscribePacket; | |||
if (unsubscribePacket != null) | |||
{ | |||
await SerializeAsync(unsubscribePacket, destination); | |||
return; | |||
} | |||
var unsubAckPacket = packet as MqttUnsubAckPacket; | |||
if (unsubAckPacket != null) | |||
{ | |||
await SerializeAsync(unsubAckPacket, destination); | |||
return; | |||
} | |||
throw new MqttProtocolViolationException("Packet type invalid."); | |||
} | |||
public async Task<MqttBasePacket> DeserializeAsync(IMqttTransportChannel source) | |||
{ | |||
using (var mqttPacketReader = new MqttPacketReader(source)) | |||
{ | |||
await mqttPacketReader.ReadToEndAsync(); | |||
switch (mqttPacketReader.ControlPacketType) | |||
{ | |||
case MqttControlPacketType.Connect: | |||
{ | |||
return await DeserializeConnectAsync(mqttPacketReader); | |||
} | |||
case MqttControlPacketType.ConnAck: | |||
{ | |||
return await DeserializeConnAck(mqttPacketReader); | |||
} | |||
case MqttControlPacketType.Disconnect: | |||
{ | |||
return new MqttDisconnectPacket(); | |||
} | |||
case MqttControlPacketType.Publish: | |||
{ | |||
return await DeserializePublishAsync(mqttPacketReader); | |||
} | |||
case MqttControlPacketType.PubAck: | |||
{ | |||
return new MqttPubAckPacket | |||
{ | |||
PacketIdentifier = await mqttPacketReader.ReadRemainingDataUShortAsync() | |||
}; | |||
} | |||
case MqttControlPacketType.PubRec: | |||
{ | |||
return new MqttPubRecPacket | |||
{ | |||
PacketIdentifier = await mqttPacketReader.ReadRemainingDataUShortAsync() | |||
}; | |||
} | |||
case MqttControlPacketType.PubRel: | |||
{ | |||
return new MqttPubRelPacket | |||
{ | |||
PacketIdentifier = await mqttPacketReader.ReadRemainingDataUShortAsync() | |||
}; | |||
} | |||
case MqttControlPacketType.PubComp: | |||
{ | |||
return new MqttPubCompPacket | |||
{ | |||
PacketIdentifier = await mqttPacketReader.ReadRemainingDataUShortAsync() | |||
}; | |||
} | |||
case MqttControlPacketType.PingReq: | |||
{ | |||
return new MqttPingReqPacket(); | |||
} | |||
case MqttControlPacketType.PingResp: | |||
{ | |||
return new MqttPingRespPacket(); | |||
} | |||
case MqttControlPacketType.Subscribe: | |||
{ | |||
return await DeserializeSubscribeAsync(mqttPacketReader); | |||
} | |||
case MqttControlPacketType.SubAck: | |||
{ | |||
return await DeserializeSubAck(mqttPacketReader); | |||
} | |||
case MqttControlPacketType.Unsubscibe: | |||
{ | |||
return await DeserializeUnsubscribeAsync(mqttPacketReader); | |||
} | |||
case MqttControlPacketType.UnsubAck: | |||
{ | |||
return new MqttUnsubAckPacket | |||
{ | |||
PacketIdentifier = await mqttPacketReader.ReadRemainingDataUShortAsync() | |||
}; | |||
} | |||
} | |||
} | |||
throw new ProtocolViolationException(); | |||
} | |||
private async Task<MqttBasePacket> DeserializeUnsubscribeAsync(MqttPacketReader reader) | |||
{ | |||
var packet = new MqttUnsubscribePacket | |||
{ | |||
PacketIdentifier = await reader.ReadRemainingDataUShortAsync(), | |||
}; | |||
while (!reader.EndOfRemainingData) | |||
{ | |||
packet.TopicFilters.Add(await reader.ReadRemainingDataStringWithLengthPrefixAsync()); | |||
} | |||
return packet; | |||
} | |||
private async Task<MqttBasePacket> DeserializeSubscribeAsync(MqttPacketReader reader) | |||
{ | |||
var packet = new MqttSubscribePacket | |||
{ | |||
PacketIdentifier = await reader.ReadRemainingDataUShortAsync(), | |||
}; | |||
while (!reader.EndOfRemainingData) | |||
{ | |||
packet.TopicFilters.Add(new TopicFilter( | |||
await reader.ReadRemainingDataStringWithLengthPrefixAsync(), | |||
(MqttQualityOfServiceLevel)await reader.ReadRemainingDataByteAsync())); | |||
} | |||
return packet; | |||
} | |||
private async Task<MqttBasePacket> DeserializePublishAsync(MqttPacketReader reader) | |||
{ | |||
var fixedHeader = new ByteReader(reader.FixedHeader); | |||
var retain = fixedHeader.Read(); | |||
var qualityOfServiceLevel = (MqttQualityOfServiceLevel)fixedHeader.Read(2); | |||
var dup = fixedHeader.Read(); | |||
var topic = await reader.ReadRemainingDataStringWithLengthPrefixAsync(); | |||
ushort? packetIdentifier = null; | |||
if (qualityOfServiceLevel > 0) | |||
{ | |||
packetIdentifier = await reader.ReadRemainingDataUShortAsync(); | |||
} | |||
var packet = new MqttPublishPacket | |||
{ | |||
Retain = retain, | |||
QualityOfServiceLevel = qualityOfServiceLevel, | |||
Dup = dup, | |||
Topic = topic, | |||
Payload = await reader.ReadRemainingDataAsync(), | |||
PacketIdentifier = packetIdentifier | |||
}; | |||
return packet; | |||
} | |||
private async Task<MqttBasePacket> DeserializeConnectAsync(MqttPacketReader reader) | |||
{ | |||
var packet = new MqttConnectPacket(); | |||
await reader.ReadRemainingDataByteAsync(); | |||
await reader.ReadRemainingDataByteAsync(); | |||
var protocolName = await reader.ReadRemainingDataAsync(4); | |||
if (Encoding.UTF8.GetString(protocolName, 0, protocolName.Length) != "MQTT") | |||
{ | |||
throw new ProtocolViolationException("Protocol name is not 'MQTT'."); | |||
} | |||
var protocolLevel = await reader.ReadRemainingDataByteAsync(); | |||
var connectFlags = await reader.ReadRemainingDataByteAsync(); | |||
var connectFlagsReader = new ByteReader(connectFlags); | |||
connectFlagsReader.Read(); // Reserved. | |||
packet.CleanSession = connectFlagsReader.Read(); | |||
var willFlag = connectFlagsReader.Read(); | |||
var willQoS = connectFlagsReader.Read(2); | |||
var willRetain = connectFlagsReader.Read(); | |||
var passwordFlag = connectFlagsReader.Read(); | |||
var usernameFlag = connectFlagsReader.Read(); | |||
packet.KeepAlivePeriod = await reader.ReadRemainingDataUShortAsync(); | |||
packet.ClientId = await reader.ReadRemainingDataStringWithLengthPrefixAsync(); | |||
if (willFlag) | |||
{ | |||
packet.WillMessage = new MqttApplicationMessage( | |||
await reader.ReadRemainingDataStringWithLengthPrefixAsync(), | |||
await reader.ReadRemainingDataWithLengthPrefixAsync(), | |||
(MqttQualityOfServiceLevel)willQoS, | |||
willRetain); | |||
} | |||
if (usernameFlag) | |||
{ | |||
packet.Username = await reader.ReadRemainingDataStringWithLengthPrefixAsync(); | |||
} | |||
if (passwordFlag) | |||
{ | |||
packet.Password = await reader.ReadRemainingDataStringWithLengthPrefixAsync(); | |||
} | |||
ValidateConnectPacket(packet); | |||
return packet; | |||
} | |||
private async Task<MqttBasePacket> DeserializeSubAck(MqttPacketReader reader) | |||
{ | |||
var packet = new MqttSubAckPacket | |||
{ | |||
PacketIdentifier = await reader.ReadRemainingDataUShortAsync() | |||
}; | |||
while (!reader.EndOfRemainingData) | |||
{ | |||
packet.SubscribeReturnCodes.Add((MqttSubscribeReturnCode)await reader.ReadRemainingDataByteAsync()); | |||
} | |||
return packet; | |||
} | |||
private async Task<MqttBasePacket> DeserializeConnAck(MqttPacketReader reader) | |||
{ | |||
var variableHeader1 = await reader.ReadRemainingDataByteAsync(); | |||
var variableHeader2 = await reader.ReadRemainingDataByteAsync(); | |||
var packet = new MqttConnAckPacket | |||
{ | |||
IsSessionPresent = new ByteReader(variableHeader1).Read(), | |||
ConnectReturnCode = (MqttConnectReturnCode)variableHeader2 | |||
}; | |||
return packet; | |||
} | |||
private void ValidateConnectPacket(MqttConnectPacket packet) | |||
{ | |||
if (string.IsNullOrEmpty(packet.ClientId) && !packet.CleanSession) | |||
{ | |||
throw new ProtocolViolationException("CleanSession must be set if ClientId is empty [MQTT-3.1.3-7]."); | |||
} | |||
if (!string.IsNullOrEmpty(packet.ClientId) && !Regex.IsMatch(packet.ClientId, "^[a-zA-Z0-9]*$")) | |||
{ | |||
throw new ProtocolViolationException("ClientId contains invalid characters [MQTT-3.1.3-5]."); | |||
} | |||
} | |||
private void ValidatePublishPacket(MqttPublishPacket packet) | |||
{ | |||
if (packet.QualityOfServiceLevel == 0 && packet.Dup) | |||
{ | |||
throw new ProtocolViolationException("Dup flag must be false for QoS 0 packets [MQTT-3.3.1-2]."); | |||
} | |||
} | |||
private async Task SerializeAsync(MqttConnectPacket packet, IMqttTransportChannel destination) | |||
{ | |||
ValidateConnectPacket(packet); | |||
using (var output = new MqttPacketWriter()) | |||
{ | |||
// Write variable header | |||
output.Write(0x00); // 3.1.2.1 Protocol Name | |||
output.Write(0x04); // "" | |||
output.Write('M'); | |||
output.Write('Q'); | |||
output.Write('T'); | |||
output.Write('T'); | |||
output.Write(0x04); // 3.1.2.2 Protocol Level | |||
var connectFlags = new ByteWriter(); // 3.1.2.3 Connect Flags | |||
connectFlags.Write(false); // Reserved | |||
connectFlags.Write(packet.CleanSession); | |||
connectFlags.Write(packet.WillMessage != null); | |||
if (packet.WillMessage != null) | |||
{ | |||
connectFlags.Write((byte)packet.WillMessage.QualityOfServiceLevel, 2); | |||
connectFlags.Write(packet.WillMessage.Retain); | |||
} | |||
else | |||
{ | |||
connectFlags.Write(0, 2); | |||
connectFlags.Write(false); | |||
} | |||
connectFlags.Write(packet.Password != null); | |||
connectFlags.Write(packet.Username != null); | |||
output.Write(connectFlags); | |||
output.Write(packet.KeepAlivePeriod); | |||
output.WriteWithLengthPrefix(packet.ClientId); | |||
if (packet.WillMessage != null) | |||
{ | |||
output.WriteWithLengthPrefix(packet.WillMessage.Topic); | |||
output.WriteWithLengthPrefix(packet.WillMessage.Payload); | |||
} | |||
if (packet.Username != null) | |||
{ | |||
output.WriteWithLengthPrefix(packet.Username); | |||
} | |||
if (packet.Password != null) | |||
{ | |||
output.WriteWithLengthPrefix(packet.Password); | |||
} | |||
output.InjectFixedHeader(MqttControlPacketType.Connect); | |||
await output.WriteToAsync(destination); | |||
} | |||
} | |||
private async Task SerializeAsync(MqttConnAckPacket packet, IMqttTransportChannel destination) | |||
{ | |||
using (var output = new MqttPacketWriter()) | |||
{ | |||
var connectAcknowledgeFlags = new ByteWriter(); | |||
connectAcknowledgeFlags.Write(packet.IsSessionPresent); | |||
output.Write(connectAcknowledgeFlags); | |||
output.Write((byte)packet.ConnectReturnCode); | |||
output.InjectFixedHeader(MqttControlPacketType.ConnAck); | |||
await output.WriteToAsync(destination); | |||
} | |||
} | |||
private async Task SerializeAsync(MqttDisconnectPacket packet, IMqttTransportChannel destination) | |||
{ | |||
await SerializeEmptyPacketAsync(MqttControlPacketType.Disconnect, destination); | |||
} | |||
private async Task SerializeAsync(MqttPingReqPacket packet, IMqttTransportChannel destination) | |||
{ | |||
await SerializeEmptyPacketAsync(MqttControlPacketType.PingReq, destination); | |||
} | |||
private async Task SerializeAsync(MqttPingRespPacket packet, IMqttTransportChannel destination) | |||
{ | |||
await SerializeEmptyPacketAsync(MqttControlPacketType.PingResp, destination); | |||
} | |||
private async Task SerializeAsync(MqttPublishPacket packet, IMqttTransportChannel destination) | |||
{ | |||
ValidatePublishPacket(packet); | |||
using (var output = new MqttPacketWriter()) | |||
{ | |||
output.WriteWithLengthPrefix(packet.Topic); | |||
if (packet.QualityOfServiceLevel > 0) | |||
{ | |||
if (!packet.PacketIdentifier.HasValue) | |||
{ | |||
throw new MqttProtocolViolationException("Packet identifier must be set if QoS > 0 [MQTT-2.3.1-1]."); | |||
} | |||
output.Write(packet.PacketIdentifier.Value); | |||
} | |||
else | |||
{ | |||
if (packet.PacketIdentifier.HasValue) | |||
{ | |||
throw new MqttProtocolViolationException("Packet identifier must be empty if QoS == 0 [MQTT-2.3.1-5]."); | |||
} | |||
} | |||
if (packet.Payload?.Length > 0) | |||
{ | |||
output.Write(packet.Payload); | |||
} | |||
var fixedHeader = new ByteWriter(); | |||
fixedHeader.Write(packet.Retain); | |||
fixedHeader.Write((byte)packet.QualityOfServiceLevel, 2); | |||
fixedHeader.Write(packet.Dup); | |||
output.InjectFixedHeader(MqttControlPacketType.Publish, fixedHeader.Value); | |||
await output.WriteToAsync(destination); | |||
} | |||
} | |||
private async Task SerializeAsync(MqttPubAckPacket packet, IMqttTransportChannel destination) | |||
{ | |||
using (var output = new MqttPacketWriter()) | |||
{ | |||
output.Write(packet.PacketIdentifier); | |||
output.InjectFixedHeader(MqttControlPacketType.PubAck); | |||
await output.WriteToAsync(destination); | |||
} | |||
} | |||
private async Task SerializeAsync(MqttPubRecPacket packet, IMqttTransportChannel destination) | |||
{ | |||
using (var output = new MqttPacketWriter()) | |||
{ | |||
output.Write(packet.PacketIdentifier); | |||
output.InjectFixedHeader(MqttControlPacketType.PubRec); | |||
await output.WriteToAsync(destination); | |||
} | |||
} | |||
private async Task SerializeAsync(MqttPubRelPacket packet, IMqttTransportChannel destination) | |||
{ | |||
using (var output = new MqttPacketWriter()) | |||
{ | |||
output.Write(packet.PacketIdentifier); | |||
output.InjectFixedHeader(MqttControlPacketType.PubRel, 0x02); | |||
await output.WriteToAsync(destination); | |||
} | |||
} | |||
private async Task SerializeAsync(MqttPubCompPacket packet, IMqttTransportChannel destination) | |||
{ | |||
using (var output = new MqttPacketWriter()) | |||
{ | |||
output.Write(packet.PacketIdentifier); | |||
output.InjectFixedHeader(MqttControlPacketType.PubComp); | |||
await output.WriteToAsync(destination); | |||
} | |||
} | |||
private async Task SerializeAsync(MqttSubscribePacket packet, IMqttTransportChannel destination) | |||
{ | |||
using (var output = new MqttPacketWriter()) | |||
{ | |||
output.Write(packet.PacketIdentifier); | |||
if (packet.TopicFilters?.Any() == true) | |||
{ | |||
foreach (var topicFilter in packet.TopicFilters) | |||
{ | |||
output.WriteWithLengthPrefix(topicFilter.Topic); | |||
output.Write((byte)topicFilter.QualityOfServiceLevel); | |||
} | |||
} | |||
output.InjectFixedHeader(MqttControlPacketType.Subscribe, 0x02); | |||
await output.WriteToAsync(destination); | |||
} | |||
} | |||
private async Task SerializeAsync(MqttSubAckPacket packet, IMqttTransportChannel destination) | |||
{ | |||
using (var output = new MqttPacketWriter()) | |||
{ | |||
output.Write(packet.PacketIdentifier); | |||
if (packet.SubscribeReturnCodes?.Any() == true) | |||
{ | |||
foreach (var packetSubscribeReturnCode in packet.SubscribeReturnCodes) | |||
{ | |||
output.Write((byte)packetSubscribeReturnCode); | |||
} | |||
} | |||
output.InjectFixedHeader(MqttControlPacketType.SubAck); | |||
await output.WriteToAsync(destination); | |||
} | |||
} | |||
private async Task SerializeAsync(MqttUnsubscribePacket packet, IMqttTransportChannel destination) | |||
{ | |||
using (var output = new MqttPacketWriter()) | |||
{ | |||
output.Write(packet.PacketIdentifier); | |||
if (packet.TopicFilters?.Any() == true) | |||
{ | |||
foreach (var topicFilter in packet.TopicFilters) | |||
{ | |||
output.WriteWithLengthPrefix(topicFilter); | |||
} | |||
} | |||
output.InjectFixedHeader(MqttControlPacketType.Unsubscibe, 0x02); | |||
await output.WriteToAsync(destination); | |||
} | |||
} | |||
private async Task SerializeAsync(MqttUnsubAckPacket packet, IMqttTransportChannel destination) | |||
{ | |||
using (var output = new MqttPacketWriter()) | |||
{ | |||
output.Write(packet.PacketIdentifier); | |||
output.InjectFixedHeader(MqttControlPacketType.UnsubAck); | |||
await output.WriteToAsync(destination); | |||
} | |||
} | |||
private async Task SerializeEmptyPacketAsync(MqttControlPacketType type, IMqttTransportChannel destination) | |||
{ | |||
using (var output = new MqttPacketWriter()) | |||
{ | |||
output.InjectFixedHeader(type); | |||
await output.WriteToAsync(destination); | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,13 @@ | |||
using System.Threading.Tasks; | |||
using MQTTnet.Core.Channel; | |||
using MQTTnet.Core.Packets; | |||
namespace MQTTnet.Core.Serializer | |||
{ | |||
public interface IMqttPacketSerializer | |||
{ | |||
Task SerializeAsync(MqttBasePacket mqttPacket, IMqttTransportChannel destination); | |||
Task<MqttBasePacket> DeserializeAsync(IMqttTransportChannel source); | |||
} | |||
} |
@@ -0,0 +1,130 @@ | |||
using System; | |||
using System.Diagnostics; | |||
using System.IO; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
using MQTTnet.Core.Channel; | |||
using MQTTnet.Core.Exceptions; | |||
using MQTTnet.Core.Protocol; | |||
namespace MQTTnet.Core.Serializer | |||
{ | |||
public sealed class MqttPacketReader : IDisposable | |||
{ | |||
private readonly MemoryStream _remainingData = new MemoryStream(); | |||
private readonly IMqttTransportChannel _source; | |||
public MqttPacketReader(IMqttTransportChannel source) | |||
{ | |||
if (source == null) throw new ArgumentNullException(nameof(source)); | |||
_source = source; | |||
} | |||
public MqttControlPacketType ControlPacketType { get; private set; } | |||
public byte FixedHeader { get; private set; } | |||
public int RemainingLength { get; private set; } | |||
public bool EndOfRemainingData => _remainingData.Position == _remainingData.Length; | |||
public async Task ReadToEndAsync() | |||
{ | |||
await ReadFixedHeaderAsync(); | |||
await ReadRemainingLengthAsync(); | |||
if (RemainingLength == 0) | |||
{ | |||
return; | |||
} | |||
var buffer = new byte[RemainingLength]; | |||
await _source.ReadAsync(buffer); | |||
_remainingData.Write(buffer, 0, buffer.Length); | |||
_remainingData.Position = 0; | |||
} | |||
private async Task ReadFixedHeaderAsync() | |||
{ | |||
FixedHeader = await ReadStreamByteAsync(); | |||
var byteReader = new ByteReader(FixedHeader); | |||
byteReader.Read(4); | |||
ControlPacketType = (MqttControlPacketType)byteReader.Read(4); | |||
} | |||
private async Task<byte> ReadStreamByteAsync() | |||
{ | |||
var buffer = new byte[1]; | |||
await _source.ReadAsync(buffer); | |||
return buffer[0]; | |||
} | |||
private async Task ReadRemainingLengthAsync() | |||
{ | |||
// Alorithm taken from http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html. | |||
var multiplier = 1; | |||
var value = 0; | |||
byte encodedByte; | |||
do | |||
{ | |||
encodedByte = await ReadStreamByteAsync(); | |||
value += (encodedByte & 127) * multiplier; | |||
multiplier *= 128; | |||
if (multiplier > 128 * 128 * 128) | |||
{ | |||
throw new MqttProtocolViolationException("Remaining length is ivalid."); | |||
} | |||
} while ((encodedByte & 128) != 0); | |||
RemainingLength = value; | |||
} | |||
public async Task<byte> ReadRemainingDataByteAsync() | |||
{ | |||
return (await ReadRemainingDataAsync(1))[0]; | |||
} | |||
public async Task<ushort> ReadRemainingDataUShortAsync() | |||
{ | |||
var buffer = await ReadRemainingDataAsync(2); | |||
var temp = buffer[0]; | |||
buffer[0] = buffer[1]; | |||
buffer[1] = temp; | |||
return BitConverter.ToUInt16(buffer, 0); | |||
} | |||
public async Task<string> ReadRemainingDataStringWithLengthPrefixAsync() | |||
{ | |||
var buffer = await ReadRemainingDataWithLengthPrefixAsync(); | |||
return Encoding.UTF8.GetString(buffer, 0, buffer.Length); | |||
} | |||
public async Task<byte[]> ReadRemainingDataWithLengthPrefixAsync() | |||
{ | |||
var length = await ReadRemainingDataUShortAsync(); | |||
return await ReadRemainingDataAsync(length); | |||
} | |||
public async Task<byte[]> ReadRemainingDataAsync() | |||
{ | |||
return await ReadRemainingDataAsync(RemainingLength - (int)_remainingData.Position); | |||
} | |||
public async Task<byte[]> ReadRemainingDataAsync(int length) | |||
{ | |||
var buffer = new byte[length]; | |||
await _remainingData.ReadAsync(buffer, 0, buffer.Length); | |||
return buffer; | |||
} | |||
public void Dispose() | |||
{ | |||
_remainingData?.Dispose(); | |||
} | |||
} | |||
} |
@@ -0,0 +1,107 @@ | |||
using System; | |||
using System.IO; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
using MQTTnet.Core.Channel; | |||
using MQTTnet.Core.Protocol; | |||
namespace MQTTnet.Core.Serializer | |||
{ | |||
public sealed class MqttPacketWriter : IDisposable | |||
{ | |||
private readonly MemoryStream _buffer = new MemoryStream(); | |||
public void InjectFixedHeader(byte fixedHeader) | |||
{ | |||
if (_buffer.Length == 0) | |||
{ | |||
Write(fixedHeader); | |||
Write(0); | |||
return; | |||
} | |||
var remainingLength = (int)_buffer.Length; | |||
using (var buffer = new MemoryStream()) | |||
{ | |||
_buffer.WriteTo(buffer); | |||
_buffer.SetLength(0); | |||
_buffer.WriteByte(fixedHeader); | |||
// Alorithm taken from http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html. | |||
var x = remainingLength; | |||
do | |||
{ | |||
var encodedByte = (byte)(x % 128); | |||
x = x / 128; | |||
if (x > 0) | |||
{ | |||
encodedByte = (byte)(encodedByte | 128); | |||
} | |||
_buffer.WriteByte(encodedByte); | |||
} while (x > 0); | |||
buffer.Position = 0; | |||
buffer.WriteTo(_buffer); | |||
} | |||
} | |||
public void InjectFixedHeader(MqttControlPacketType packetType, byte flags = 0) | |||
{ | |||
var fixedHeader = (byte)((byte)packetType << 4); | |||
fixedHeader |= flags; | |||
InjectFixedHeader(fixedHeader); | |||
} | |||
public void Write(byte value) | |||
{ | |||
_buffer.WriteByte(value); | |||
} | |||
public void Write(char value) | |||
{ | |||
_buffer.WriteByte((byte)value); | |||
} | |||
public void Write(ushort value) | |||
{ | |||
var buffer = BitConverter.GetBytes(value); | |||
_buffer.WriteByte(buffer[1]); | |||
_buffer.WriteByte(buffer[0]); | |||
} | |||
public void Write(ByteWriter value) | |||
{ | |||
_buffer.WriteByte(value.Value); | |||
} | |||
public void Write(byte[] value) | |||
{ | |||
_buffer.Write(value, 0, value.Length); | |||
} | |||
public void WriteWithLengthPrefix(string value) | |||
{ | |||
WriteWithLengthPrefix(Encoding.UTF8.GetBytes(value ?? string.Empty)); | |||
} | |||
public void WriteWithLengthPrefix(byte[] value) | |||
{ | |||
var length = (ushort)value.Length; | |||
Write(length); | |||
Write(value); | |||
} | |||
public void Dispose() | |||
{ | |||
_buffer?.Dispose(); | |||
} | |||
public async Task WriteToAsync(IMqttTransportChannel destination) | |||
{ | |||
await destination.WriteAsync(_buffer.ToArray()); | |||
} | |||
} | |||
} |
@@ -0,0 +1,11 @@ | |||
using System.Threading.Tasks; | |||
namespace MQTTnet.Core | |||
{ | |||
public static class TaskExtensions | |||
{ | |||
public static void Forget(this Task task) | |||
{ | |||
} | |||
} | |||
} |
@@ -0,0 +1,6 @@ | |||
<?xml version="1.0" encoding="utf-8" ?> | |||
<configuration> | |||
<startup> | |||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" /> | |||
</startup> | |||
</configuration> |
@@ -0,0 +1,71 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | |||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> | |||
<PropertyGroup> | |||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> | |||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> | |||
<ProjectGuid>{7B19B139-2E9D-4F1D-88B4-6180B4CF872A}</ProjectGuid> | |||
<OutputType>Exe</OutputType> | |||
<AppDesignerFolder>Properties</AppDesignerFolder> | |||
<RootNamespace>MQTTnet.TestConsole</RootNamespace> | |||
<AssemblyName>MQTTnet.TestConsole</AssemblyName> | |||
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion> | |||
<FileAlignment>512</FileAlignment> | |||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects> | |||
</PropertyGroup> | |||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> | |||
<PlatformTarget>AnyCPU</PlatformTarget> | |||
<DebugSymbols>true</DebugSymbols> | |||
<DebugType>full</DebugType> | |||
<Optimize>false</Optimize> | |||
<OutputPath>bin\Debug\</OutputPath> | |||
<DefineConstants>DEBUG;TRACE</DefineConstants> | |||
<ErrorReport>prompt</ErrorReport> | |||
<WarningLevel>4</WarningLevel> | |||
</PropertyGroup> | |||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> | |||
<PlatformTarget>AnyCPU</PlatformTarget> | |||
<DebugType>pdbonly</DebugType> | |||
<Optimize>true</Optimize> | |||
<OutputPath>bin\Release\</OutputPath> | |||
<DefineConstants>TRACE</DefineConstants> | |||
<ErrorReport>prompt</ErrorReport> | |||
<WarningLevel>4</WarningLevel> | |||
</PropertyGroup> | |||
<ItemGroup> | |||
<Reference Include="System" /> | |||
<Reference Include="System.Core" /> | |||
<Reference Include="System.ServiceModel.Activities" /> | |||
<Reference Include="System.Xml.Linq" /> | |||
<Reference Include="System.Data.DataSetExtensions" /> | |||
<Reference Include="Microsoft.CSharp" /> | |||
<Reference Include="System.Data" /> | |||
<Reference Include="System.Net.Http" /> | |||
<Reference Include="System.Xml" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<Compile Include="Program.cs" /> | |||
<Compile Include="Properties\AssemblyInfo.cs" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<None Include="App.config" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<ProjectReference Include="..\MQTT.NET.Core\MQTTnet.Core.csproj"> | |||
<Project>{99c884f3-b4b9-417d-aa92-dc7dd1c4cfee}</Project> | |||
<Name>MQTTnet.Core</Name> | |||
</ProjectReference> | |||
<ProjectReference Include="..\MQTTnet.NET\MQTTnet.NETFramework.csproj"> | |||
<Project>{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}</Project> | |||
<Name>MQTTnet.NETFramework</Name> | |||
</ProjectReference> | |||
</ItemGroup> | |||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> | |||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. | |||
Other similar extension points exist, see Microsoft.Common.targets. | |||
<Target Name="BeforeBuild"> | |||
</Target> | |||
<Target Name="AfterBuild"> | |||
</Target> | |||
--> | |||
</Project> |
@@ -0,0 +1,109 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
using MQTTnet.Core; | |||
using MQTTnet.Core.Adapter; | |||
using MQTTnet.Core.Client; | |||
using MQTTnet.Core.Diagnostics; | |||
using MQTTnet.Core.Packets; | |||
using MQTTnet.Core.Protocol; | |||
using MQTTnet.NETFramework; | |||
namespace MQTTnet.TestConsole | |||
{ | |||
public static class Program | |||
{ | |||
public static void Main(string[] arguments) | |||
{ | |||
Task.Run(() => Run(arguments)).Wait(); | |||
} | |||
private static async Task Run(string[] arguments) | |||
{ | |||
MqttTrace.TraceMessagePublished += (s, e) => | |||
{ | |||
Console.WriteLine($">> [{e.ThreadId}] [{e.Source}] [{e.Level}]: {e.Message}"); | |||
if (e.Exception != null) | |||
{ | |||
// Console.WriteLine(e.Exception); | |||
} | |||
}; | |||
try | |||
{ | |||
var options = new MqttClientOptions | |||
{ | |||
Server = "localhost" | |||
}; | |||
var client = new MqttClientFactory().CreateMqttClient(options); | |||
client.ApplicationMessageReceived += (s, e) => | |||
{ | |||
Console.WriteLine("### RECEIVED APPLICATION MESSAGE ###"); | |||
Console.WriteLine($"+ Topic = {e.ApplicationMessage.Topic}"); | |||
Console.WriteLine($"+ Payload = {Encoding.UTF8.GetString(e.ApplicationMessage.Payload)}"); | |||
Console.WriteLine($"+ QoS = {e.ApplicationMessage.QualityOfServiceLevel}"); | |||
Console.WriteLine($"+ Retain = {e.ApplicationMessage.Retain}"); | |||
Console.WriteLine(); | |||
}; | |||
client.Connected += async (s, e) => | |||
{ | |||
Console.WriteLine("### CONNECTED WITH SERVER ###"); | |||
await client.SubscribeAsync(new List<TopicFilter> | |||
{ | |||
new TopicFilter("#", MqttQualityOfServiceLevel.AtMostOnce) | |||
}); | |||
Console.WriteLine("### SUBSCRIBED ###"); | |||
}; | |||
client.Disconnected += async (s, e) => | |||
{ | |||
Console.WriteLine("### DISCONNECTED FROM SERVER ###"); | |||
await Task.Delay(TimeSpan.FromSeconds(5)); | |||
try | |||
{ | |||
await client.ConnectAsync(); | |||
} | |||
catch | |||
{ | |||
Console.WriteLine("### RECONNECTING FAILED ###"); | |||
} | |||
}; | |||
try | |||
{ | |||
await client.ConnectAsync(); | |||
} | |||
catch | |||
{ | |||
Console.WriteLine("### CONNECTING FAILED ###"); | |||
} | |||
Console.WriteLine("### WAITING FOR APPLICATION MESSAGES ###"); | |||
while (true) | |||
{ | |||
Console.ReadLine(); | |||
var applicationMessage = new MqttApplicationMessage( | |||
"A/B/C", | |||
Encoding.UTF8.GetBytes("Hello World"), | |||
MqttQualityOfServiceLevel.AtLeastOnce, | |||
false | |||
); | |||
await client.PublishAsync(applicationMessage); | |||
} | |||
} | |||
catch (Exception exception) | |||
{ | |||
Console.WriteLine(exception); | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,15 @@ | |||
using System.Reflection; | |||
using System.Runtime.InteropServices; | |||
[assembly: AssemblyTitle("MQTTnet.TestConsole")] | |||
[assembly: AssemblyDescription("")] | |||
[assembly: AssemblyConfiguration("")] | |||
[assembly: AssemblyCompany("Christian Kratky")] | |||
[assembly: AssemblyProduct("MQTTnet")] | |||
[assembly: AssemblyCopyright("Copyright © Christian Kratky 2016-2017")] | |||
[assembly: AssemblyTrademark("")] | |||
[assembly: AssemblyCulture("")] | |||
[assembly: ComVisible(false)] | |||
[assembly: Guid("7b19b139-2e9d-4f1d-88b4-6180b4cf872a")] | |||
[assembly: AssemblyVersion("1.0.0.0")] | |||
[assembly: AssemblyFileVersion("1.0.0.0")] |
@@ -0,0 +1,56 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | |||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> | |||
<PropertyGroup> | |||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> | |||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> | |||
<ProjectGuid>{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}</ProjectGuid> | |||
<OutputType>Library</OutputType> | |||
<AppDesignerFolder>Properties</AppDesignerFolder> | |||
<RootNamespace>MQTTnet.NETFramework</RootNamespace> | |||
<AssemblyName>MQTTnet.NETFramework</AssemblyName> | |||
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion> | |||
<FileAlignment>512</FileAlignment> | |||
</PropertyGroup> | |||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> | |||
<DebugSymbols>true</DebugSymbols> | |||
<DebugType>full</DebugType> | |||
<Optimize>false</Optimize> | |||
<OutputPath>bin\Debug\</OutputPath> | |||
<DefineConstants>DEBUG;TRACE</DefineConstants> | |||
<ErrorReport>prompt</ErrorReport> | |||
<WarningLevel>4</WarningLevel> | |||
</PropertyGroup> | |||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> | |||
<DebugType>pdbonly</DebugType> | |||
<Optimize>true</Optimize> | |||
<OutputPath>bin\Release\</OutputPath> | |||
<DefineConstants>TRACE</DefineConstants> | |||
<ErrorReport>prompt</ErrorReport> | |||
<WarningLevel>4</WarningLevel> | |||
</PropertyGroup> | |||
<ItemGroup> | |||
<Reference Include="System" /> | |||
<Reference Include="System.Core" /> | |||
<Reference Include="System.Xml" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<Compile Include="MqttClientFactory.cs" /> | |||
<Compile Include="Properties\AssemblyInfo.cs" /> | |||
<Compile Include="MqttTcpChannel.cs" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<ProjectReference Include="..\MQTT.NET.Core\MQTTnet.Core.csproj"> | |||
<Project>{99C884F3-B4B9-417D-AA92-DC7DD1C4CFEE}</Project> | |||
<Name>MQTTnet.Core</Name> | |||
</ProjectReference> | |||
</ItemGroup> | |||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> | |||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. | |||
Other similar extension points exist, see Microsoft.Common.targets. | |||
<Target Name="BeforeBuild"> | |||
</Target> | |||
<Target Name="AfterBuild"> | |||
</Target> | |||
--> | |||
</Project> |
@@ -0,0 +1,17 @@ | |||
using System; | |||
using MQTTnet.Core.Adapter; | |||
using MQTTnet.Core.Client; | |||
using MQTTnet.Core.Serializer; | |||
namespace MQTTnet.NETFramework | |||
{ | |||
public class MqttClientFactory | |||
{ | |||
public MqttClient CreateMqttClient(MqttClientOptions options) | |||
{ | |||
if (options == null) throw new ArgumentNullException(nameof(options)); | |||
return new MqttClient(options, new MqttChannelAdapter(new MqttTcpChannel(), new DefaultMqttV311PacketSerializer())); | |||
} | |||
} | |||
} |
@@ -0,0 +1,46 @@ | |||
using System; | |||
using System.Net.Sockets; | |||
using System.Threading.Tasks; | |||
using MQTTnet.Core.Channel; | |||
using MQTTnet.Core.Client; | |||
namespace MQTTnet.NETFramework | |||
{ | |||
public class MqttTcpChannel : IMqttTransportChannel, IDisposable | |||
{ | |||
private readonly Socket _socket = new Socket(SocketType.Stream, ProtocolType.Tcp); | |||
public async Task ConnectAsync(MqttClientOptions options) | |||
{ | |||
await Task.Factory.FromAsync(_socket.BeginConnect, _socket.EndConnect, options.Server, options.Port, null); | |||
} | |||
public async Task DisconnectAsync() | |||
{ | |||
await Task.Factory.FromAsync(_socket.BeginDisconnect, _socket.EndDisconnect, true, null); | |||
} | |||
public async Task WriteAsync(byte[] buffer) | |||
{ | |||
if (buffer == null) throw new ArgumentNullException(nameof(buffer)); | |||
await Task.Factory.FromAsync( | |||
// ReSharper disable once AssignNullToNotNullAttribute | |||
_socket.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, null, null), | |||
_socket.EndSend); | |||
} | |||
public async Task ReadAsync(byte[] buffer) | |||
{ | |||
await Task.Factory.FromAsync( | |||
// ReSharper disable once AssignNullToNotNullAttribute | |||
_socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, null, null), | |||
_socket.EndReceive); | |||
} | |||
public void Dispose() | |||
{ | |||
_socket?.Dispose(); | |||
} | |||
} | |||
} |
@@ -0,0 +1,15 @@ | |||
using System.Reflection; | |||
using System.Runtime.InteropServices; | |||
[assembly: AssemblyTitle("MQTTnet.NETFramework")] | |||
[assembly: AssemblyDescription("")] | |||
[assembly: AssemblyConfiguration("")] | |||
[assembly: AssemblyCompany("Christian Kratky")] | |||
[assembly: AssemblyProduct("MQTTnet")] | |||
[assembly: AssemblyCopyright("Copyright © Christian Kratky 2016-2017")] | |||
[assembly: AssemblyTrademark("")] | |||
[assembly: AssemblyCulture("")] | |||
[assembly: ComVisible(false)] | |||
[assembly: Guid("a480ef90-0eaa-4d9a-b271-47a9c47f6f7d")] | |||
[assembly: AssemblyVersion("2.1.0.4")] | |||
[assembly: AssemblyFileVersion("2.1.0.4")] |
@@ -0,0 +1,132 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | |||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> | |||
<PropertyGroup> | |||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> | |||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> | |||
<ProjectGuid>{BD60C727-D8E8-40C3-B8E3-C95A864AE611}</ProjectGuid> | |||
<OutputType>Library</OutputType> | |||
<AppDesignerFolder>Properties</AppDesignerFolder> | |||
<RootNamespace>MQTTnet.Universal</RootNamespace> | |||
<AssemblyName>MQTTnet.Universal</AssemblyName> | |||
<DefaultLanguage>en-US</DefaultLanguage> | |||
<TargetPlatformIdentifier>UAP</TargetPlatformIdentifier> | |||
<TargetPlatformVersion>10.0.14393.0</TargetPlatformVersion> | |||
<TargetPlatformMinVersion>10.0.10586.0</TargetPlatformMinVersion> | |||
<MinimumVisualStudioVersion>14</MinimumVisualStudioVersion> | |||
<FileAlignment>512</FileAlignment> | |||
<ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> | |||
</PropertyGroup> | |||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> | |||
<PlatformTarget>AnyCPU</PlatformTarget> | |||
<DebugSymbols>true</DebugSymbols> | |||
<DebugType>full</DebugType> | |||
<Optimize>false</Optimize> | |||
<OutputPath>bin\Debug\</OutputPath> | |||
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants> | |||
<ErrorReport>prompt</ErrorReport> | |||
<WarningLevel>4</WarningLevel> | |||
</PropertyGroup> | |||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> | |||
<PlatformTarget>AnyCPU</PlatformTarget> | |||
<DebugType>pdbonly</DebugType> | |||
<Optimize>true</Optimize> | |||
<OutputPath>bin\Release\</OutputPath> | |||
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants> | |||
<ErrorReport>prompt</ErrorReport> | |||
<WarningLevel>4</WarningLevel> | |||
</PropertyGroup> | |||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'"> | |||
<PlatformTarget>x86</PlatformTarget> | |||
<DebugSymbols>true</DebugSymbols> | |||
<OutputPath>bin\x86\Debug\</OutputPath> | |||
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants> | |||
<NoWarn>;2008</NoWarn> | |||
<DebugType>full</DebugType> | |||
<PlatformTarget>x86</PlatformTarget> | |||
<UseVSHostingProcess>false</UseVSHostingProcess> | |||
<ErrorReport>prompt</ErrorReport> | |||
</PropertyGroup> | |||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'"> | |||
<PlatformTarget>x86</PlatformTarget> | |||
<OutputPath>bin\x86\Release\</OutputPath> | |||
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants> | |||
<Optimize>true</Optimize> | |||
<NoWarn>;2008</NoWarn> | |||
<DebugType>pdbonly</DebugType> | |||
<PlatformTarget>x86</PlatformTarget> | |||
<UseVSHostingProcess>false</UseVSHostingProcess> | |||
<ErrorReport>prompt</ErrorReport> | |||
</PropertyGroup> | |||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|ARM'"> | |||
<PlatformTarget>ARM</PlatformTarget> | |||
<DebugSymbols>true</DebugSymbols> | |||
<OutputPath>bin\ARM\Debug\</OutputPath> | |||
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants> | |||
<NoWarn>;2008</NoWarn> | |||
<DebugType>full</DebugType> | |||
<PlatformTarget>ARM</PlatformTarget> | |||
<UseVSHostingProcess>false</UseVSHostingProcess> | |||
<ErrorReport>prompt</ErrorReport> | |||
</PropertyGroup> | |||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|ARM'"> | |||
<PlatformTarget>ARM</PlatformTarget> | |||
<OutputPath>bin\ARM\Release\</OutputPath> | |||
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants> | |||
<Optimize>true</Optimize> | |||
<NoWarn>;2008</NoWarn> | |||
<DebugType>pdbonly</DebugType> | |||
<PlatformTarget>ARM</PlatformTarget> | |||
<UseVSHostingProcess>false</UseVSHostingProcess> | |||
<ErrorReport>prompt</ErrorReport> | |||
</PropertyGroup> | |||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'"> | |||
<PlatformTarget>x64</PlatformTarget> | |||
<DebugSymbols>true</DebugSymbols> | |||
<OutputPath>bin\x64\Debug\</OutputPath> | |||
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants> | |||
<NoWarn>;2008</NoWarn> | |||
<DebugType>full</DebugType> | |||
<PlatformTarget>x64</PlatformTarget> | |||
<UseVSHostingProcess>false</UseVSHostingProcess> | |||
<ErrorReport>prompt</ErrorReport> | |||
</PropertyGroup> | |||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'"> | |||
<PlatformTarget>x64</PlatformTarget> | |||
<OutputPath>bin\x64\Release\</OutputPath> | |||
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants> | |||
<Optimize>true</Optimize> | |||
<NoWarn>;2008</NoWarn> | |||
<DebugType>pdbonly</DebugType> | |||
<PlatformTarget>x64</PlatformTarget> | |||
<UseVSHostingProcess>false</UseVSHostingProcess> | |||
<ErrorReport>prompt</ErrorReport> | |||
</PropertyGroup> | |||
<ItemGroup> | |||
<!-- A reference to the entire .Net Framework and Windows SDK are automatically included --> | |||
<None Include="project.json" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<Compile Include="MqttClientFactory.cs" /> | |||
<Compile Include="MqttTcpChannel.cs" /> | |||
<Compile Include="Properties\AssemblyInfo.cs" /> | |||
<EmbeddedResource Include="Properties\MQTTnet.Universal.rd.xml" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<ProjectReference Include="..\MQTT.NET.Core\MQTTnet.Core.csproj"> | |||
<Project>{99C884F3-B4B9-417D-AA92-DC7DD1C4CFEE}</Project> | |||
<Name>MQTTnet.Core</Name> | |||
</ProjectReference> | |||
</ItemGroup> | |||
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' < '14.0' "> | |||
<VisualStudioVersion>14.0</VisualStudioVersion> | |||
</PropertyGroup> | |||
<Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets" /> | |||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. | |||
Other similar extension points exist, see Microsoft.Common.targets. | |||
<Target Name="BeforeBuild"> | |||
</Target> | |||
<Target Name="AfterBuild"> | |||
</Target> | |||
--> | |||
</Project> |
@@ -0,0 +1,17 @@ | |||
using System; | |||
using MQTTnet.Core.Adapter; | |||
using MQTTnet.Core.Client; | |||
using MQTTnet.Core.Serializer; | |||
namespace MQTTnet.Universal | |||
{ | |||
public class MqttClientFactory | |||
{ | |||
public MqttClient CreateMqttClient(MqttClientOptions options) | |||
{ | |||
if (options == null) throw new ArgumentNullException(nameof(options)); | |||
return new MqttClient(options, new MqttChannelAdapter(new MqttTcpChannel(), new DefaultMqttV311PacketSerializer())); | |||
} | |||
} | |||
} |
@@ -0,0 +1,52 @@ | |||
using System; | |||
using System.Runtime.InteropServices.WindowsRuntime; | |||
using System.Threading.Tasks; | |||
using Windows.Networking; | |||
using Windows.Networking.Sockets; | |||
using Windows.Storage.Streams; | |||
using MQTTnet.Core.Channel; | |||
using MQTTnet.Core.Client; | |||
using Buffer = Windows.Storage.Streams.Buffer; | |||
namespace MQTTnet.Universal | |||
{ | |||
public sealed class MqttTcpChannel : IMqttTransportChannel, IDisposable | |||
{ | |||
private readonly StreamSocket _socket = new StreamSocket(); | |||
public async Task ConnectAsync(MqttClientOptions options) | |||
{ | |||
if (options == null) throw new ArgumentNullException(nameof(options)); | |||
await _socket.ConnectAsync(new HostName(options.Server), options.Port.ToString()); | |||
} | |||
public async Task DisconnectAsync() | |||
{ | |||
await _socket.CancelIOAsync(); | |||
_socket.Dispose(); | |||
} | |||
public async Task WriteAsync(byte[] buffer) | |||
{ | |||
if (buffer == null) throw new ArgumentNullException(nameof(buffer)); | |||
await _socket.OutputStream.WriteAsync(buffer.AsBuffer()); | |||
await _socket.OutputStream.FlushAsync(); | |||
} | |||
public async Task ReadAsync(byte[] buffer) | |||
{ | |||
var buffer2 = new Buffer((uint)buffer.Length); | |||
await _socket.InputStream.ReadAsync(buffer2, (uint)buffer.Length, InputStreamOptions.None); | |||
var array2 = buffer2.ToArray(); | |||
Array.Copy(array2, buffer, array2.Length); | |||
} | |||
public void Dispose() | |||
{ | |||
_socket?.Dispose(); | |||
} | |||
} | |||
} |
@@ -0,0 +1,14 @@ | |||
using System.Reflection; | |||
using System.Runtime.InteropServices; | |||
[assembly: AssemblyTitle("MQTTnet.Universal")] | |||
[assembly: AssemblyDescription("")] | |||
[assembly: AssemblyConfiguration("")] | |||
[assembly: AssemblyCompany("Christian Kratky")] | |||
[assembly: AssemblyProduct("MQTTnet")] | |||
[assembly: AssemblyCopyright("Copyright © Christian Kratky 2016-2017")] | |||
[assembly: AssemblyTrademark("")] | |||
[assembly: AssemblyCulture("")] | |||
[assembly: ComVisible(false)] | |||
[assembly: AssemblyVersion("2.1.0.4")] | |||
[assembly: AssemblyFileVersion("2.1.0.4")] |
@@ -0,0 +1,33 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<!-- | |||
This file contains Runtime Directives, specifications about types your application accesses | |||
through reflection and other dynamic code patterns. Runtime Directives are used to control the | |||
.NET Native optimizer and ensure that it does not remove code accessed by your library. If your | |||
library does not do any reflection, then you generally do not need to edit this file. However, | |||
if your library reflects over types, especially types passed to it or derived from its types, | |||
then you should write Runtime Directives. | |||
The most common use of reflection in libraries is to discover information about types passed | |||
to the library. Runtime Directives have three ways to express requirements on types passed to | |||
your library. | |||
1. Parameter, GenericParameter, TypeParameter, TypeEnumerableParameter | |||
Use these directives to reflect over types passed as a parameter. | |||
2. SubTypes | |||
Use a SubTypes directive to reflect over types derived from another type. | |||
3. AttributeImplies | |||
Use an AttributeImplies directive to indicate that your library needs to reflect over | |||
types or methods decorated with an attribute. | |||
For more information on writing Runtime Directives for libraries, please visit | |||
http://go.microsoft.com/fwlink/?LinkID=391919 | |||
--> | |||
<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata"> | |||
<Library Name="MQTTnet.Universal"> | |||
<!-- add directives for your library here --> | |||
</Library> | |||
</Directives> |
@@ -0,0 +1,16 @@ | |||
{ | |||
"dependencies": { | |||
"Microsoft.NETCore.UniversalWindowsPlatform": "5.1.0" | |||
}, | |||
"frameworks": { | |||
"uap10.0": {} | |||
}, | |||
"runtimes": { | |||
"win10-arm": {}, | |||
"win10-arm-aot": {}, | |||
"win10-x86": {}, | |||
"win10-x86-aot": {}, | |||
"win10-x64": {}, | |||
"win10-x64-aot": {} | |||
} | |||
} |
@@ -0,0 +1,112 @@ | |||
| |||
Microsoft Visual Studio Solution File, Format Version 12.00 | |||
# Visual Studio 14 | |||
VisualStudioVersion = 14.0.25420.1 | |||
MinimumVisualStudioVersion = 10.0.40219.1 | |||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.Core", "MQTT.NET.Core\MQTTnet.Core.csproj", "{99C884F3-B4B9-417D-AA92-DC7DD1C4CFEE}" | |||
EndProject | |||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.TestConsole", "MQTT.NET.TestConsole\MQTTnet.TestConsole.csproj", "{7B19B139-2E9D-4F1D-88B4-6180B4CF872A}" | |||
EndProject | |||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.Core.Tests", "MQTT.NET.Core.Tests\MQTTnet.Core.Tests.csproj", "{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}" | |||
EndProject | |||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.NETFramework", "MQTTnet.NET\MQTTnet.NETFramework.csproj", "{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}" | |||
EndProject | |||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.Universal", "MQTTnet.Universal\MQTTnet.Universal.csproj", "{BD60C727-D8E8-40C3-B8E3-C95A864AE611}" | |||
EndProject | |||
Global | |||
GlobalSection(SolutionConfigurationPlatforms) = preSolution | |||
Debug|Any CPU = Debug|Any CPU | |||
Debug|ARM = Debug|ARM | |||
Debug|x64 = Debug|x64 | |||
Debug|x86 = Debug|x86 | |||
Release|Any CPU = Release|Any CPU | |||
Release|ARM = Release|ARM | |||
Release|x64 = Release|x64 | |||
Release|x86 = Release|x86 | |||
EndGlobalSection | |||
GlobalSection(ProjectConfigurationPlatforms) = postSolution | |||
{99C884F3-B4B9-417D-AA92-DC7DD1C4CFEE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||
{99C884F3-B4B9-417D-AA92-DC7DD1C4CFEE}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||
{99C884F3-B4B9-417D-AA92-DC7DD1C4CFEE}.Debug|ARM.ActiveCfg = Debug|Any CPU | |||
{99C884F3-B4B9-417D-AA92-DC7DD1C4CFEE}.Debug|ARM.Build.0 = Debug|Any CPU | |||
{99C884F3-B4B9-417D-AA92-DC7DD1C4CFEE}.Debug|x64.ActiveCfg = Debug|Any CPU | |||
{99C884F3-B4B9-417D-AA92-DC7DD1C4CFEE}.Debug|x64.Build.0 = Debug|Any CPU | |||
{99C884F3-B4B9-417D-AA92-DC7DD1C4CFEE}.Debug|x86.ActiveCfg = Debug|Any CPU | |||
{99C884F3-B4B9-417D-AA92-DC7DD1C4CFEE}.Debug|x86.Build.0 = Debug|Any CPU | |||
{99C884F3-B4B9-417D-AA92-DC7DD1C4CFEE}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||
{99C884F3-B4B9-417D-AA92-DC7DD1C4CFEE}.Release|Any CPU.Build.0 = Release|Any CPU | |||
{99C884F3-B4B9-417D-AA92-DC7DD1C4CFEE}.Release|ARM.ActiveCfg = Release|Any CPU | |||
{99C884F3-B4B9-417D-AA92-DC7DD1C4CFEE}.Release|ARM.Build.0 = Release|Any CPU | |||
{99C884F3-B4B9-417D-AA92-DC7DD1C4CFEE}.Release|x64.ActiveCfg = Release|Any CPU | |||
{99C884F3-B4B9-417D-AA92-DC7DD1C4CFEE}.Release|x64.Build.0 = Release|Any CPU | |||
{99C884F3-B4B9-417D-AA92-DC7DD1C4CFEE}.Release|x86.ActiveCfg = Release|Any CPU | |||
{99C884F3-B4B9-417D-AA92-DC7DD1C4CFEE}.Release|x86.Build.0 = Release|Any CPU | |||
{7B19B139-2E9D-4F1D-88B4-6180B4CF872A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||
{7B19B139-2E9D-4F1D-88B4-6180B4CF872A}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||
{7B19B139-2E9D-4F1D-88B4-6180B4CF872A}.Debug|ARM.ActiveCfg = Debug|Any CPU | |||
{7B19B139-2E9D-4F1D-88B4-6180B4CF872A}.Debug|ARM.Build.0 = Debug|Any CPU | |||
{7B19B139-2E9D-4F1D-88B4-6180B4CF872A}.Debug|x64.ActiveCfg = Debug|Any CPU | |||
{7B19B139-2E9D-4F1D-88B4-6180B4CF872A}.Debug|x64.Build.0 = Debug|Any CPU | |||
{7B19B139-2E9D-4F1D-88B4-6180B4CF872A}.Debug|x86.ActiveCfg = Debug|Any CPU | |||
{7B19B139-2E9D-4F1D-88B4-6180B4CF872A}.Debug|x86.Build.0 = Debug|Any CPU | |||
{7B19B139-2E9D-4F1D-88B4-6180B4CF872A}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||
{7B19B139-2E9D-4F1D-88B4-6180B4CF872A}.Release|Any CPU.Build.0 = Release|Any CPU | |||
{7B19B139-2E9D-4F1D-88B4-6180B4CF872A}.Release|ARM.ActiveCfg = Release|Any CPU | |||
{7B19B139-2E9D-4F1D-88B4-6180B4CF872A}.Release|ARM.Build.0 = Release|Any CPU | |||
{7B19B139-2E9D-4F1D-88B4-6180B4CF872A}.Release|x64.ActiveCfg = Release|Any CPU | |||
{7B19B139-2E9D-4F1D-88B4-6180B4CF872A}.Release|x64.Build.0 = Release|Any CPU | |||
{7B19B139-2E9D-4F1D-88B4-6180B4CF872A}.Release|x86.ActiveCfg = Release|Any CPU | |||
{7B19B139-2E9D-4F1D-88B4-6180B4CF872A}.Release|x86.Build.0 = Release|Any CPU | |||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|ARM.ActiveCfg = Debug|Any CPU | |||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|ARM.Build.0 = Debug|Any CPU | |||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|x64.ActiveCfg = Debug|Any CPU | |||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|x64.Build.0 = Debug|Any CPU | |||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|x86.ActiveCfg = Debug|Any CPU | |||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|x86.Build.0 = Debug|Any CPU | |||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|Any CPU.Build.0 = Release|Any CPU | |||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|ARM.ActiveCfg = Release|Any CPU | |||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|ARM.Build.0 = Release|Any CPU | |||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|x64.ActiveCfg = Release|Any CPU | |||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|x64.Build.0 = Release|Any CPU | |||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|x86.ActiveCfg = Release|Any CPU | |||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|x86.Build.0 = Release|Any CPU | |||
{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||
{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||
{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}.Debug|ARM.ActiveCfg = Debug|Any CPU | |||
{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}.Debug|ARM.Build.0 = Debug|Any CPU | |||
{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}.Debug|x64.ActiveCfg = Debug|Any CPU | |||
{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}.Debug|x64.Build.0 = Debug|Any CPU | |||
{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}.Debug|x86.ActiveCfg = Debug|Any CPU | |||
{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}.Debug|x86.Build.0 = Debug|Any CPU | |||
{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||
{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}.Release|Any CPU.Build.0 = Release|Any CPU | |||
{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}.Release|ARM.ActiveCfg = Release|Any CPU | |||
{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}.Release|ARM.Build.0 = Release|Any CPU | |||
{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}.Release|x64.ActiveCfg = Release|Any CPU | |||
{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}.Release|x64.Build.0 = Release|Any CPU | |||
{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}.Release|x86.ActiveCfg = Release|Any CPU | |||
{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}.Release|x86.Build.0 = Release|Any CPU | |||
{BD60C727-D8E8-40C3-B8E3-C95A864AE611}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | |||
{BD60C727-D8E8-40C3-B8E3-C95A864AE611}.Debug|Any CPU.Build.0 = Debug|Any CPU | |||
{BD60C727-D8E8-40C3-B8E3-C95A864AE611}.Debug|ARM.ActiveCfg = Debug|ARM | |||
{BD60C727-D8E8-40C3-B8E3-C95A864AE611}.Debug|ARM.Build.0 = Debug|ARM | |||
{BD60C727-D8E8-40C3-B8E3-C95A864AE611}.Debug|x64.ActiveCfg = Debug|x64 | |||
{BD60C727-D8E8-40C3-B8E3-C95A864AE611}.Debug|x64.Build.0 = Debug|x64 | |||
{BD60C727-D8E8-40C3-B8E3-C95A864AE611}.Debug|x86.ActiveCfg = Debug|x86 | |||
{BD60C727-D8E8-40C3-B8E3-C95A864AE611}.Debug|x86.Build.0 = Debug|x86 | |||
{BD60C727-D8E8-40C3-B8E3-C95A864AE611}.Release|Any CPU.ActiveCfg = Release|Any CPU | |||
{BD60C727-D8E8-40C3-B8E3-C95A864AE611}.Release|Any CPU.Build.0 = Release|Any CPU | |||
{BD60C727-D8E8-40C3-B8E3-C95A864AE611}.Release|ARM.ActiveCfg = Release|ARM | |||
{BD60C727-D8E8-40C3-B8E3-C95A864AE611}.Release|ARM.Build.0 = Release|ARM | |||
{BD60C727-D8E8-40C3-B8E3-C95A864AE611}.Release|x64.ActiveCfg = Release|x64 | |||
{BD60C727-D8E8-40C3-B8E3-C95A864AE611}.Release|x64.Build.0 = Release|x64 | |||
{BD60C727-D8E8-40C3-B8E3-C95A864AE611}.Release|x86.ActiveCfg = Release|x86 | |||
{BD60C727-D8E8-40C3-B8E3-C95A864AE611}.Release|x86.Build.0 = Release|x86 | |||
EndGlobalSection | |||
GlobalSection(SolutionProperties) = preSolution | |||
HideSolutionNode = FALSE | |||
EndGlobalSection | |||
EndGlobal |