@@ -10,7 +10,8 @@ | |||||
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl> | <iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl> | ||||
<requireLicenseAcceptance>false</requireLicenseAcceptance> | <requireLicenseAcceptance>false</requireLicenseAcceptance> | ||||
<description>MQTTnet is a .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker).</description> | <description>MQTTnet is a .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker).</description> | ||||
<releaseNotes>* | |||||
<releaseNotes>* [Client] Added support for web socket communication channel (thanks to nowakpiotr) | |||||
* [Core] Performance optimizations (thanks to JanEggers) | |||||
</releaseNotes> | </releaseNotes> | ||||
<copyright>Copyright Christian Kratky 2016-2017</copyright> | <copyright>Copyright Christian Kratky 2016-2017</copyright> | ||||
<tags>MQTT MQTTClient MQTTServer MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Queue Hardware Arduino</tags> | <tags>MQTT MQTTClient MQTTServer MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Queue Hardware Arduino</tags> | ||||
@@ -8,20 +8,15 @@ using System.Threading.Tasks; | |||||
namespace MQTTnet.Implementations | namespace MQTTnet.Implementations | ||||
{ | { | ||||
public sealed class MqttWebSocketsChannel : IMqttCommunicationChannel, IDisposable | |||||
public sealed class MqttWebSocketChannel : IMqttCommunicationChannel, IDisposable | |||||
{ | { | ||||
private ClientWebSocket _webSocket; | |||||
private ClientWebSocket _webSocket = new ClientWebSocket(); | |||||
private const int BufferSize = 4096; | private const int BufferSize = 4096; | ||||
private const int BufferAmplifier = 20; | private const int BufferAmplifier = 20; | ||||
private readonly byte[] WebSocketBuffer = new byte[BufferSize * BufferAmplifier]; | private readonly byte[] WebSocketBuffer = new byte[BufferSize * BufferAmplifier]; | ||||
private int WebSocketBufferSize; | private int WebSocketBufferSize; | ||||
private int WebSocketBufferOffset; | private int WebSocketBufferOffset; | ||||
public MqttWebSocketsChannel() | |||||
{ | |||||
_webSocket = new ClientWebSocket(); | |||||
} | |||||
public async Task ConnectAsync(MqttClientOptions options) | public async Task ConnectAsync(MqttClientOptions options) | ||||
{ | { | ||||
_webSocket = null; | _webSocket = null; | ||||
@@ -45,10 +40,7 @@ namespace MQTTnet.Implementations | |||||
public void Dispose() | public void Dispose() | ||||
{ | { | ||||
if (_webSocket != null) | |||||
{ | |||||
_webSocket.Dispose(); | |||||
} | |||||
_webSocket?.Dispose(); | |||||
} | } | ||||
public Task ReadAsync(byte[] buffer) | public Task ReadAsync(byte[] buffer) | ||||
@@ -70,8 +62,7 @@ namespace MQTTnet.Implementations | |||||
WebSocketReceiveResult response; | WebSocketReceiveResult response; | ||||
do | do | ||||
{ | { | ||||
response = | |||||
await _webSocket.ReceiveAsync(new ArraySegment<byte>(temporaryBuffer), CancellationToken.None); | |||||
response = await _webSocket.ReceiveAsync(new ArraySegment<byte>(temporaryBuffer), CancellationToken.None); | |||||
temporaryBuffer.CopyTo(WebSocketBuffer, offset); | temporaryBuffer.CopyTo(WebSocketBuffer, offset); | ||||
offset += response.Count; | offset += response.Count; |
@@ -100,6 +100,7 @@ | |||||
<Reference Include="System.Core" /> | <Reference Include="System.Core" /> | ||||
</ItemGroup> | </ItemGroup> | ||||
<ItemGroup> | <ItemGroup> | ||||
<Compile Include="Implementations\MqttWebSocketChannel.cs" /> | |||||
<Compile Include="MqttClientFactory.cs" /> | <Compile Include="MqttClientFactory.cs" /> | ||||
<Compile Include="MqttServerFactory.cs" /> | <Compile Include="MqttServerFactory.cs" /> | ||||
<Compile Include="Implementations\MqttServerAdapter.cs" /> | <Compile Include="Implementations\MqttServerAdapter.cs" /> | ||||
@@ -1,5 +1,6 @@ | |||||
using System; | using System; | ||||
using MQTTnet.Core.Adapter; | using MQTTnet.Core.Adapter; | ||||
using MQTTnet.Core.Channel; | |||||
using MQTTnet.Core.Client; | using MQTTnet.Core.Client; | ||||
using MQTTnet.Core.Serializer; | using MQTTnet.Core.Serializer; | ||||
using MQTTnet.Implementations; | using MQTTnet.Implementations; | ||||
@@ -12,7 +13,23 @@ namespace MQTTnet | |||||
{ | { | ||||
if (options == null) throw new ArgumentNullException(nameof(options)); | if (options == null) throw new ArgumentNullException(nameof(options)); | ||||
return new MqttClient(options, new MqttChannelCommunicationAdapter(new MqttTcpChannel(), new MqttPacketSerializer())); | |||||
return new MqttClient(options, new MqttChannelCommunicationAdapter(GetMqttCommunicationChannel(options), new MqttPacketSerializer())); | |||||
} | |||||
private static IMqttCommunicationChannel GetMqttCommunicationChannel(MqttClientOptions options) | |||||
{ | |||||
switch (options.ConnectionType) | |||||
{ | |||||
case MqttConnectionType.Tcp: | |||||
case MqttConnectionType.Tls: | |||||
return new MqttTcpChannel(); | |||||
case MqttConnectionType.Ws: | |||||
case MqttConnectionType.Wss: | |||||
return new MqttWebSocketChannel(); | |||||
default: | |||||
throw new NotSupportedException(); | |||||
} | |||||
} | } | ||||
} | } | ||||
} | } |
@@ -0,0 +1,110 @@ | |||||
using MQTTnet.Core.Channel; | |||||
using MQTTnet.Core.Client; | |||||
using MQTTnet.Core.Exceptions; | |||||
using System; | |||||
using System.Net.WebSockets; | |||||
using System.Threading; | |||||
using System.Threading.Tasks; | |||||
namespace MQTTnet.Implementations | |||||
{ | |||||
public sealed class MqttWebSocketChannel : IMqttCommunicationChannel, IDisposable | |||||
{ | |||||
private ClientWebSocket _webSocket = new ClientWebSocket(); | |||||
private const int BufferSize = 4096; | |||||
private const int BufferAmplifier = 20; | |||||
private readonly byte[] WebSocketBuffer = new byte[BufferSize * BufferAmplifier]; | |||||
private int WebSocketBufferSize; | |||||
private int WebSocketBufferOffset; | |||||
public async Task ConnectAsync(MqttClientOptions options) | |||||
{ | |||||
_webSocket = null; | |||||
try | |||||
{ | |||||
_webSocket = new ClientWebSocket(); | |||||
await _webSocket.ConnectAsync(new Uri(options.Server), CancellationToken.None); | |||||
} | |||||
catch (WebSocketException exception) | |||||
{ | |||||
throw new MqttCommunicationException(exception); | |||||
} | |||||
} | |||||
public async Task DisconnectAsync() | |||||
{ | |||||
await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None); | |||||
} | |||||
public void Dispose() | |||||
{ | |||||
_webSocket?.Dispose(); | |||||
} | |||||
public Task ReadAsync(byte[] buffer) | |||||
{ | |||||
return Task.WhenAll(ReadToBufferAsync(buffer)); | |||||
} | |||||
private async Task ReadToBufferAsync(byte[] buffer) | |||||
{ | |||||
var temporaryBuffer = new byte[BufferSize]; | |||||
var offset = 0; | |||||
while (_webSocket.State == WebSocketState.Open) | |||||
{ | |||||
if (WebSocketBufferSize == 0) | |||||
{ | |||||
WebSocketBufferOffset = 0; | |||||
WebSocketReceiveResult response; | |||||
do | |||||
{ | |||||
response = await _webSocket.ReceiveAsync(new ArraySegment<byte>(temporaryBuffer), CancellationToken.None); | |||||
temporaryBuffer.CopyTo(WebSocketBuffer, offset); | |||||
offset += response.Count; | |||||
temporaryBuffer = new byte[BufferSize]; | |||||
} while (!response.EndOfMessage); | |||||
WebSocketBufferSize = response.Count; | |||||
if (response.MessageType == WebSocketMessageType.Close) | |||||
{ | |||||
await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None); | |||||
} | |||||
Buffer.BlockCopy(WebSocketBuffer, 0, buffer, 0, buffer.Length); | |||||
WebSocketBufferSize -= buffer.Length; | |||||
WebSocketBufferOffset += buffer.Length; | |||||
} | |||||
else | |||||
{ | |||||
Buffer.BlockCopy(WebSocketBuffer, WebSocketBufferOffset, buffer, 0, buffer.Length); | |||||
WebSocketBufferSize -= buffer.Length; | |||||
WebSocketBufferOffset += buffer.Length; | |||||
} | |||||
return; | |||||
} | |||||
} | |||||
public Task WriteAsync(byte[] buffer) | |||||
{ | |||||
if (buffer == null) { | |||||
throw new ArgumentNullException(nameof(buffer)); | |||||
} | |||||
try | |||||
{ | |||||
return _webSocket.SendAsync(new ArraySegment<byte>(buffer), WebSocketMessageType.Binary, true, | |||||
CancellationToken.None); | |||||
} | |||||
catch (WebSocketException exception) | |||||
{ | |||||
throw new MqttCommunicationException(exception); | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -22,15 +22,15 @@ namespace MQTTnet | |||||
{ | { | ||||
switch (options.ConnectionType) | switch (options.ConnectionType) | ||||
{ | { | ||||
case ConnectionTypes.TCP: | |||||
case ConnectionTypes.TLS: | |||||
case MqttConnectionType.Tcp: | |||||
case MqttConnectionType.Tls: | |||||
return new MqttTcpChannel(); | return new MqttTcpChannel(); | ||||
case ConnectionTypes.WS: | |||||
case ConnectionTypes.WSS: | |||||
return new MqttWebSocketsChannel(); | |||||
case MqttConnectionType.Ws: | |||||
case MqttConnectionType.Wss: | |||||
return new MqttWebSocketChannel(); | |||||
default: | default: | ||||
return null; | |||||
throw new NotSupportedException(); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
@@ -0,0 +1,110 @@ | |||||
using MQTTnet.Core.Channel; | |||||
using MQTTnet.Core.Client; | |||||
using MQTTnet.Core.Exceptions; | |||||
using System; | |||||
using System.Net.WebSockets; | |||||
using System.Threading; | |||||
using System.Threading.Tasks; | |||||
namespace MQTTnet.Implementations | |||||
{ | |||||
public sealed class MqttWebSocketChannel : IMqttCommunicationChannel, IDisposable | |||||
{ | |||||
private ClientWebSocket _webSocket = new ClientWebSocket(); | |||||
private const int BufferSize = 4096; | |||||
private const int BufferAmplifier = 20; | |||||
private readonly byte[] WebSocketBuffer = new byte[BufferSize * BufferAmplifier]; | |||||
private int WebSocketBufferSize; | |||||
private int WebSocketBufferOffset; | |||||
public async Task ConnectAsync(MqttClientOptions options) | |||||
{ | |||||
_webSocket = null; | |||||
try | |||||
{ | |||||
_webSocket = new ClientWebSocket(); | |||||
await _webSocket.ConnectAsync(new Uri(options.Server), CancellationToken.None); | |||||
} | |||||
catch (WebSocketException exception) | |||||
{ | |||||
throw new MqttCommunicationException(exception); | |||||
} | |||||
} | |||||
public async Task DisconnectAsync() | |||||
{ | |||||
await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None); | |||||
} | |||||
public void Dispose() | |||||
{ | |||||
_webSocket?.Dispose(); | |||||
} | |||||
public Task ReadAsync(byte[] buffer) | |||||
{ | |||||
return Task.WhenAll(ReadToBufferAsync(buffer)); | |||||
} | |||||
private async Task ReadToBufferAsync(byte[] buffer) | |||||
{ | |||||
var temporaryBuffer = new byte[BufferSize]; | |||||
var offset = 0; | |||||
while (_webSocket.State == WebSocketState.Open) | |||||
{ | |||||
if (WebSocketBufferSize == 0) | |||||
{ | |||||
WebSocketBufferOffset = 0; | |||||
WebSocketReceiveResult response; | |||||
do | |||||
{ | |||||
response = await _webSocket.ReceiveAsync(new ArraySegment<byte>(temporaryBuffer), CancellationToken.None); | |||||
temporaryBuffer.CopyTo(WebSocketBuffer, offset); | |||||
offset += response.Count; | |||||
temporaryBuffer = new byte[BufferSize]; | |||||
} while (!response.EndOfMessage); | |||||
WebSocketBufferSize = response.Count; | |||||
if (response.MessageType == WebSocketMessageType.Close) | |||||
{ | |||||
await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None); | |||||
} | |||||
Buffer.BlockCopy(WebSocketBuffer, 0, buffer, 0, buffer.Length); | |||||
WebSocketBufferSize -= buffer.Length; | |||||
WebSocketBufferOffset += buffer.Length; | |||||
} | |||||
else | |||||
{ | |||||
Buffer.BlockCopy(WebSocketBuffer, WebSocketBufferOffset, buffer, 0, buffer.Length); | |||||
WebSocketBufferSize -= buffer.Length; | |||||
WebSocketBufferOffset += buffer.Length; | |||||
} | |||||
return; | |||||
} | |||||
} | |||||
public Task WriteAsync(byte[] buffer) | |||||
{ | |||||
if (buffer == null) { | |||||
throw new ArgumentNullException(nameof(buffer)); | |||||
} | |||||
try | |||||
{ | |||||
return _webSocket.SendAsync(new ArraySegment<byte>(buffer), WebSocketMessageType.Binary, true, | |||||
CancellationToken.None); | |||||
} | |||||
catch (WebSocketException exception) | |||||
{ | |||||
throw new MqttCommunicationException(exception); | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -110,6 +110,7 @@ | |||||
<DocumentationFile>bin\x64\Release\MQTTnet.XML</DocumentationFile> | <DocumentationFile>bin\x64\Release\MQTTnet.XML</DocumentationFile> | ||||
</PropertyGroup> | </PropertyGroup> | ||||
<ItemGroup> | <ItemGroup> | ||||
<Compile Include="Implementations\MqttWebSocketChannel.cs" /> | |||||
<Compile Include="MqttClientFactory.cs" /> | <Compile Include="MqttClientFactory.cs" /> | ||||
<Compile Include="Implementations\MqttServerAdapter.cs" /> | <Compile Include="Implementations\MqttServerAdapter.cs" /> | ||||
<Compile Include="MqttServerFactory.cs" /> | <Compile Include="MqttServerFactory.cs" /> | ||||
@@ -1,5 +1,6 @@ | |||||
using System; | using System; | ||||
using MQTTnet.Core.Adapter; | using MQTTnet.Core.Adapter; | ||||
using MQTTnet.Core.Channel; | |||||
using MQTTnet.Core.Client; | using MQTTnet.Core.Client; | ||||
using MQTTnet.Core.Serializer; | using MQTTnet.Core.Serializer; | ||||
using MQTTnet.Implementations; | using MQTTnet.Implementations; | ||||
@@ -12,7 +13,23 @@ namespace MQTTnet | |||||
{ | { | ||||
if (options == null) throw new ArgumentNullException(nameof(options)); | if (options == null) throw new ArgumentNullException(nameof(options)); | ||||
return new MqttClient(options, new MqttChannelCommunicationAdapter(new MqttTcpChannel(), new MqttPacketSerializer())); | |||||
return new MqttClient(options, new MqttChannelCommunicationAdapter(GetMqttCommunicationChannel(options), new MqttPacketSerializer())); | |||||
} | |||||
private static IMqttCommunicationChannel GetMqttCommunicationChannel(MqttClientOptions options) | |||||
{ | |||||
switch (options.ConnectionType) | |||||
{ | |||||
case MqttConnectionType.Tcp: | |||||
case MqttConnectionType.Tls: | |||||
return new MqttTcpChannel(); | |||||
case MqttConnectionType.Ws: | |||||
case MqttConnectionType.Wss: | |||||
return new MqttWebSocketChannel(); | |||||
default: | |||||
throw new NotSupportedException(); | |||||
} | |||||
} | } | ||||
} | } | ||||
} | } |
@@ -1,14 +0,0 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Text; | |||||
namespace MQTTnet.Core.Client | |||||
{ | |||||
public enum ConnectionTypes | |||||
{ | |||||
TCP, | |||||
TLS, | |||||
WS, | |||||
WSS | |||||
} | |||||
} |
@@ -25,6 +25,6 @@ namespace MQTTnet.Core.Client | |||||
public MqttProtocolVersion ProtocolVersion { get; set; } = MqttProtocolVersion.V311; | public MqttProtocolVersion ProtocolVersion { get; set; } = MqttProtocolVersion.V311; | ||||
public ConnectionTypes ConnectionType { get; set; } = ConnectionTypes.TCP; | |||||
public MqttConnectionType ConnectionType { get; set; } = MqttConnectionType.Tcp; | |||||
} | } | ||||
} | } |
@@ -0,0 +1,10 @@ | |||||
namespace MQTTnet.Core.Client | |||||
{ | |||||
public enum MqttConnectionType | |||||
{ | |||||
Tcp, | |||||
Tls, | |||||
Ws, | |||||
Wss | |||||
} | |||||
} |
@@ -51,7 +51,7 @@ namespace MQTTnet.TestApp.NetCore | |||||
Server = "localhost", | Server = "localhost", | ||||
ClientId = "XYZ", | ClientId = "XYZ", | ||||
CleanSession = true, | CleanSession = true, | ||||
ConnectionType = ConnectionTypes.WS | |||||
ConnectionType = MqttConnectionType.Ws | |||||
}; | }; | ||||
var client = new MqttClientFactory().CreateMqttClient(options); | var client = new MqttClientFactory().CreateMqttClient(options); | ||||