mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-10-15 00:15:15 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0bdb7e9e44 | ||
|
|
54e3cd7cb7 | ||
|
|
16ad07cfc9 | ||
|
|
dee0adcd8c | ||
|
|
acbf1226ab | ||
|
|
f002744d87 | ||
|
|
9efee7af21 | ||
|
|
9814b10496 | ||
|
|
978694b3c0 | ||
|
|
bc6f155f12 | ||
|
|
d494c2104b | ||
|
|
8035c6859f | ||
|
|
2f10295022 | ||
|
|
e5f32730a7 | ||
|
|
cd257b4d25 | ||
|
|
ce534baa75 | ||
|
|
2b420b8e66 | ||
|
|
35300abfb5 | ||
|
|
3d0ecc05ff | ||
|
|
bfb6ec8856 | ||
|
|
bf1d682a85 | ||
|
|
7d9326a147 | ||
|
|
7fd09009e7 | ||
|
|
9fa5e2dc1f | ||
|
|
15d4ee4d68 | ||
|
|
6ca6a52ae8 | ||
|
|
3d643aa4cb | ||
|
|
9739d23b6f | ||
|
|
c43acf7dac | ||
|
|
995c226893 | ||
|
|
8574bbb64f | ||
|
|
b2bb545947 | ||
|
|
bf8dae67de | ||
|
|
c6309da015 | ||
|
|
2e83a1010e | ||
|
|
d5efb31e3a | ||
|
|
b77798f885 | ||
|
|
5923c7c408 | ||
|
|
7dfb6fdc52 | ||
|
|
4e40b2ddf3 | ||
|
|
4e9abd41c0 | ||
|
|
7240b86d98 | ||
|
|
211853d519 | ||
|
|
f39589a8c1 | ||
|
|
e387971292 | ||
|
|
4fc1912ee8 | ||
|
|
31a704b675 | ||
|
|
c5c088300f | ||
|
|
ccea8e6eb5 | ||
|
|
ac60ed4559 | ||
|
|
8026263a27 | ||
|
|
6fa6abe6f0 | ||
|
|
e8d5bd7bfb | ||
|
|
2a03c1f1f4 | ||
|
|
0c6b92536e | ||
|
|
dddc1ec112 | ||
|
|
8b376874c0 | ||
|
|
0322660561 | ||
|
|
a7205ddf02 | ||
|
|
64b38487bc | ||
|
|
bc20850fba | ||
|
|
66048931a2 | ||
|
|
8444b6b3be | ||
|
|
baac26c5f6 | ||
|
|
56e9e15ac0 | ||
|
|
beb54acc2f | ||
|
|
d573423372 | ||
|
|
410bc211a5 | ||
|
|
e65c3f2f2b | ||
|
|
cf947415e8 | ||
|
|
c32a606a70 | ||
|
|
78c6059668 | ||
|
|
7c2adf636f | ||
|
|
d5ac8c7183 | ||
|
|
7a7fab0907 | ||
|
|
c758ae89f4 | ||
|
|
caea7910b4 | ||
|
|
6192685671 | ||
|
|
26eed30dbf | ||
|
|
20779dfece | ||
|
|
6bcfa436ea | ||
|
|
e62316105f | ||
|
|
9e318fc40d | ||
|
|
4b407d5213 | ||
|
|
841a22b2b2 | ||
|
|
840d8ab4fd | ||
|
|
2e3e58ed73 | ||
|
|
eb48f87667 | ||
|
|
a164c120d5 | ||
|
|
9dddd40dbe | ||
|
|
ad778636cf | ||
|
|
78360bc24a | ||
|
|
3480d9ac35 | ||
|
|
ae72bc3f99 | ||
|
|
c8bf36044b | ||
|
|
b05cb0d2ea | ||
|
|
fd1880f1ff | ||
|
|
70c5bd1875 | ||
|
|
00ac9ce961 | ||
|
|
1d455221e4 | ||
|
|
31b4eb7937 | ||
|
|
c88b46783b | ||
|
|
39edd22a9d | ||
|
|
afe25db0d3 | ||
|
|
f86a5af794 | ||
|
|
e6de4d014e | ||
|
|
b38df9eecd | ||
|
|
54abf84e96 | ||
|
|
66ebdfd61a | ||
|
|
4fe7c13d8e | ||
|
|
2578b380f9 | ||
|
|
cadf9f797f | ||
|
|
fd50e4e260 | ||
|
|
1cbec27b7f | ||
|
|
8d110c44f7 | ||
|
|
66a77db851 | ||
|
|
0505efbac1 | ||
|
|
99614f2e15 | ||
|
|
08e41244fc | ||
|
|
4e74b59796 | ||
|
|
2b6b268b4e | ||
|
|
09aacf011e | ||
|
|
9bea46adcb | ||
|
|
a10183628b | ||
|
|
7e0017d14a | ||
|
|
62e9a333c7 | ||
|
|
4838da48ef | ||
|
|
b5a43c416a | ||
|
|
c1297eadd4 | ||
|
|
06886fe6c2 | ||
|
|
137d1d181b | ||
|
|
97d0286a31 | ||
|
|
782dcffc62 | ||
|
|
40f7a7c593 | ||
|
|
44e5b2c2fd | ||
|
|
f844a8be5e | ||
|
|
449d8aca65 | ||
|
|
9b29f8c23a | ||
|
|
5e79581662 | ||
|
|
79a2952f94 | ||
|
|
8077d8dfeb | ||
|
|
d72ac9694e | ||
|
|
5e0bc17341 | ||
|
|
932aa1fd39 | ||
|
|
2c45e9fedd | ||
|
|
c1352d50c1 | ||
|
|
2499d29196 | ||
|
|
fbf1cbc76e | ||
|
|
719838de3b | ||
|
|
ba66c108fa | ||
|
|
5287bf5cac | ||
|
|
10740f12c4 | ||
|
|
3e6edb92c5 | ||
|
|
dc7b9414d9 | ||
|
|
8df8cb1eb6 | ||
|
|
e14ea9a240 | ||
|
|
57287a5303 | ||
|
|
febc8f774d | ||
|
|
e51042ed04 | ||
|
|
910c123887 | ||
|
|
e0899abaac | ||
|
|
9df1bb2ff2 | ||
|
|
7b987635f7 | ||
|
|
8c37cf16c0 | ||
|
|
c569c10740 | ||
|
|
325541642e | ||
|
|
97d91ec0f8 | ||
|
|
17db90d5ab | ||
|
|
e7a7bb1a7b | ||
|
|
b621806a7c | ||
|
|
7720a3f091 | ||
|
|
b22b02a0b6 | ||
|
|
d14088512b | ||
|
|
62527a91ac | ||
|
|
f9e4a97eb8 | ||
|
|
ff45be1f71 | ||
|
|
aaed2ed790 | ||
|
|
d9501de252 | ||
|
|
9fdcb08364 | ||
|
|
9f4f37bb51 | ||
|
|
49cf364352 | ||
|
|
b9a73cc23d | ||
|
|
357fd844d7 | ||
|
|
74a5522965 | ||
|
|
84dab18c2a | ||
|
|
9e90111b37 | ||
|
|
ffb1645f14 | ||
|
|
6448b217d5 | ||
|
|
1b84ab40ce | ||
|
|
af8d1ec6be | ||
|
|
04a4080768 | ||
|
|
942bf70582 | ||
|
|
b9f0ee2317 | ||
|
|
cb09bac246 | ||
|
|
109e4b2cf1 | ||
|
|
e4cf2978e6 | ||
|
|
17b74f20c9 | ||
|
|
ae11e31048 | ||
|
|
e455ce9c78 | ||
|
|
41df5cdd8b | ||
|
|
383225d7f4 | ||
|
|
a62c15ad1a | ||
|
|
5300742543 | ||
|
|
cd09a2f176 | ||
|
|
11ba46d140 | ||
|
|
e006377e64 | ||
|
|
786fa53334 | ||
|
|
9bc34e4406 | ||
|
|
c915af8dad | ||
|
|
98fbbc2864 | ||
|
|
d2049d63d6 | ||
|
|
63a12d4d40 | ||
|
|
9051f8275b | ||
|
|
a5f33bc244 | ||
|
|
b23d25217b | ||
|
|
1df6798934 | ||
|
|
9afff6d8a0 | ||
|
|
ec41efd860 | ||
|
|
580a431fbf | ||
|
|
c06d84e834 | ||
|
|
06a299d212 | ||
|
|
f90985a1f9 | ||
|
|
677beddf07 | ||
|
|
7154a7b3dc | ||
|
|
7de3d79d09 | ||
|
|
54a0265ef9 | ||
|
|
8f96acaec4 | ||
|
|
cea984cc2f | ||
|
|
fcf748e980 | ||
|
|
e58cb3c9cd | ||
|
|
3587a8ebd1 | ||
|
|
3193381b69 | ||
|
|
7a9ddfc2b5 | ||
|
|
447b2aa2c6 | ||
|
|
b3eaeea8a4 | ||
|
|
45b4c05867 | ||
|
|
49da66dcff | ||
|
|
bb9d5586f0 | ||
|
|
c5d22f4b27 | ||
|
|
e57ec11ed8 | ||
|
|
56acc82445 | ||
|
|
6e828d091f | ||
|
|
e1d8697331 | ||
|
|
90098c2006 | ||
|
|
b3a25fb9e8 | ||
|
|
f85ad4965b | ||
|
|
f5696b6fc0 | ||
|
|
c26555aca5 | ||
|
|
0accb467b5 | ||
|
|
3b34fd4750 | ||
|
|
abb216c055 | ||
|
|
1760dc7d35 | ||
|
|
034e7fd750 | ||
|
|
76c7b319b8 | ||
|
|
09010e95a2 | ||
|
|
2341d66192 | ||
|
|
21586b7710 | ||
|
|
b426ae1198 | ||
|
|
e9db9d65ac | ||
|
|
59dcbf4c0c | ||
|
|
a204d233f6 | ||
|
|
26cd84c88c | ||
|
|
5f77255893 | ||
|
|
8856a7aa34 | ||
|
|
30754433eb | ||
|
|
751ac19c4b | ||
|
|
1415e4b878 | ||
|
|
e56203faac | ||
|
|
64c997a4a2 | ||
|
|
391ee34a0f | ||
|
|
88de5047bc | ||
|
|
d020e809a1 | ||
|
|
8b2b8d41df | ||
|
|
40c33ac639 | ||
|
|
002e4b8d69 | ||
|
|
c060c379d3 | ||
|
|
4d13407a83 | ||
|
|
d737f550c7 | ||
|
|
5e8d5b9219 | ||
|
|
f98f21ffdf | ||
|
|
ec9e64ebfb | ||
|
|
39672970e2 | ||
|
|
5becf7980a | ||
|
|
c7c84ad39b | ||
|
|
8773117493 | ||
|
|
bb697e1bd9 | ||
|
|
e1a9aa7d03 | ||
|
|
312773d054 | ||
|
|
1ee48a978f | ||
|
|
5ddb6b77e2 | ||
|
|
e0284886b5 | ||
|
|
44c07c2034 | ||
|
|
f6b38081fc | ||
|
|
c570509628 | ||
|
|
e98ff1d6d6 | ||
|
|
0f54663789 | ||
|
|
f91d935708 | ||
|
|
fb8b60fccc | ||
|
|
ad8506cc30 | ||
|
|
d6f88acb8d | ||
|
|
333fc4c387 | ||
|
|
5f9214e9a2 | ||
|
|
f974dcb647 | ||
|
|
2cb1c544f5 | ||
|
|
644e948489 | ||
|
|
d12d2648b7 | ||
|
|
0ee3c8e135 | ||
|
|
bc959a8c3d | ||
|
|
69781d822a | ||
|
|
611fabcd01 | ||
|
|
24ab285066 | ||
|
|
ee6ec14bf8 | ||
|
|
f9d67e2cd2 | ||
|
|
a72516fdcc | ||
|
|
ebfc6aab6b | ||
|
|
9c20edd3d0 | ||
|
|
b02b0ccd7c | ||
|
|
1bcf7ec29c | ||
|
|
e54b4f58df | ||
|
|
30540729df | ||
|
|
77b80dc53e | ||
|
|
861d3dfa0f | ||
|
|
e0235aedff | ||
|
|
8411382c42 | ||
|
|
f8078a6894 | ||
|
|
f66e8c8b3a | ||
|
|
acc269e702 | ||
|
|
5bcda12ca9 | ||
|
|
8de87c8772 | ||
|
|
b956623234 | ||
|
|
6e615f85eb | ||
|
|
b70b469201 | ||
|
|
c4b706f8ad | ||
|
|
9d971003d5 | ||
|
|
c322817cb1 | ||
|
|
0c4c93784d | ||
|
|
4cae09a4e9 | ||
|
|
c775867db4 | ||
|
|
8672fb2792 | ||
|
|
48c47c6fcf | ||
|
|
38904a6213 | ||
|
|
bdadfa1c26 | ||
|
|
e38142399e | ||
|
|
5cbbfedcce | ||
|
|
bc97575760 | ||
|
|
afd56a7f6f | ||
|
|
0844abd7e5 | ||
|
|
3625363fcf | ||
|
|
9692ae5617 | ||
|
|
c01edb192f | ||
|
|
acbf9153f4 | ||
|
|
b6cd4638b9 | ||
|
|
683d97dc14 | ||
|
|
ad77dc0bea | ||
|
|
587622af81 | ||
|
|
eaf27c1e12 | ||
|
|
1c78cd4c31 | ||
|
|
d1ab867afc | ||
|
|
522a385523 | ||
|
|
40dd620f90 | ||
|
|
7dfc19e588 | ||
|
|
0b002e8d11 | ||
|
|
d2e481719c | ||
|
|
7e48746944 | ||
|
|
f481c704c9 | ||
|
|
6e5b991406 | ||
|
|
e07ead53a9 | ||
|
|
e6e88e132b | ||
|
|
8778848301 | ||
|
|
5fbef7beb5 | ||
|
|
387a3bc281 | ||
|
|
11ccb951aa | ||
|
|
3a6df2eab0 | ||
|
|
c5e3bc70b1 | ||
|
|
9c46b8fd75 | ||
|
|
d5dc3a1361 | ||
|
|
f0e8d8e98c | ||
|
|
589fd22a63 | ||
|
|
83821453a0 | ||
|
|
d335b6393e | ||
|
|
6240d60003 | ||
|
|
4519c4fe3d | ||
|
|
e969b2f31f | ||
|
|
44ae9093f9 | ||
|
|
fcd3bdf297 | ||
|
|
cbb2f43047 | ||
|
|
5e74869dbc | ||
|
|
98cac153e6 | ||
|
|
d076aac421 | ||
|
|
b51a603aa9 | ||
|
|
2fe520ad46 | ||
|
|
c8846329f3 | ||
|
|
08f2db05a9 | ||
|
|
5427b488c3 | ||
|
|
ec24f03324 | ||
|
|
4ef16cfd8d | ||
|
|
177c7cbf03 | ||
|
|
8025717658 | ||
|
|
275d8d9f0b | ||
|
|
30df7d4c9e | ||
|
|
019ef1398e | ||
|
|
4a6f517e91 | ||
|
|
d560cb4c9e | ||
|
|
ba16ae6568 | ||
|
|
3b2c40d49e | ||
|
|
9894dbef87 | ||
|
|
dd447f24d2 | ||
|
|
3a250723d7 | ||
|
|
e0797e5d14 | ||
|
|
d0f1c18a2f | ||
|
|
e6d811ea7c | ||
|
|
bb6eb246e8 | ||
|
|
0e666f0316 | ||
|
|
0811938466 | ||
|
|
466b6a25cd | ||
|
|
15a564dcfe | ||
|
|
f85721fbf5 | ||
|
|
3b366f91fc | ||
|
|
418f2b2c3f | ||
|
|
8fa91736b6 | ||
|
|
a18c533603 | ||
|
|
a9b394401b | ||
|
|
c257e5cb77 | ||
|
|
93863ece0d | ||
|
|
29918fb454 | ||
|
|
412cf115a5 | ||
|
|
8dab5d3800 | ||
|
|
2cd3bd7588 | ||
|
|
0e0fcabc04 | ||
|
|
e80d21e852 | ||
|
|
a68343a306 | ||
|
|
d594b1447f | ||
|
|
1ca9b0bd62 | ||
|
|
1c0b2b55b6 | ||
|
|
41ca15120d | ||
|
|
49b1706495 | ||
|
|
daf22c762f | ||
|
|
3529724bdc | ||
|
|
56a86b6127 | ||
|
|
7e6f4453ad | ||
|
|
84a647e712 | ||
|
|
d10f29bbc6 | ||
|
|
41edc3a1ec | ||
|
|
3f080e04b9 | ||
|
|
4386ef1e56 | ||
|
|
df1dd0eec7 | ||
|
|
3954777d20 | ||
|
|
b52645f77a | ||
|
|
65e6f96e59 | ||
|
|
24c35de330 | ||
|
|
d052f409b6 | ||
|
|
f9eb6ed85e | ||
|
|
11ade8c3df | ||
|
|
fe88b7551c | ||
|
|
bddfadaefa | ||
|
|
40b1be075c | ||
|
|
680bc4658f | ||
|
|
870ed00e3c | ||
|
|
1f3b24d20f | ||
|
|
bc19f66812 | ||
|
|
9915cc4492 | ||
|
|
ad4585989a | ||
|
|
ea5616002a | ||
|
|
1f8a781dc2 | ||
|
|
fd63cf0086 | ||
|
|
60bdca8716 | ||
|
|
a2971aa03b | ||
|
|
05a3acb229 | ||
|
|
47257baba9 | ||
|
|
906a0ba189 | ||
|
|
69b02f3224 | ||
|
|
a7d0ad5f43 | ||
|
|
59f07c868d | ||
|
|
4517d80704 | ||
|
|
9bca4aa53f | ||
|
|
4c4539509c | ||
|
|
af3cebb266 | ||
|
|
d22528ca46 | ||
|
|
67267c05fd | ||
|
|
d23ab88add | ||
|
|
1d78d675f7 | ||
|
|
3079ad37ce | ||
|
|
c7b7d56229 | ||
|
|
c97b97c677 | ||
|
|
96a62551b2 | ||
|
|
300c9b9ccb | ||
|
|
342085db40 | ||
|
|
862f76fea2 |
+1
-6
@@ -1,8 +1,3 @@
|
||||
exclude:
|
||||
- /Frameworks/.*
|
||||
- /MQTTnet.TestApp.NetCore/Program.cs
|
||||
- /Tests/MQTTnet.TestApp.NetFramework/.*
|
||||
- /Tests/MQTTnet.TestApp.UniversalWindows/.*
|
||||
component_depth: 1
|
||||
component_depth: 2
|
||||
languages:
|
||||
- csharp
|
||||
|
||||
+5
-1
@@ -197,7 +197,7 @@ ClientBin/
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.jfm
|
||||
*.pfx
|
||||
# *.pfx
|
||||
*.publishsettings
|
||||
orleans.codegen.cs
|
||||
|
||||
@@ -289,3 +289,7 @@ __pycache__/
|
||||
|
||||
|
||||
Build/nuget.exe
|
||||
*.js
|
||||
*.map
|
||||
|
||||
/Tests/MQTTnet.TestApp.NetCore/RetainedMessages.json
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0"?>
|
||||
<package >
|
||||
<metadata>
|
||||
<id>MQTTnet.AspNetCore</id>
|
||||
<version>0.0.0</version>
|
||||
<authors>Christian Kratky</authors>
|
||||
<owners>Christian Kratky</owners>
|
||||
<licenseUrl>https://github.com/chkr1011/MQTTnet/blob/master/LICENSE</licenseUrl>
|
||||
<projectUrl>https://github.com/chkr1011/MQTTnet</projectUrl>
|
||||
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<description>This is a support library to integrate MQTTnet into AspNetCore.</description>
|
||||
<releaseNotes>* Updated to MQTTnet 2.8.1.
|
||||
* For more release notes please check the MQTTnet release notes.
|
||||
</releaseNotes>
|
||||
<copyright>Copyright Christian Kratky 2016-2018</copyright>
|
||||
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin</tags>
|
||||
<dependencies>
|
||||
<dependency id="MQTTnet" version="2.8.1" />
|
||||
<dependency id="Microsoft.AspNetCore.Connections.Abstractions" version="2.1.0" />
|
||||
<dependency id="Microsoft.AspNetCore.WebSockets" version="2.0.1" />
|
||||
<dependency id="Microsoft.Extensions.Hosting.Abstractions" version="2.0.1" />
|
||||
</dependencies>
|
||||
</metadata>
|
||||
|
||||
<files>
|
||||
<!-- .NET Standard 2.0 -->
|
||||
<file src="..\Source\MQTTnet.AspNetCore\bin\Release\netstandard2.0\MQTTnet.AspNetCore.*" target="lib\netstandard2.0\"/>
|
||||
</files>
|
||||
</package>
|
||||
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0"?>
|
||||
<package >
|
||||
<metadata>
|
||||
<id>MQTTnet.Extensions.ManagedClient</id>
|
||||
<version>0.0.0</version>
|
||||
<authors>Christian Kratky</authors>
|
||||
<owners>Christian Kratky</owners>
|
||||
<licenseUrl>https://github.com/chkr1011/MQTTnet/blob/master/LICENSE</licenseUrl>
|
||||
<projectUrl>https://github.com/chkr1011/MQTTnet</projectUrl>
|
||||
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<description>This is an extension library which provides a managed MQTT client with additional features using MQTTnet.</description>
|
||||
<releaseNotes>* Updated to MQTTnet 2.8.1.
|
||||
* For more release notes please check the MQTTnet release notes.
|
||||
</releaseNotes>
|
||||
<copyright>Copyright Christian Kratky 2016-2018</copyright>
|
||||
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin</tags>
|
||||
<dependencies>
|
||||
<dependency id="MQTTnet" version="2.8.1" />
|
||||
</dependencies>
|
||||
</metadata>
|
||||
|
||||
<files>
|
||||
<!-- .NET Standard 1.3 -->
|
||||
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\netstandard1.3\MQTTnet.Extensions.ManagedClient.*" target="lib\netstandard1.3\"/>
|
||||
|
||||
<!-- .NET Standard 2.0 -->
|
||||
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\netstandard2.0\MQTTnet.Extensions.ManagedClient.*" target="lib\netstandard2.0\"/>
|
||||
|
||||
<!-- Universal Windows -->
|
||||
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\uap10.0\MQTTnet.Extensions.ManagedClient.*" target="lib\uap10.0\"/>
|
||||
|
||||
<!-- .NET Framework -->
|
||||
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\net452\MQTTnet.Extensions.ManagedClient.*" target="lib\net452\"/>
|
||||
|
||||
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\netstandard2.0\MQTTnet.Extensions.ManagedClient.*" target="lib\net461\"/>
|
||||
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\netstandard2.0\MQTTnet.Extensions.ManagedClient.*" target="lib\net472\"/>
|
||||
</files>
|
||||
</package>
|
||||
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0"?>
|
||||
<package >
|
||||
<metadata>
|
||||
<id>MQTTnet.Extensions.Rpc</id>
|
||||
<version>0.0.0</version>
|
||||
<authors>Christian Kratky</authors>
|
||||
<owners>Christian Kratky</owners>
|
||||
<licenseUrl>https://github.com/chkr1011/MQTTnet/blob/master/LICENSE</licenseUrl>
|
||||
<projectUrl>https://github.com/chkr1011/MQTTnet</projectUrl>
|
||||
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<description>This is an extension library which allows executing synchronous device calls including a response using MQTTnet.</description>
|
||||
<releaseNotes>* Updated to MQTTnet 2.8.1.
|
||||
* For more release notes please check the MQTTnet release notes.
|
||||
</releaseNotes>
|
||||
<copyright>Copyright Christian Kratky 2016-2018</copyright>
|
||||
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin</tags>
|
||||
<dependencies>
|
||||
<dependency id="MQTTnet" version="2.8.1" />
|
||||
</dependencies>
|
||||
</metadata>
|
||||
|
||||
<files>
|
||||
<!-- .NET Standard 1.3 -->
|
||||
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\netstandard1.3\MQTTnet.Extensions.Rpc.*" target="lib\netstandard1.3\"/>
|
||||
|
||||
<!-- .NET Standard 2.0 -->
|
||||
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\netstandard2.0\MQTTnet.Extensions.Rpc.*" target="lib\netstandard2.0\"/>
|
||||
|
||||
<!-- Universal Windows -->
|
||||
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\uap10.0\MQTTnet.Extensions.Rpc.*" target="lib\uap10.0\"/>
|
||||
|
||||
<!-- .NET Framework -->
|
||||
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\net452\MQTTnet.Extensions.Rpc.*" target="lib\net452\"/>
|
||||
|
||||
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\netstandard2.0\MQTTnet.Extensions.Rpc.*" target="lib\net461\"/>
|
||||
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\netstandard2.0\MQTTnet.Extensions.Rpc.*" target="lib\net462\"/>
|
||||
</files>
|
||||
</package>
|
||||
+33
-20
@@ -2,47 +2,60 @@
|
||||
<package >
|
||||
<metadata>
|
||||
<id>MQTTnet</id>
|
||||
<version>2.3.1</version>
|
||||
<version>0.0.0</version>
|
||||
<authors>Christian Kratky</authors>
|
||||
<owners>Christian Kratky</owners>
|
||||
<licenseUrl>https://github.com/chkr1011/MQTTnet/blob/master/LICENSE</licenseUrl>
|
||||
<projectUrl>https://github.com/chkr1011/MQTTnet</projectUrl>
|
||||
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<description>MQTTnet is a .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker).</description>
|
||||
<releaseNotes>* [Server] Fixed an issue when accepting a new connection (UWP only) (Thanks to haeberle)
|
||||
[Core] Fixed a dead lock while sending messages (Thanks to 1liveowl, JanEggers)
|
||||
[Client] The client is no longer sending packets before receiving has started
|
||||
[Core] Minor changes and improvements
|
||||
<description>MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker).</description>
|
||||
<releaseNotes>* [Core] Performance optimizations.
|
||||
* [Core] Fixed a bug which prevents receiving large packets (UWP only)
|
||||
* [Client] The ManagedClient options now allow configuring the interval for connection checks.
|
||||
* [Server] Added the Endpoint of the Adapter (remote IP and port) to the connection validation callback.
|
||||
* [Server] The ipv4 and ipv6 endpoint can be disabled now by setting the bound IP address to _None_.
|
||||
* [Server] Fix a bug in the keep alive monitor which caused high CPU load (thanks to @GarageGadget).
|
||||
</releaseNotes>
|
||||
<copyright>Copyright Christian Kratky 2016-2017</copyright>
|
||||
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M</tags>
|
||||
<copyright>Copyright Christian Kratky 2016-2018</copyright>
|
||||
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin</tags>
|
||||
<dependencies>
|
||||
|
||||
<group targetFramework="netstandard1.3">
|
||||
<dependency id="NETStandard.Library" version="1.6.1" />
|
||||
<dependency id="NETStandard.Library" version="1.3.0" />
|
||||
<dependency id="System.Net.Security" version="4.3.2" />
|
||||
<dependency id="System.Net.WebSockets" version="4.3.0" />
|
||||
<dependency id="System.Net.WebSockets.Client" version="4.3.1" />
|
||||
</group>
|
||||
<group targetFramework="netstandard2.0">
|
||||
<dependency id="NETStandard.Library" version="2.0.0" />
|
||||
<dependency id="System.Net.Security" version="4.3.2" />
|
||||
<dependency id="System.Net.WebSockets" version="4.3.0" />
|
||||
<dependency id="System.Net.WebSockets.Client" version="4.3.2" />
|
||||
</group>
|
||||
|
||||
<group targetFramework="uap10.0">
|
||||
<dependency id="Microsoft.NETCore.UniversalWindowsPlatform" version="5.4.0" />
|
||||
<dependency id="Microsoft.NETCore.UniversalWindowsPlatform" version="6.1.4" />
|
||||
</group>
|
||||
<group targetFramework="net452">
|
||||
</group>
|
||||
<group targetFramework="net461">
|
||||
</group>
|
||||
|
||||
</dependencies>
|
||||
</metadata>
|
||||
|
||||
<files>
|
||||
<!-- .NET Standard 1.3 -->
|
||||
<file src="..\Frameworks\MQTTnet.Netstandard\bin\Release\netstandard1.3\MQTTnet.Core.*" target="lib\netstandard1.3\"/>
|
||||
<file src="..\Frameworks\MQTTnet.Netstandard\bin\Release\netstandard1.3\MQTTnet.*" target="lib\netstandard1.3\"/>
|
||||
<file src="..\Source\MQTTnet\bin\Release\netstandard1.3\MQTTnet.*" target="lib\netstandard1.3\"/>
|
||||
|
||||
<!-- .NET Standard 2.0 -->
|
||||
<file src="..\Source\MQTTnet\bin\Release\netstandard2.0\MQTTnet.*" target="lib\netstandard2.0\"/>
|
||||
|
||||
<!-- Universal Windows -->
|
||||
<file src="..\Frameworks\MQTTnet.UniversalWindows\bin\Release\MQTTnet.Core.*" target="lib\uap10.0\"/>
|
||||
<file src="..\Frameworks\MQTTnet.UniversalWindows\bin\Release\MQTTnet.*" target="lib\uap10.0\"/>
|
||||
<file src="..\Source\MQTTnet\bin\Release\uap10.0\MQTTnet.*" target="lib\uap10.0\"/>
|
||||
|
||||
<!-- .NET Framework -->
|
||||
<file src="..\Frameworks\MQTTnet.NetFramework\bin\Release\MQTTnet.Core.*" target="lib\net45\"/>
|
||||
<file src="..\Frameworks\MQTTnet.NetFramework\bin\Release\MQTTnet.*" target="lib\net45\"/>
|
||||
|
||||
<file src="..\Source\MQTTnet\bin\Release\net452\MQTTnet.*" target="lib\net452\"/>
|
||||
|
||||
<file src="..\Source\MQTTnet\bin\Release\netstandard2.0\MQTTnet.*" target="lib\net461\"/>
|
||||
<file src="..\Source\MQTTnet\bin\Release\netstandard2.0\MQTTnet.*" target="lib\net472\"/>
|
||||
</files>
|
||||
</package>
|
||||
+44
-9
@@ -1,12 +1,47 @@
|
||||
param([string]$version)
|
||||
param([string]$assemblyVersion, [string]$nugetVersion)
|
||||
|
||||
if ([string]::IsNullOrEmpty($version)) {$version = "0.0.1"}
|
||||
if ([string]::IsNullOrEmpty($assemblyVersion)) {$assemblyVersion = "0.0.1"}
|
||||
if ([string]::IsNullOrEmpty($nugetVersion)) {$nugetVersion = "0.0.1"}
|
||||
|
||||
$msbuild = "MSBuild.exe"
|
||||
&$msbuild ..\Frameworks\MQTTnet.NetFramework\MQTTnet.NetFramework.csproj /t:Build /p:Configuration="Release"
|
||||
&$msbuild ..\Frameworks\MQTTnet.Netstandard\MQTTnet.Netstandard.csproj /t:Build /p:Configuration="Release"
|
||||
&$msbuild ..\Frameworks\MQTTnet.UniversalWindows\MQTTnet.UniversalWindows.csproj /t:Build /p:Configuration="Release"
|
||||
Write-Host "Assembly version = $assemblyVersion"
|
||||
Write-Host "Nuget version = $nugetVersion"
|
||||
Write-Host
|
||||
|
||||
Remove-Item .\NuGet -Force -Recurse
|
||||
New-Item -ItemType Directory -Force -Path .\NuGet
|
||||
.\NuGet.exe pack MQTTnet.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $version
|
||||
$vswhere = ${Env:\ProgramFiles(x86)} + '\Microsoft Visual Studio\Installer\vswhere'
|
||||
|
||||
$path = &$vswhere -latest -products * -requires Microsoft.Component.MSBuild -property installationPath
|
||||
if ($path) {
|
||||
$msbuild = join-path $path 'MSBuild\15.0\Bin\MSBuild.exe'
|
||||
|
||||
# Build the core library
|
||||
&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net452" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net461" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard1.3" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="uap10.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
|
||||
# Build the ASP.NET Core 2.0 extension
|
||||
&$msbuild ..\Source\MQTTnet.AspNetCore\MQTTnet.AspNetCore.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
|
||||
# Build the RPC extension
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net452" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net461" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard1.3" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="uap10.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
|
||||
# Build the Managed Client extension
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net452" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net461" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard1.3" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="uap10.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
|
||||
Remove-Item .\NuGet -Force -Recurse -ErrorAction SilentlyContinue
|
||||
|
||||
New-Item -ItemType Directory -Force -Path .\NuGet
|
||||
.\nuget.exe pack MQTTnet.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $nugetVersion
|
||||
.\nuget.exe pack MQTTnet.AspNetCore.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $nugetVersion
|
||||
.\nuget.exe pack MQTTnet.Extensions.Rpc.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $nugetVersion
|
||||
.\nuget.exe pack MQTTnet.Extensions.ManagedClient.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $nugetVersion
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,9 @@
|
||||
param([string]$apiKey)
|
||||
|
||||
$files = Get-ChildItem -Path ".\NuGet" -Filter "*.nupkg"
|
||||
foreach ($file in $files)
|
||||
{
|
||||
Write-Host "Uploading: " $file
|
||||
|
||||
.\nuget.exe push $file.Fullname $apiKey -NoSymbols -Source https://api.nuget.org/v3/index.json
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
# Import codeSigningKey.pfx
|
||||
In order to import the key for code signing on a new developer machine use the following command within the VS developer command line:
|
||||
|
||||
> sn –i codeSigningKey.pfx VS_KEY_EFCA4C5B6DFD4B4F
|
||||
|
||||
The container name may be different.
|
||||
|
||||
# Check if the assembly has a strong name
|
||||
> sn -vf MQTTnet.dll
|
||||
@@ -1,129 +0,0 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Security;
|
||||
using System.Net.Sockets;
|
||||
using System.Security.Authentication;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Core.Adapter;
|
||||
using MQTTnet.Core.Diagnostics;
|
||||
using MQTTnet.Core.Serializer;
|
||||
using MQTTnet.Core.Server;
|
||||
|
||||
namespace MQTTnet.Implementations
|
||||
{
|
||||
public class MqttServerAdapter : IMqttServerAdapter, IDisposable
|
||||
{
|
||||
private CancellationTokenSource _cancellationTokenSource;
|
||||
private Socket _defaultEndpointSocket;
|
||||
private Socket _tlsEndpointSocket;
|
||||
private X509Certificate2 _tlsCertificate;
|
||||
|
||||
private bool _isRunning;
|
||||
|
||||
public event EventHandler<MqttClientConnectedEventArgs> ClientConnected;
|
||||
|
||||
public void Start(MqttServerOptions options)
|
||||
{
|
||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||
|
||||
if (_isRunning) throw new InvalidOperationException("Server is already started.");
|
||||
_isRunning = true;
|
||||
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
if (options.DefaultEndpointOptions.IsEnabled)
|
||||
{
|
||||
_defaultEndpointSocket = new Socket(SocketType.Stream, ProtocolType.Tcp);
|
||||
_defaultEndpointSocket.Bind(new IPEndPoint(IPAddress.Any, options.GetDefaultEndpointPort()));
|
||||
_defaultEndpointSocket.Listen(options.ConnectionBacklog);
|
||||
|
||||
Task.Run(() => AcceptDefaultEndpointConnectionsAsync(_cancellationTokenSource.Token), _cancellationTokenSource.Token);
|
||||
}
|
||||
|
||||
if (options.TlsEndpointOptions.IsEnabled)
|
||||
{
|
||||
if (options.TlsEndpointOptions.Certificate == null)
|
||||
{
|
||||
throw new ArgumentException("TLS certificate is not set.");
|
||||
}
|
||||
|
||||
_tlsCertificate = new X509Certificate2(options.TlsEndpointOptions.Certificate);
|
||||
|
||||
_tlsEndpointSocket = new Socket(SocketType.Stream, ProtocolType.Tcp);
|
||||
_tlsEndpointSocket.Bind(new IPEndPoint(IPAddress.Any, options.GetTlsEndpointPort()));
|
||||
_tlsEndpointSocket.Listen(options.ConnectionBacklog);
|
||||
|
||||
Task.Run(() => AcceptTlsEndpointConnectionsAsync(_cancellationTokenSource.Token), _cancellationTokenSource.Token);
|
||||
}
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
_isRunning = false;
|
||||
|
||||
_cancellationTokenSource?.Cancel(false);
|
||||
_cancellationTokenSource?.Dispose();
|
||||
_cancellationTokenSource = null;
|
||||
|
||||
_defaultEndpointSocket?.Dispose();
|
||||
_defaultEndpointSocket = null;
|
||||
|
||||
_tlsEndpointSocket?.Dispose();
|
||||
_tlsEndpointSocket = null;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
|
||||
private async Task AcceptDefaultEndpointConnectionsAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
var clientSocket = await Task.Factory.FromAsync(_defaultEndpointSocket.BeginAccept, _defaultEndpointSocket.EndAccept, null).ConfigureAwait(false);
|
||||
|
||||
var tcpChannel = new MqttTcpChannel(clientSocket, null);
|
||||
var clientAdapter = new MqttChannelCommunicationAdapter(tcpChannel, new MqttPacketSerializer());
|
||||
ClientConnected?.Invoke(this, new MqttClientConnectedEventArgs(clientSocket.RemoteEndPoint.ToString(), clientAdapter));
|
||||
}
|
||||
catch (Exception exception) when (!(exception is ObjectDisposedException))
|
||||
{
|
||||
MqttTrace.Error(nameof(MqttServerAdapter), exception, "Error while accepting connection at default endpoint.");
|
||||
|
||||
//excessive CPU consumed if in endless loop of socket errors
|
||||
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task AcceptTlsEndpointConnectionsAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
var clientSocket = await Task.Factory.FromAsync(_tlsEndpointSocket.BeginAccept, _tlsEndpointSocket.EndAccept, null).ConfigureAwait(false);
|
||||
|
||||
var sslStream = new SslStream(new NetworkStream(clientSocket));
|
||||
await sslStream.AuthenticateAsServerAsync(_tlsCertificate, false, SslProtocols.Tls12, false).ConfigureAwait(false);
|
||||
|
||||
var tcpChannel = new MqttTcpChannel(clientSocket, sslStream);
|
||||
var clientAdapter = new MqttChannelCommunicationAdapter(tcpChannel, new MqttPacketSerializer());
|
||||
ClientConnected?.Invoke(this, new MqttClientConnectedEventArgs(clientSocket.RemoteEndPoint.ToString(), clientAdapter));
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
MqttTrace.Error(nameof(MqttServerAdapter), exception, "Error while accepting connection at TLS endpoint.");
|
||||
|
||||
//excessive CPU consumed if in endless loop of socket errors
|
||||
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
using System;
|
||||
using System.Net.Security;
|
||||
using System.Net.Sockets;
|
||||
using System.Security.Authentication;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Core.Channel;
|
||||
using MQTTnet.Core.Client;
|
||||
using System.IO;
|
||||
|
||||
namespace MQTTnet.Implementations
|
||||
{
|
||||
public sealed class MqttTcpChannel : IMqttCommunicationChannel, IDisposable
|
||||
{
|
||||
// ReSharper disable once MemberCanBePrivate.Global
|
||||
// ReSharper disable once AutoPropertyCanBeMadeGetOnly.Global
|
||||
public static int BufferSize { get; set; } = 4096 * 20; // Can be changed for fine tuning by library user.
|
||||
|
||||
private Socket _socket;
|
||||
private SslStream _sslStream;
|
||||
|
||||
/// <summary>
|
||||
/// called on client sockets are created in connect
|
||||
/// </summary>
|
||||
public MqttTcpChannel()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// called on server, sockets are passed in
|
||||
/// connect will not be called
|
||||
/// </summary>
|
||||
public MqttTcpChannel(Socket socket, SslStream sslStream)
|
||||
{
|
||||
_socket = socket ?? throw new ArgumentNullException(nameof(socket));
|
||||
_sslStream = sslStream;
|
||||
CreateStreams(socket, sslStream);
|
||||
}
|
||||
|
||||
public Stream SendStream { get; private set; }
|
||||
public Stream ReceiveStream { get; private set; }
|
||||
public Stream RawReceiveStream { get; private set; }
|
||||
|
||||
public async Task ConnectAsync(MqttClientOptions options)
|
||||
{
|
||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||
|
||||
if (_socket == null)
|
||||
{
|
||||
_socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
|
||||
}
|
||||
|
||||
await Task.Factory.FromAsync(_socket.BeginConnect, _socket.EndConnect, options.Server, options.GetPort(), null).ConfigureAwait(false);
|
||||
|
||||
if (options.TlsOptions.UseTls)
|
||||
{
|
||||
_sslStream = new SslStream(new NetworkStream(_socket, true));
|
||||
|
||||
await _sslStream.AuthenticateAsClientAsync(options.Server, LoadCertificates(options), SslProtocols.Tls12, options.TlsOptions.CheckCertificateRevocation).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
CreateStreams(_socket, _sslStream);
|
||||
}
|
||||
|
||||
public Task DisconnectAsync()
|
||||
{
|
||||
Dispose();
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
RawReceiveStream?.Dispose();
|
||||
RawReceiveStream = null;
|
||||
|
||||
ReceiveStream?.Dispose();
|
||||
ReceiveStream = null;
|
||||
|
||||
SendStream?.Dispose();
|
||||
SendStream = null;
|
||||
|
||||
_socket?.Dispose();
|
||||
_socket = null;
|
||||
|
||||
_sslStream?.Dispose();
|
||||
_sslStream = null;
|
||||
}
|
||||
|
||||
private void CreateStreams(Socket socket, Stream sslStream)
|
||||
{
|
||||
RawReceiveStream = sslStream ?? new NetworkStream(socket);
|
||||
|
||||
//cannot use this as default buffering prevents from receiving the first connect message
|
||||
//need two streams otherwise read and write have to be synchronized
|
||||
SendStream = new BufferedStream(RawReceiveStream, BufferSize);
|
||||
ReceiveStream = new BufferedStream(RawReceiveStream, BufferSize);
|
||||
}
|
||||
|
||||
private static X509CertificateCollection LoadCertificates(MqttClientOptions options)
|
||||
{
|
||||
var certificates = new X509CertificateCollection();
|
||||
if (options.TlsOptions.Certificates == null)
|
||||
{
|
||||
return certificates;
|
||||
}
|
||||
|
||||
foreach (var certificate in options.TlsOptions.Certificates)
|
||||
{
|
||||
certificates.Add(new X509Certificate(certificate));
|
||||
}
|
||||
|
||||
return certificates;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
using MQTTnet.Core.Channel;
|
||||
using MQTTnet.Core.Client;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net.WebSockets;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MQTTnet.Implementations
|
||||
{
|
||||
public sealed class MqttWebSocketChannel : IMqttCommunicationChannel, IDisposable
|
||||
{
|
||||
private ClientWebSocket _webSocket;
|
||||
|
||||
public Stream RawReceiveStream { get; private set; }
|
||||
public Stream SendStream => RawReceiveStream;
|
||||
public Stream ReceiveStream => RawReceiveStream;
|
||||
|
||||
public async Task ConnectAsync(MqttClientOptions options)
|
||||
{
|
||||
var uri = options.Server;
|
||||
if (!uri.StartsWith("ws://", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
uri = "ws://" + uri;
|
||||
}
|
||||
|
||||
if (options.Port.HasValue)
|
||||
{
|
||||
uri += ":" + options.Port;
|
||||
}
|
||||
|
||||
_webSocket = new ClientWebSocket();
|
||||
_webSocket.Options.KeepAliveInterval = options.KeepAlivePeriod;
|
||||
await _webSocket.ConnectAsync(new Uri(uri), CancellationToken.None).ConfigureAwait(false);
|
||||
RawReceiveStream = new WebSocketStream(_webSocket);
|
||||
}
|
||||
|
||||
public async Task DisconnectAsync()
|
||||
{
|
||||
RawReceiveStream = null;
|
||||
|
||||
if (_webSocket == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_webSocket?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net.WebSockets;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MQTTnet.Implementations
|
||||
{
|
||||
public class WebSocketStream : Stream
|
||||
{
|
||||
private readonly ClientWebSocket _webSocket;
|
||||
|
||||
public WebSocketStream(ClientWebSocket webSocket)
|
||||
{
|
||||
_webSocket = webSocket;
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
}
|
||||
|
||||
public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
||||
{
|
||||
var currentOffset = offset;
|
||||
var targetOffset = offset + count;
|
||||
while (_webSocket.State == WebSocketState.Open && currentOffset < targetOffset)
|
||||
{
|
||||
var response = await _webSocket.ReceiveAsync(new ArraySegment<byte>(buffer, currentOffset, count), cancellationToken).ConfigureAwait(false);
|
||||
currentOffset += response.Count;
|
||||
|
||||
if (response.MessageType == WebSocketMessageType.Close)
|
||||
{
|
||||
await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
return currentOffset - offset;
|
||||
}
|
||||
|
||||
public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
||||
{
|
||||
return _webSocket.SendAsync(new ArraySegment<byte>(buffer, offset, count), WebSocketMessageType.Binary, true, cancellationToken);
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
return ReadAsync(buffer, offset, count).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
WriteAsync(buffer, offset, count).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
public override bool CanRead => true;
|
||||
public override bool CanSeek => false;
|
||||
public override bool CanWrite => true;
|
||||
|
||||
public override long Length => throw new NotSupportedException();
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get => throw new NotSupportedException();
|
||||
set => throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
<?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</RootNamespace>
|
||||
<AssemblyName>MQTTnet</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<TargetFrameworkProfile />
|
||||
</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>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</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>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Any CPU|AnyCPU'">
|
||||
<OutputPath>bin\Any CPU\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x86\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
||||
<OutputPath>bin\x86\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Any CPU|x86'">
|
||||
<OutputPath>bin\x86\Any CPU\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x64\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||
<OutputPath>bin\x64\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Any CPU|x64'">
|
||||
<OutputPath>bin\x64\Any CPU\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Implementations\MqttWebSocketChannel.cs" />
|
||||
<Compile Include="Implementations\WebSocketStream.cs" />
|
||||
<Compile Include="MqttClientFactory.cs" />
|
||||
<Compile Include="MqttServerFactory.cs" />
|
||||
<Compile Include="Implementations\MqttServerAdapter.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Implementations\MqttTcpChannel.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\MQTTnet.Core\MQTTnet.Core.csproj">
|
||||
<Project>{2ecb99e4-72d0-4c23-99ba-93d511d3967d}</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>
|
||||
@@ -1,33 +0,0 @@
|
||||
using System;
|
||||
using MQTTnet.Core.Adapter;
|
||||
using MQTTnet.Core.Channel;
|
||||
using MQTTnet.Core.Client;
|
||||
using MQTTnet.Core.Serializer;
|
||||
using MQTTnet.Implementations;
|
||||
|
||||
namespace MQTTnet
|
||||
{
|
||||
public class MqttClientFactory : IMqttClientFactory
|
||||
{
|
||||
public IMqttClient CreateMqttClient(MqttClientOptions options)
|
||||
{
|
||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||
|
||||
return new MqttClient(options, new MqttChannelCommunicationAdapter(GetMqttCommunicationChannel(options), new MqttPacketSerializer()));
|
||||
}
|
||||
|
||||
private static IMqttCommunicationChannel GetMqttCommunicationChannel(MqttClientOptions options)
|
||||
{
|
||||
switch (options.ConnectionType)
|
||||
{
|
||||
case MqttConnectionType.Tcp:
|
||||
return new MqttTcpChannel();
|
||||
case MqttConnectionType.Ws:
|
||||
return new MqttWebSocketChannel();
|
||||
|
||||
default:
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using MQTTnet.Core.Adapter;
|
||||
using MQTTnet.Core.Server;
|
||||
using MQTTnet.Implementations;
|
||||
|
||||
namespace MQTTnet
|
||||
{
|
||||
public class MqttServerFactory : IMqttServerFactory
|
||||
{
|
||||
public IMqttServer CreateMqttServer(MqttServerOptions options)
|
||||
{
|
||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||
|
||||
return new MqttServer(options, new List<IMqttServerAdapter> { new MqttServerAdapter() });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
[assembly: AssemblyTitle("MQTTnet")]
|
||||
[assembly: AssemblyDescription("MQTTnet for .NET Framework")]
|
||||
[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.3.1.0")]
|
||||
[assembly: AssemblyFileVersion("2.3.1.0")]
|
||||
@@ -1,125 +0,0 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Security;
|
||||
using System.Net.Sockets;
|
||||
using System.Security.Authentication;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Core.Adapter;
|
||||
using MQTTnet.Core.Diagnostics;
|
||||
using MQTTnet.Core.Serializer;
|
||||
using MQTTnet.Core.Server;
|
||||
|
||||
namespace MQTTnet.Implementations
|
||||
{
|
||||
public class MqttServerAdapter : IMqttServerAdapter, IDisposable
|
||||
{
|
||||
private CancellationTokenSource _cancellationTokenSource;
|
||||
private Socket _defaultEndpointSocket;
|
||||
private Socket _tlsEndpointSocket;
|
||||
private X509Certificate2 _tlsCertificate;
|
||||
|
||||
private bool _isRunning;
|
||||
|
||||
public event EventHandler<MqttClientConnectedEventArgs> ClientConnected;
|
||||
|
||||
public void Start(MqttServerOptions options)
|
||||
{
|
||||
if (_isRunning) throw new InvalidOperationException("Server is already started.");
|
||||
|
||||
_isRunning = true;
|
||||
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
if (options.DefaultEndpointOptions.IsEnabled)
|
||||
{
|
||||
_defaultEndpointSocket = new Socket(SocketType.Stream, ProtocolType.Tcp);
|
||||
_defaultEndpointSocket.Bind(new IPEndPoint(IPAddress.Any, options.GetDefaultEndpointPort()));
|
||||
_defaultEndpointSocket.Listen(options.ConnectionBacklog);
|
||||
|
||||
Task.Run(() => AcceptDefaultEndpointConnectionsAsync(_cancellationTokenSource.Token), _cancellationTokenSource.Token);
|
||||
}
|
||||
|
||||
if (options.TlsEndpointOptions.IsEnabled)
|
||||
{
|
||||
if (options.TlsEndpointOptions.Certificate == null)
|
||||
{
|
||||
throw new ArgumentException("TLS certificate is not set.");
|
||||
}
|
||||
|
||||
_tlsCertificate = new X509Certificate2(options.TlsEndpointOptions.Certificate);
|
||||
|
||||
_tlsEndpointSocket = new Socket(SocketType.Stream, ProtocolType.Tcp);
|
||||
_tlsEndpointSocket.Bind(new IPEndPoint(IPAddress.Any, options.GetTlsEndpointPort()));
|
||||
_tlsEndpointSocket.Listen(options.ConnectionBacklog);
|
||||
|
||||
Task.Run(() => AcceptTlsEndpointConnectionsAsync(_cancellationTokenSource.Token), _cancellationTokenSource.Token);
|
||||
}
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
_isRunning = false;
|
||||
|
||||
_cancellationTokenSource?.Cancel(false);
|
||||
_cancellationTokenSource?.Dispose();
|
||||
_cancellationTokenSource = null;
|
||||
|
||||
_defaultEndpointSocket?.Dispose();
|
||||
_defaultEndpointSocket = null;
|
||||
|
||||
_tlsEndpointSocket?.Dispose();
|
||||
_tlsEndpointSocket = null;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
|
||||
private async Task AcceptDefaultEndpointConnectionsAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
var clientSocket = await _defaultEndpointSocket.AcceptAsync().ConfigureAwait(false);
|
||||
var clientAdapter = new MqttChannelCommunicationAdapter(new MqttTcpChannel(clientSocket, null), new MqttPacketSerializer());
|
||||
ClientConnected?.Invoke(this, new MqttClientConnectedEventArgs(clientSocket.RemoteEndPoint.ToString(), clientAdapter));
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
MqttTrace.Error(nameof(MqttServerAdapter), exception, "Error while accepting connection at default endpoint.");
|
||||
|
||||
//excessive CPU consumed if in endless loop of socket errors
|
||||
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task AcceptTlsEndpointConnectionsAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
var clientSocket = await _tlsEndpointSocket.AcceptAsync().ConfigureAwait(false);
|
||||
|
||||
var sslStream = new SslStream(new NetworkStream(clientSocket));
|
||||
await sslStream.AuthenticateAsServerAsync(_tlsCertificate, false, SslProtocols.Tls12, false).ConfigureAwait(false);
|
||||
|
||||
var clientAdapter = new MqttChannelCommunicationAdapter(new MqttTcpChannel(clientSocket, sslStream), new MqttPacketSerializer());
|
||||
ClientConnected?.Invoke(this, new MqttClientConnectedEventArgs(clientSocket.RemoteEndPoint.ToString(), clientAdapter));
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
MqttTrace.Error(nameof(MqttServerAdapter), exception, "Error while accepting connection at TLS endpoint.");
|
||||
|
||||
//excessive CPU consumed if in endless loop of socket errors
|
||||
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
using System;
|
||||
using System.Net.Security;
|
||||
using System.Net.Sockets;
|
||||
using System.Security.Authentication;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Core.Channel;
|
||||
using MQTTnet.Core.Client;
|
||||
using System.IO;
|
||||
|
||||
namespace MQTTnet.Implementations
|
||||
{
|
||||
public sealed class MqttTcpChannel : IMqttCommunicationChannel, IDisposable
|
||||
{
|
||||
private Socket _socket;
|
||||
private SslStream _sslStream;
|
||||
|
||||
/// <summary>
|
||||
/// called on client sockets are created in connect
|
||||
/// </summary>
|
||||
public MqttTcpChannel()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// called on server, sockets are passed in
|
||||
/// connect will not be called
|
||||
/// </summary>
|
||||
public MqttTcpChannel(Socket socket, SslStream sslStream)
|
||||
{
|
||||
_socket = socket ?? throw new ArgumentNullException(nameof(socket));
|
||||
_sslStream = sslStream;
|
||||
ReceiveStream = (Stream)sslStream ?? new NetworkStream(socket);
|
||||
}
|
||||
|
||||
public Stream SendStream => ReceiveStream;
|
||||
public Stream ReceiveStream { get; private set; }
|
||||
public Stream RawReceiveStream => ReceiveStream;
|
||||
|
||||
public async Task ConnectAsync(MqttClientOptions options)
|
||||
{
|
||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||
|
||||
if (_socket == null)
|
||||
{
|
||||
_socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
|
||||
}
|
||||
|
||||
await _socket.ConnectAsync(options.Server, options.GetPort()).ConfigureAwait(false);
|
||||
|
||||
if (options.TlsOptions.UseTls)
|
||||
{
|
||||
_sslStream = new SslStream(new NetworkStream(_socket, true));
|
||||
ReceiveStream = _sslStream;
|
||||
await _sslStream.AuthenticateAsClientAsync(options.Server, LoadCertificates(options), SslProtocols.Tls12, options.TlsOptions.CheckCertificateRevocation).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
ReceiveStream = new NetworkStream(_socket);
|
||||
}
|
||||
}
|
||||
|
||||
public Task DisconnectAsync()
|
||||
{
|
||||
Dispose();
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_socket?.Dispose();
|
||||
_socket = null;
|
||||
|
||||
_sslStream?.Dispose();
|
||||
_sslStream = null;
|
||||
}
|
||||
|
||||
private static X509CertificateCollection LoadCertificates(MqttClientOptions options)
|
||||
{
|
||||
var certificates = new X509CertificateCollection();
|
||||
if (options.TlsOptions.Certificates == null)
|
||||
{
|
||||
return certificates;
|
||||
}
|
||||
|
||||
foreach (var certificate in options.TlsOptions.Certificates)
|
||||
{
|
||||
certificates.Add(new X509Certificate(certificate));
|
||||
}
|
||||
|
||||
return certificates;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
using MQTTnet.Core.Channel;
|
||||
using MQTTnet.Core.Client;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net.WebSockets;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MQTTnet.Implementations
|
||||
{
|
||||
public sealed class MqttWebSocketChannel : IMqttCommunicationChannel, IDisposable
|
||||
{
|
||||
private ClientWebSocket _webSocket;
|
||||
|
||||
public Stream SendStream => RawReceiveStream;
|
||||
public Stream ReceiveStream => RawReceiveStream;
|
||||
public Stream RawReceiveStream { get; private set; }
|
||||
|
||||
public async Task ConnectAsync(MqttClientOptions options)
|
||||
{
|
||||
var uri = options.Server;
|
||||
if (!uri.StartsWith("ws://", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
uri = "ws://" + uri;
|
||||
}
|
||||
|
||||
if (options.Port.HasValue)
|
||||
{
|
||||
uri += ":" + options.Port;
|
||||
}
|
||||
|
||||
_webSocket = new ClientWebSocket();
|
||||
_webSocket.Options.KeepAliveInterval = options.KeepAlivePeriod;
|
||||
await _webSocket.ConnectAsync(new Uri(uri), CancellationToken.None);
|
||||
RawReceiveStream = new WebSocketStream(_webSocket);
|
||||
}
|
||||
|
||||
public async Task DisconnectAsync()
|
||||
{
|
||||
RawReceiveStream = null;
|
||||
|
||||
if (_webSocket == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_webSocket?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net.WebSockets;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MQTTnet.Implementations
|
||||
{
|
||||
public class WebSocketStream : Stream
|
||||
{
|
||||
private readonly ClientWebSocket _webSocket;
|
||||
|
||||
public WebSocketStream(ClientWebSocket webSocket)
|
||||
{
|
||||
_webSocket = webSocket;
|
||||
}
|
||||
|
||||
public override bool CanRead => true;
|
||||
|
||||
public override bool CanSeek => false;
|
||||
|
||||
public override bool CanWrite => true;
|
||||
|
||||
public override long Length => throw new NotSupportedException();
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get => throw new NotSupportedException();
|
||||
set => throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
}
|
||||
|
||||
public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
||||
{
|
||||
var currentOffset = offset;
|
||||
var targetOffset = offset + count;
|
||||
while (_webSocket.State == WebSocketState.Open && currentOffset < targetOffset)
|
||||
{
|
||||
var response = await _webSocket.ReceiveAsync(new ArraySegment<byte>(buffer, currentOffset, count), cancellationToken).ConfigureAwait(false);
|
||||
currentOffset += response.Count;
|
||||
|
||||
if (response.MessageType == WebSocketMessageType.Close)
|
||||
{
|
||||
await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
return currentOffset - offset;
|
||||
}
|
||||
|
||||
public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
||||
{
|
||||
return _webSocket.SendAsync(new ArraySegment<byte>(buffer, offset, count), WebSocketMessageType.Binary, true, cancellationToken);
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
return ReadAsync(buffer, offset, count).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
WriteAsync(buffer, offset, count).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard1.3</TargetFramework>
|
||||
<AssemblyName>MQTTnet</AssemblyName>
|
||||
<RootNamespace>MQTTnet</RootNamespace>
|
||||
<AssemblyVersion>2.3.1.0</AssemblyVersion>
|
||||
<FileVersion>2.3.1.0</FileVersion>
|
||||
<Version>0.0.0.0</Version>
|
||||
<Company />
|
||||
<Product />
|
||||
<Description />
|
||||
<Authors />
|
||||
<PackageId />
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x86'" />
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" />
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\MQTTnet.Core\MQTTnet.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Net.Security" Version="4.3.2" />
|
||||
<PackageReference Include="System.Net.WebSockets" Version="4.3.0" />
|
||||
<PackageReference Include="System.Net.WebSockets.Client" Version="4.3.1" />
|
||||
<PackageReference Include="System.Threading.Thread" Version="4.3.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,33 +0,0 @@
|
||||
using System;
|
||||
using MQTTnet.Core.Adapter;
|
||||
using MQTTnet.Core.Channel;
|
||||
using MQTTnet.Core.Client;
|
||||
using MQTTnet.Core.Serializer;
|
||||
using MQTTnet.Implementations;
|
||||
|
||||
namespace MQTTnet
|
||||
{
|
||||
public class MqttClientFactory : IMqttClientFactory
|
||||
{
|
||||
public IMqttClient CreateMqttClient(MqttClientOptions options)
|
||||
{
|
||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||
|
||||
return new MqttClient(options, new MqttChannelCommunicationAdapter(GetMqttCommunicationChannel(options), new MqttPacketSerializer()));
|
||||
}
|
||||
|
||||
private static IMqttCommunicationChannel GetMqttCommunicationChannel(MqttClientOptions options)
|
||||
{
|
||||
switch (options.ConnectionType)
|
||||
{
|
||||
case MqttConnectionType.Tcp:
|
||||
return new MqttTcpChannel();
|
||||
case MqttConnectionType.Ws:
|
||||
return new MqttWebSocketChannel();
|
||||
|
||||
default:
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using MQTTnet.Core.Adapter;
|
||||
using MQTTnet.Core.Server;
|
||||
using MQTTnet.Implementations;
|
||||
|
||||
namespace MQTTnet
|
||||
{
|
||||
public class MqttServerFactory : IMqttServerFactory
|
||||
{
|
||||
public IMqttServer CreateMqttServer(MqttServerOptions options)
|
||||
{
|
||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||
|
||||
return new MqttServer(options, new List<IMqttServerAdapter> { new MqttServerAdapter() });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
using System;
|
||||
using MQTTnet.Core.Adapter;
|
||||
using MQTTnet.Core.Diagnostics;
|
||||
using MQTTnet.Core.Serializer;
|
||||
using MQTTnet.Core.Server;
|
||||
using Windows.Networking.Sockets;
|
||||
|
||||
namespace MQTTnet.Implementations
|
||||
{
|
||||
public class MqttServerAdapter : IMqttServerAdapter, IDisposable
|
||||
{
|
||||
private StreamSocketListener _defaultEndpointSocket;
|
||||
|
||||
private bool _isRunning;
|
||||
|
||||
public event EventHandler<MqttClientConnectedEventArgs> ClientConnected;
|
||||
|
||||
public void Start(MqttServerOptions options)
|
||||
{
|
||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||
|
||||
if (_isRunning) throw new InvalidOperationException("Server is already started.");
|
||||
|
||||
_isRunning = true;
|
||||
|
||||
if (options.DefaultEndpointOptions.IsEnabled)
|
||||
{
|
||||
_defaultEndpointSocket = new StreamSocketListener();
|
||||
_defaultEndpointSocket.BindServiceNameAsync(options.GetDefaultEndpointPort().ToString(), SocketProtectionLevel.PlainSocket).GetAwaiter().GetResult();
|
||||
_defaultEndpointSocket.ConnectionReceived += AcceptDefaultEndpointConnectionsAsync;
|
||||
}
|
||||
|
||||
if (options.TlsEndpointOptions.IsEnabled)
|
||||
{
|
||||
throw new NotSupportedException("TLS servers are not supported for UWP apps.");
|
||||
}
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
_isRunning = false;
|
||||
|
||||
_defaultEndpointSocket?.Dispose();
|
||||
_defaultEndpointSocket = null;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
|
||||
private void AcceptDefaultEndpointConnectionsAsync(StreamSocketListener sender, StreamSocketListenerConnectionReceivedEventArgs args)
|
||||
{
|
||||
try
|
||||
{
|
||||
var clientAdapter = new MqttChannelCommunicationAdapter(new MqttTcpChannel(args.Socket), new MqttPacketSerializer());
|
||||
ClientConnected?.Invoke(this, new MqttClientConnectedEventArgs(args.Socket.Information.RemoteAddress.ToString(), clientAdapter));
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
MqttTrace.Error(nameof(MqttServerAdapter), exception, "Error while accepting connection at default endpoint.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Networking;
|
||||
using Windows.Networking.Sockets;
|
||||
using Windows.Security.Cryptography.Certificates;
|
||||
using MQTTnet.Core.Channel;
|
||||
using MQTTnet.Core.Client;
|
||||
|
||||
namespace MQTTnet.Implementations
|
||||
{
|
||||
public sealed class MqttTcpChannel : IMqttCommunicationChannel, IDisposable
|
||||
{
|
||||
private StreamSocket _socket;
|
||||
|
||||
public MqttTcpChannel()
|
||||
{
|
||||
}
|
||||
|
||||
public MqttTcpChannel(StreamSocket socket)
|
||||
{
|
||||
_socket = socket ?? throw new ArgumentNullException(nameof(socket));
|
||||
CreateStreams();
|
||||
}
|
||||
|
||||
public Stream SendStream { get; private set; }
|
||||
public Stream ReceiveStream { get; private set; }
|
||||
public Stream RawReceiveStream { get; private set; }
|
||||
|
||||
public async Task ConnectAsync(MqttClientOptions options)
|
||||
{
|
||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||
|
||||
if (_socket == null)
|
||||
{
|
||||
_socket = new StreamSocket();
|
||||
}
|
||||
|
||||
if (!options.TlsOptions.UseTls)
|
||||
{
|
||||
await _socket.ConnectAsync(new HostName(options.Server), options.GetPort().ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
_socket.Control.ClientCertificate = LoadCertificate(options);
|
||||
|
||||
if (!options.TlsOptions.CheckCertificateRevocation)
|
||||
{
|
||||
_socket.Control.IgnorableServerCertificateErrors.Add(ChainValidationResult.IncompleteChain);
|
||||
_socket.Control.IgnorableServerCertificateErrors.Add(ChainValidationResult.RevocationInformationMissing);
|
||||
}
|
||||
|
||||
await _socket.ConnectAsync(new HostName(options.Server), options.GetPort().ToString(), SocketProtectionLevel.Tls12);
|
||||
}
|
||||
|
||||
CreateStreams();
|
||||
}
|
||||
|
||||
public Task DisconnectAsync()
|
||||
{
|
||||
Dispose();
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
RawReceiveStream?.Dispose();
|
||||
RawReceiveStream = null;
|
||||
|
||||
SendStream?.Dispose();
|
||||
SendStream = null;
|
||||
|
||||
ReceiveStream?.Dispose();
|
||||
ReceiveStream = null;
|
||||
|
||||
_socket?.Dispose();
|
||||
_socket = null;
|
||||
}
|
||||
|
||||
private void CreateStreams()
|
||||
{
|
||||
SendStream = _socket.OutputStream.AsStreamForWrite();
|
||||
ReceiveStream = _socket.InputStream.AsStreamForRead();
|
||||
RawReceiveStream = ReceiveStream;
|
||||
}
|
||||
|
||||
private static Certificate LoadCertificate(MqttClientOptions options)
|
||||
{
|
||||
if (options.TlsOptions.Certificates == null || !options.TlsOptions.Certificates.Any())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (options.TlsOptions.Certificates.Count > 1)
|
||||
{
|
||||
throw new NotSupportedException("Only one client certificate is supported for UWP.");
|
||||
}
|
||||
|
||||
return new Certificate(options.TlsOptions.Certificates.First().AsBuffer());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
using MQTTnet.Core.Channel;
|
||||
using MQTTnet.Core.Client;
|
||||
using System;
|
||||
using System.IO;
|
||||
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();
|
||||
|
||||
public Stream RawReceiveStream { get; private set; }
|
||||
public Stream SendStream => RawReceiveStream;
|
||||
public Stream ReceiveStream => RawReceiveStream;
|
||||
|
||||
public async Task ConnectAsync(MqttClientOptions options)
|
||||
{
|
||||
var uri = options.Server;
|
||||
if (!uri.StartsWith("ws://", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
uri = "ws://" + uri;
|
||||
}
|
||||
|
||||
if (options.Port.HasValue)
|
||||
{
|
||||
uri += ":" + options.Port;
|
||||
}
|
||||
|
||||
_webSocket = new ClientWebSocket();
|
||||
_webSocket.Options.KeepAliveInterval = options.KeepAlivePeriod;
|
||||
await _webSocket.ConnectAsync(new Uri(uri), CancellationToken.None).ConfigureAwait(false);
|
||||
|
||||
RawReceiveStream = new WebSocketStream(_webSocket);
|
||||
}
|
||||
|
||||
public async Task DisconnectAsync()
|
||||
{
|
||||
RawReceiveStream = null;
|
||||
|
||||
if (_webSocket == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_webSocket?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net.WebSockets;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MQTTnet.Implementations
|
||||
{
|
||||
public class WebSocketStream : Stream
|
||||
{
|
||||
private readonly ClientWebSocket _webSocket;
|
||||
|
||||
public WebSocketStream(ClientWebSocket webSocket)
|
||||
{
|
||||
_webSocket = webSocket;
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
}
|
||||
|
||||
public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
||||
{
|
||||
var currentOffset = offset;
|
||||
var targetOffset = offset + count;
|
||||
while (_webSocket.State == WebSocketState.Open && currentOffset < targetOffset)
|
||||
{
|
||||
var response = await _webSocket.ReceiveAsync(new ArraySegment<byte>(buffer, currentOffset, count), cancellationToken).ConfigureAwait(false);
|
||||
currentOffset += response.Count;
|
||||
|
||||
if (response.MessageType == WebSocketMessageType.Close)
|
||||
{
|
||||
await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
return currentOffset - offset;
|
||||
}
|
||||
|
||||
public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
||||
{
|
||||
return _webSocket.SendAsync(new ArraySegment<byte>(buffer, offset, count), WebSocketMessageType.Binary, true, cancellationToken);
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
return ReadAsync(buffer, offset, count).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
WriteAsync(buffer, offset, count).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
public override bool CanRead => true;
|
||||
public override bool CanSeek => false;
|
||||
public override bool CanWrite => true;
|
||||
|
||||
public override long Length => throw new NotSupportedException();
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get => throw new NotSupportedException();
|
||||
set => throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,194 +0,0 @@
|
||||
<?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</RootNamespace>
|
||||
<AssemblyName>MQTTnet</AssemblyName>
|
||||
<DefaultLanguage>en-US</DefaultLanguage>
|
||||
<TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
|
||||
<TargetPlatformVersion>10.0.15063.0</TargetPlatformVersion>
|
||||
<TargetPlatformMinVersion>10.0.10240.0</TargetPlatformMinVersion>
|
||||
<MinimumVisualStudioVersion>14</MinimumVisualStudioVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<RuntimeIdentifiers>win10-arm;win10-arm-aot;win10-x86;win10-x86-aot;win10-x64;win10-x64-aot</RuntimeIdentifiers>
|
||||
</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>
|
||||
<NoWarn>CS1591</NoWarn>
|
||||
</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>
|
||||
<NoWarn>CS1591</NoWarn>
|
||||
</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,CS1591</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,CS1591</NoWarn>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<DocumentationFile>bin\x86\Release\MQTTnet.XML</DocumentationFile>
|
||||
</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,CS1591</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,CS1591</NoWarn>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>ARM</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<DocumentationFile>bin\ARM\Release\MQTTnet.XML</DocumentationFile>
|
||||
</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,CS1591</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,CS1591</NoWarn>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<GenerateLibraryLayout>false</GenerateLibraryLayout>
|
||||
<DocumentationFile>bin\x64\Release\MQTTnet.XML</DocumentationFile>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Implementations\MqttWebSocketChannel.cs" />
|
||||
<Compile Include="MqttClientFactory.cs" />
|
||||
<Compile Include="Implementations\MqttServerAdapter.cs" />
|
||||
<Compile Include="Implementations\MqttTcpChannel.cs" />
|
||||
<Compile Include="MqttServerFactory.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Implementations\WebSocketStream.cs" />
|
||||
<EmbeddedResource Include="Properties\MQTTnet.Universal.rd.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\MQTTnet.Core\MQTTnet.Core.csproj">
|
||||
<Project>{2ecb99e4-72d0-4c23-99ba-93d511d3967d}</Project>
|
||||
<Name>MQTTnet.Core</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
|
||||
<Version>5.4.0</Version>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' < '14.0' ">
|
||||
<VisualStudioVersion>14.0</VisualStudioVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Any CPU|AnyCPU'">
|
||||
<OutputPath>bin\Any CPU\</OutputPath>
|
||||
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<NoStdLib>true</NoStdLib>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Any CPU|x86'">
|
||||
<OutputPath>bin\x86\Any CPU\</OutputPath>
|
||||
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<DocumentationFile>bin\x86\Release\MQTTnet.XML</DocumentationFile>
|
||||
<Optimize>true</Optimize>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<NoStdLib>true</NoStdLib>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Any CPU|ARM'">
|
||||
<OutputPath>bin\ARM\Any CPU\</OutputPath>
|
||||
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<DocumentationFile>bin\ARM\Release\MQTTnet.XML</DocumentationFile>
|
||||
<Optimize>true</Optimize>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<NoStdLib>true</NoStdLib>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>ARM</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Any CPU|x64'">
|
||||
<OutputPath>bin\x64\Any CPU\</OutputPath>
|
||||
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
|
||||
<DocumentationFile>bin\x64\Release\MQTTnet.XML</DocumentationFile>
|
||||
<Optimize>true</Optimize>
|
||||
<NoWarn>;2008</NoWarn>
|
||||
<NoStdLib>true</NoStdLib>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</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>
|
||||
@@ -1,33 +0,0 @@
|
||||
using System;
|
||||
using MQTTnet.Core.Adapter;
|
||||
using MQTTnet.Core.Channel;
|
||||
using MQTTnet.Core.Client;
|
||||
using MQTTnet.Core.Serializer;
|
||||
using MQTTnet.Implementations;
|
||||
|
||||
namespace MQTTnet
|
||||
{
|
||||
public class MqttClientFactory : IMqttClientFactory
|
||||
{
|
||||
public IMqttClient CreateMqttClient(MqttClientOptions options)
|
||||
{
|
||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||
|
||||
return new MqttClient(options, new MqttChannelCommunicationAdapter(GetMqttCommunicationChannel(options), new MqttPacketSerializer()));
|
||||
}
|
||||
|
||||
private static IMqttCommunicationChannel GetMqttCommunicationChannel(MqttClientOptions options)
|
||||
{
|
||||
switch (options.ConnectionType)
|
||||
{
|
||||
case MqttConnectionType.Tcp:
|
||||
return new MqttTcpChannel();
|
||||
case MqttConnectionType.Ws:
|
||||
return new MqttWebSocketChannel();
|
||||
|
||||
default:
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using MQTTnet.Core.Adapter;
|
||||
using MQTTnet.Core.Server;
|
||||
using MQTTnet.Implementations;
|
||||
|
||||
namespace MQTTnet
|
||||
{
|
||||
public class MqttServerFactory : IMqttServerFactory
|
||||
{
|
||||
public IMqttServer CreateMqttServer(MqttServerOptions options)
|
||||
{
|
||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||
|
||||
return new MqttServer(options, new List<IMqttServerAdapter> { new MqttServerAdapter() });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
[assembly: AssemblyTitle("MQTTnet")]
|
||||
[assembly: AssemblyDescription("MQTTnet for Universal Windows")]
|
||||
[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.3.1.0")]
|
||||
[assembly: AssemblyFileVersion("2.3.1.0")]
|
||||
@@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata">
|
||||
<Library Name="MQTTnet.Universal">
|
||||
</Library>
|
||||
</Directives>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 1.2 KiB |
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Christian
|
||||
Copyright (c) 2017 Christian Kratky
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Core.Client;
|
||||
using MQTTnet.Core.Packets;
|
||||
using MQTTnet.Core.Serializer;
|
||||
|
||||
namespace MQTTnet.Core.Adapter
|
||||
{
|
||||
public interface IMqttCommunicationAdapter
|
||||
{
|
||||
IMqttPacketSerializer PacketSerializer { get; }
|
||||
|
||||
Task ConnectAsync(TimeSpan timeout, MqttClientOptions options);
|
||||
|
||||
Task DisconnectAsync(TimeSpan timeout);
|
||||
|
||||
Task SendPacketsAsync(TimeSpan timeout, CancellationToken cancellationToken, IEnumerable<MqttBasePacket> packets);
|
||||
|
||||
Task<MqttBasePacket> ReceivePacketAsync(TimeSpan timeout, CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
using System;
|
||||
using MQTTnet.Core.Server;
|
||||
|
||||
namespace MQTTnet.Core.Adapter
|
||||
{
|
||||
public interface IMqttServerAdapter
|
||||
{
|
||||
event EventHandler<MqttClientConnectedEventArgs> ClientConnected;
|
||||
|
||||
void Start(MqttServerOptions options);
|
||||
|
||||
void Stop();
|
||||
}
|
||||
}
|
||||
@@ -1,194 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Core.Channel;
|
||||
using MQTTnet.Core.Client;
|
||||
using MQTTnet.Core.Diagnostics;
|
||||
using MQTTnet.Core.Exceptions;
|
||||
using MQTTnet.Core.Internal;
|
||||
using MQTTnet.Core.Packets;
|
||||
using MQTTnet.Core.Serializer;
|
||||
|
||||
namespace MQTTnet.Core.Adapter
|
||||
{
|
||||
public class MqttChannelCommunicationAdapter : IMqttCommunicationAdapter
|
||||
{
|
||||
private readonly IMqttCommunicationChannel _channel;
|
||||
private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
|
||||
|
||||
public MqttChannelCommunicationAdapter(IMqttCommunicationChannel channel, IMqttPacketSerializer serializer)
|
||||
{
|
||||
_channel = channel ?? throw new ArgumentNullException(nameof(channel));
|
||||
PacketSerializer = serializer ?? throw new ArgumentNullException(nameof(serializer));
|
||||
}
|
||||
|
||||
public IMqttPacketSerializer PacketSerializer { get; }
|
||||
|
||||
public async Task ConnectAsync(TimeSpan timeout, MqttClientOptions options)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _channel.ConnectAsync(options).TimeoutAfter(timeout).ConfigureAwait(false);
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (MqttCommunicationTimedOutException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (MqttCommunicationException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
throw new MqttCommunicationException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task DisconnectAsync(TimeSpan timeout)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _channel.DisconnectAsync().TimeoutAfter(timeout).ConfigureAwait(false);
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (MqttCommunicationTimedOutException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (MqttCommunicationException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
throw new MqttCommunicationException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task SendPacketsAsync(TimeSpan timeout, CancellationToken cancellationToken, IEnumerable<MqttBasePacket> packets)
|
||||
{
|
||||
await _semaphore.WaitAsync(cancellationToken);
|
||||
|
||||
try
|
||||
{
|
||||
foreach (var packet in packets)
|
||||
{
|
||||
if (packet == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
MqttTrace.Information(nameof(MqttChannelCommunicationAdapter), $"TX >>> {0} [Timeout={1}]", packet, timeout);
|
||||
|
||||
var writeBuffer = PacketSerializer.Serialize(packet);
|
||||
await _channel.SendStream.WriteAsync(writeBuffer, 0, writeBuffer.Length, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (timeout > TimeSpan.Zero)
|
||||
{
|
||||
await _channel.SendStream.FlushAsync(cancellationToken).TimeoutAfter(timeout).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await _channel.SendStream.FlushAsync(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (MqttCommunicationTimedOutException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (MqttCommunicationException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
throw new MqttCommunicationException(exception);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_semaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<MqttBasePacket> ReceivePacketAsync(TimeSpan timeout, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
ReceivedMqttPacket receivedMqttPacket;
|
||||
if (timeout > TimeSpan.Zero)
|
||||
{
|
||||
receivedMqttPacket = await ReceiveAsync(_channel.RawReceiveStream, cancellationToken).TimeoutAfter(timeout).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
receivedMqttPacket = await ReceiveAsync(_channel.ReceiveStream, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
throw new TaskCanceledException();
|
||||
}
|
||||
|
||||
var packet = PacketSerializer.Deserialize(receivedMqttPacket);
|
||||
if (packet == null)
|
||||
{
|
||||
throw new MqttProtocolViolationException("Received malformed packet.");
|
||||
}
|
||||
|
||||
MqttTrace.Information(nameof(MqttChannelCommunicationAdapter), "RX <<< {0}", packet);
|
||||
return packet;
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (MqttCommunicationTimedOutException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (MqttCommunicationException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
throw new MqttCommunicationException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<ReceivedMqttPacket> ReceiveAsync(Stream stream, CancellationToken cancellationToken)
|
||||
{
|
||||
var header = MqttPacketReader.ReadHeaderFromSource(stream, cancellationToken);
|
||||
|
||||
if (header.BodyLength == 0)
|
||||
{
|
||||
return new ReceivedMqttPacket(header, new MemoryStream(0));
|
||||
}
|
||||
|
||||
var body = new byte[header.BodyLength];
|
||||
|
||||
var offset = 0;
|
||||
do
|
||||
{
|
||||
var readBytesCount = await stream.ReadAsync(body, offset, body.Length - offset, cancellationToken).ConfigureAwait(false);
|
||||
offset += readBytesCount;
|
||||
} while (offset < header.BodyLength);
|
||||
|
||||
return new ReceivedMqttPacket(header, new MemoryStream(body, 0, body.Length));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace MQTTnet.Core.Adapter
|
||||
{
|
||||
public class MqttClientConnectedEventArgs : EventArgs
|
||||
{
|
||||
public MqttClientConnectedEventArgs(string identifier, IMqttCommunicationAdapter clientAdapter)
|
||||
{
|
||||
Identifier = identifier ?? throw new ArgumentNullException(nameof(identifier));
|
||||
ClientAdapter = clientAdapter ?? throw new ArgumentNullException(nameof(clientAdapter));
|
||||
}
|
||||
|
||||
public string Identifier { get; }
|
||||
|
||||
public IMqttCommunicationAdapter ClientAdapter { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Core.Packets;
|
||||
|
||||
namespace MQTTnet.Core.Adapter
|
||||
{
|
||||
public static class MqttCommunicationAdapterExtensions
|
||||
{
|
||||
public static Task SendPacketsAsync(this IMqttCommunicationAdapter adapter, TimeSpan timeout, CancellationToken cancellationToken, params MqttBasePacket[] packets)
|
||||
{
|
||||
return adapter.SendPacketsAsync(timeout, cancellationToken, packets);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using MQTTnet.Core.Packets;
|
||||
|
||||
namespace MQTTnet.Core.Adapter
|
||||
{
|
||||
public class ReceivedMqttPacket
|
||||
{
|
||||
public ReceivedMqttPacket(MqttPacketHeader header, MemoryStream body)
|
||||
{
|
||||
Header = header ?? throw new ArgumentNullException(nameof(header));
|
||||
Body = body ?? throw new ArgumentNullException(nameof(body));
|
||||
}
|
||||
|
||||
public MqttPacketHeader Header { get; }
|
||||
|
||||
public MemoryStream Body { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Core.Client;
|
||||
using System.IO;
|
||||
|
||||
namespace MQTTnet.Core.Channel
|
||||
{
|
||||
public interface IMqttCommunicationChannel
|
||||
{
|
||||
Stream SendStream { get; }
|
||||
Stream ReceiveStream { get; }
|
||||
Stream RawReceiveStream { get; }
|
||||
|
||||
Task ConnectAsync(MqttClientOptions options);
|
||||
Task DisconnectAsync();
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Core.Packets;
|
||||
|
||||
namespace MQTTnet.Core.Client
|
||||
{
|
||||
public interface IMqttClient
|
||||
{
|
||||
bool IsConnected { get; }
|
||||
|
||||
event EventHandler<MqttApplicationMessageReceivedEventArgs> ApplicationMessageReceived;
|
||||
event EventHandler Connected;
|
||||
event EventHandler Disconnected;
|
||||
|
||||
Task ConnectAsync(MqttApplicationMessage willApplicationMessage = null);
|
||||
Task DisconnectAsync();
|
||||
|
||||
Task<IList<MqttSubscribeResult>> SubscribeAsync(IEnumerable<TopicFilter> topicFilters);
|
||||
Task UnsubscribeAsync(IEnumerable<string> topicFilters);
|
||||
|
||||
Task PublishAsync(IEnumerable<MqttApplicationMessage> applicationMessages);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
namespace MQTTnet.Core.Client
|
||||
{
|
||||
public interface IMqttClientFactory
|
||||
{
|
||||
IMqttClient CreateMqttClient(MqttClientOptions options);
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace MQTTnet.Core.Client
|
||||
{
|
||||
public sealed class MqttApplicationMessageReceivedEventArgs : EventArgs
|
||||
{
|
||||
public MqttApplicationMessageReceivedEventArgs(MqttApplicationMessage applicationMessage)
|
||||
{
|
||||
ApplicationMessage = applicationMessage ?? throw new ArgumentNullException(nameof(applicationMessage));
|
||||
}
|
||||
|
||||
public MqttApplicationMessage ApplicationMessage { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,454 +0,0 @@
|
||||
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.Internal;
|
||||
using MQTTnet.Core.Packets;
|
||||
using MQTTnet.Core.Protocol;
|
||||
|
||||
namespace MQTTnet.Core.Client
|
||||
{
|
||||
public class MqttClient : IMqttClient
|
||||
{
|
||||
private readonly HashSet<ushort> _unacknowledgedPublishPackets = new HashSet<ushort>();
|
||||
private readonly MqttPacketDispatcher _packetDispatcher = new MqttPacketDispatcher();
|
||||
private readonly MqttClientOptions _options;
|
||||
private readonly IMqttCommunicationAdapter _adapter;
|
||||
|
||||
private bool _isReceivingPackets;
|
||||
private int _latestPacketIdentifier;
|
||||
private CancellationTokenSource _cancellationTokenSource;
|
||||
|
||||
public MqttClient(MqttClientOptions options, IMqttCommunicationAdapter adapter)
|
||||
{
|
||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
||||
_adapter = adapter ?? throw new ArgumentNullException(nameof(adapter));
|
||||
|
||||
_adapter.PacketSerializer.ProtocolVersion = options.ProtocolVersion;
|
||||
}
|
||||
|
||||
public event EventHandler Connected;
|
||||
public event EventHandler Disconnected;
|
||||
public event EventHandler<MqttApplicationMessageReceivedEventArgs> ApplicationMessageReceived;
|
||||
|
||||
public bool IsConnected => _cancellationTokenSource != null && !_cancellationTokenSource.IsCancellationRequested;
|
||||
|
||||
public async Task ConnectAsync(MqttApplicationMessage willApplicationMessage = null)
|
||||
{
|
||||
ThrowIfConnected("It is not allowed to connect with a server after the connection is established.");
|
||||
|
||||
try
|
||||
{
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
_latestPacketIdentifier = 0;
|
||||
_packetDispatcher.Reset();
|
||||
|
||||
MqttTrace.Verbose(nameof(MqttClient), "Trying to connect with server.");
|
||||
await _adapter.ConnectAsync(_options.DefaultCommunicationTimeout, _options).ConfigureAwait(false);
|
||||
MqttTrace.Verbose(nameof(MqttClient), "Connection with server established.");
|
||||
|
||||
await SetupIncomingPacketProcessingAsync();
|
||||
await AuthenticateAsync(willApplicationMessage);
|
||||
|
||||
MqttTrace.Verbose(nameof(MqttClient), "MQTT connection with server established.");
|
||||
|
||||
if (_options.KeepAlivePeriod != TimeSpan.Zero)
|
||||
{
|
||||
StartSendKeepAliveMessages(_cancellationTokenSource.Token);
|
||||
}
|
||||
|
||||
Connected?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
await DisconnectInternalAsync().ConfigureAwait(false);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task DisconnectAsync()
|
||||
{
|
||||
if (!IsConnected)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await SendAsync(new MqttDisconnectPacket()).ConfigureAwait(false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
await DisconnectInternalAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<IList<MqttSubscribeResult>> SubscribeAsync(IEnumerable<TopicFilter> topicFilters)
|
||||
{
|
||||
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
|
||||
|
||||
ThrowIfNotConnected();
|
||||
|
||||
var subscribePacket = new MqttSubscribePacket
|
||||
{
|
||||
PacketIdentifier = GetNewPacketIdentifier(),
|
||||
TopicFilters = topicFilters.ToList()
|
||||
};
|
||||
|
||||
var response = await SendAndReceiveAsync<MqttSubAckPacket>(subscribePacket).ConfigureAwait(false);
|
||||
|
||||
if (response.SubscribeReturnCodes.Count != subscribePacket.TopicFilters.Count)
|
||||
{
|
||||
throw new MqttProtocolViolationException("The return codes are not matching the topic filters [MQTT-3.9.3-1].");
|
||||
}
|
||||
|
||||
return subscribePacket.TopicFilters.Select((t, i) => new MqttSubscribeResult(t, response.SubscribeReturnCodes[i])).ToList();
|
||||
}
|
||||
|
||||
public async Task UnsubscribeAsync(IEnumerable<string> topicFilters)
|
||||
{
|
||||
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
|
||||
|
||||
ThrowIfNotConnected();
|
||||
|
||||
var unsubscribePacket = new MqttUnsubscribePacket
|
||||
{
|
||||
PacketIdentifier = GetNewPacketIdentifier(),
|
||||
TopicFilters = topicFilters.ToList()
|
||||
};
|
||||
|
||||
await SendAndReceiveAsync<MqttUnsubAckPacket>(unsubscribePacket);
|
||||
}
|
||||
|
||||
public async Task PublishAsync(IEnumerable<MqttApplicationMessage> applicationMessages)
|
||||
{
|
||||
ThrowIfNotConnected();
|
||||
|
||||
var publishPackets = applicationMessages.Select(m => m.ToPublishPacket());
|
||||
|
||||
foreach (var qosGroup in publishPackets.GroupBy(p => p.QualityOfServiceLevel))
|
||||
{
|
||||
var qosPackets = qosGroup.ToArray();
|
||||
switch (qosGroup.Key)
|
||||
{
|
||||
case MqttQualityOfServiceLevel.AtMostOnce:
|
||||
{
|
||||
// No packet identifier is used for QoS 0 [3.3.2.2 Packet Identifier]
|
||||
await _adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, _cancellationTokenSource.Token, qosPackets);
|
||||
break;
|
||||
}
|
||||
case MqttQualityOfServiceLevel.AtLeastOnce:
|
||||
{
|
||||
foreach (var publishPacket in qosPackets)
|
||||
{
|
||||
publishPacket.PacketIdentifier = GetNewPacketIdentifier();
|
||||
await SendAndReceiveAsync<MqttPubAckPacket>(publishPacket);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case MqttQualityOfServiceLevel.ExactlyOnce:
|
||||
{
|
||||
foreach (var publishPacket in qosPackets)
|
||||
{
|
||||
publishPacket.PacketIdentifier = GetNewPacketIdentifier();
|
||||
var pubRecPacket = await SendAndReceiveAsync<MqttPubRecPacket>(publishPacket).ConfigureAwait(false);
|
||||
await SendAndReceiveAsync<MqttPubCompPacket>(pubRecPacket.CreateResponse<MqttPubRelPacket>()).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task AuthenticateAsync(MqttApplicationMessage willApplicationMessage)
|
||||
{
|
||||
var connectPacket = new MqttConnectPacket
|
||||
{
|
||||
ClientId = _options.ClientId,
|
||||
Username = _options.UserName,
|
||||
Password = _options.Password,
|
||||
CleanSession = _options.CleanSession,
|
||||
KeepAlivePeriod = (ushort)_options.KeepAlivePeriod.TotalSeconds,
|
||||
WillMessage = willApplicationMessage
|
||||
};
|
||||
|
||||
var response = await SendAndReceiveAsync<MqttConnAckPacket>(connectPacket).ConfigureAwait(false);
|
||||
if (response.ConnectReturnCode != MqttConnectReturnCode.ConnectionAccepted)
|
||||
{
|
||||
throw new MqttConnectingFailedException(response.ConnectReturnCode);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SetupIncomingPacketProcessingAsync()
|
||||
{
|
||||
_isReceivingPackets = false;
|
||||
|
||||
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||
Task.Factory.StartNew(
|
||||
() => ReceivePackets(_cancellationTokenSource.Token),
|
||||
_cancellationTokenSource.Token,
|
||||
TaskCreationOptions.LongRunning,
|
||||
TaskScheduler.Default).ConfigureAwait(false);
|
||||
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||
|
||||
while (!_isReceivingPackets && _cancellationTokenSource != null && !_cancellationTokenSource.IsCancellationRequested)
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromMilliseconds(100));
|
||||
}
|
||||
}
|
||||
|
||||
private void ThrowIfNotConnected()
|
||||
{
|
||||
if (!IsConnected) throw new MqttCommunicationException("The client is not connected.");
|
||||
}
|
||||
|
||||
private void ThrowIfConnected(string message)
|
||||
{
|
||||
if (IsConnected) throw new MqttProtocolViolationException(message);
|
||||
}
|
||||
|
||||
private async Task DisconnectInternalAsync()
|
||||
{
|
||||
var cts = _cancellationTokenSource;
|
||||
if (cts == null || cts.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
cts.Cancel(false);
|
||||
cts.Dispose();
|
||||
_cancellationTokenSource = null;
|
||||
|
||||
try
|
||||
{
|
||||
await _adapter.DisconnectAsync(_options.DefaultCommunicationTimeout).ConfigureAwait(false);
|
||||
MqttTrace.Information(nameof(MqttClient), "Disconnected from adapter.");
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
MqttTrace.Warning(nameof(MqttClient), exception, "Error while disconnecting from adapter.");
|
||||
}
|
||||
finally
|
||||
{
|
||||
Disconnected?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ProcessReceivedPacketAsync(MqttBasePacket packet)
|
||||
{
|
||||
try
|
||||
{
|
||||
MqttTrace.Information(nameof(MqttClient), "Received <<< {0}", packet);
|
||||
|
||||
if (packet is MqttPingReqPacket)
|
||||
{
|
||||
await SendAsync(new MqttPingRespPacket());
|
||||
return;
|
||||
}
|
||||
|
||||
if (packet is MqttDisconnectPacket)
|
||||
{
|
||||
await DisconnectAsync();
|
||||
return;
|
||||
}
|
||||
|
||||
if (packet is MqttPublishPacket publishPacket)
|
||||
{
|
||||
await ProcessReceivedPublishPacket(publishPacket);
|
||||
return;
|
||||
}
|
||||
|
||||
if (packet is MqttPubRelPacket pubRelPacket)
|
||||
{
|
||||
await ProcessReceivedPubRelPacket(pubRelPacket);
|
||||
return;
|
||||
}
|
||||
|
||||
_packetDispatcher.Dispatch(packet);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
MqttTrace.Error(nameof(MqttClient), exception, "Unhandled exception while processing received packet.");
|
||||
}
|
||||
}
|
||||
|
||||
private void FireApplicationMessageReceivedEvent(MqttPublishPacket publishPacket)
|
||||
{
|
||||
try
|
||||
{
|
||||
var applicationMessage = publishPacket.ToApplicationMessage();
|
||||
ApplicationMessageReceived?.Invoke(this, new MqttApplicationMessageReceivedEventArgs(applicationMessage));
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
MqttTrace.Error(nameof(MqttClient), exception, "Unhandled exception while handling application message.");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ProcessReceivedPublishPacket(MqttPublishPacket publishPacket)
|
||||
{
|
||||
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce)
|
||||
{
|
||||
FireApplicationMessageReceivedEvent(publishPacket);
|
||||
return;
|
||||
}
|
||||
|
||||
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtLeastOnce)
|
||||
{
|
||||
FireApplicationMessageReceivedEvent(publishPacket);
|
||||
await SendAsync(new MqttPubAckPacket { PacketIdentifier = publishPacket.PacketIdentifier });
|
||||
return;
|
||||
}
|
||||
|
||||
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.ExactlyOnce)
|
||||
{
|
||||
// QoS 2 is implement as method "B" [4.3.3 QoS 2: Exactly once delivery]
|
||||
lock (_unacknowledgedPublishPackets)
|
||||
{
|
||||
_unacknowledgedPublishPackets.Add(publishPacket.PacketIdentifier);
|
||||
}
|
||||
|
||||
FireApplicationMessageReceivedEvent(publishPacket);
|
||||
await SendAsync(new MqttPubRecPacket { PacketIdentifier = publishPacket.PacketIdentifier });
|
||||
return;
|
||||
}
|
||||
|
||||
throw new MqttCommunicationException("Received a not supported QoS level.");
|
||||
}
|
||||
|
||||
private Task ProcessReceivedPubRelPacket(MqttPubRelPacket pubRelPacket)
|
||||
{
|
||||
lock (_unacknowledgedPublishPackets)
|
||||
{
|
||||
_unacknowledgedPublishPackets.Remove(pubRelPacket.PacketIdentifier);
|
||||
}
|
||||
|
||||
return SendAsync(pubRelPacket.CreateResponse<MqttPubCompPacket>());
|
||||
}
|
||||
|
||||
private Task SendAsync(MqttBasePacket packet)
|
||||
{
|
||||
return _adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, _cancellationTokenSource.Token, packet);
|
||||
}
|
||||
|
||||
private async Task<TResponsePacket> SendAndReceiveAsync<TResponsePacket>(MqttBasePacket requestPacket) where TResponsePacket : MqttBasePacket
|
||||
{
|
||||
var packetAwaiter = _packetDispatcher.WaitForPacketAsync(requestPacket, typeof(TResponsePacket), _options.DefaultCommunicationTimeout);
|
||||
await _adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, _cancellationTokenSource.Token, requestPacket).ConfigureAwait(false);
|
||||
return (TResponsePacket)await packetAwaiter.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
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).ConfigureAwait(false);
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await SendAndReceiveAsync<MqttPingRespPacket>(new MqttPingReqPacket()).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
}
|
||||
catch (MqttCommunicationException exception)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
MqttTrace.Warning(nameof(MqttClient), exception, "MQTT communication exception while sending/receiving keep alive packets.");
|
||||
await DisconnectInternalAsync().ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
MqttTrace.Warning(nameof(MqttClient), exception, "Unhandled exception while sending/receiving keep alive packets.");
|
||||
await DisconnectInternalAsync().ConfigureAwait(false);
|
||||
}
|
||||
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)
|
||||
{
|
||||
_isReceivingPackets = true;
|
||||
|
||||
var packet = await _adapter.ReceivePacketAsync(TimeSpan.Zero, cancellationToken).ConfigureAwait(false);
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
StartProcessReceivedPacket(packet, cancellationToken);
|
||||
}
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
}
|
||||
catch (MqttCommunicationException exception)
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
MqttTrace.Warning(nameof(MqttClient), exception, "MQTT communication exception while receiving packets.");
|
||||
await DisconnectInternalAsync().ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
MqttTrace.Error(nameof(MqttClient), exception, "Unhandled exception while receiving packets.");
|
||||
await DisconnectInternalAsync().ConfigureAwait(false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
MqttTrace.Information(nameof(MqttClient), "Stopped receiving packets.");
|
||||
}
|
||||
}
|
||||
|
||||
private void StartProcessReceivedPacket(MqttBasePacket packet, CancellationToken cancellationToken)
|
||||
{
|
||||
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||
Task.Run(() => ProcessReceivedPacketAsync(packet), cancellationToken).ConfigureAwait(false);
|
||||
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||
}
|
||||
|
||||
private void StartSendKeepAliveMessages(CancellationToken cancellationToken)
|
||||
{
|
||||
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||
Task.Factory.StartNew(() => SendKeepAliveMessagesAsync(cancellationToken), cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Default).ConfigureAwait(false);
|
||||
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
using System;
|
||||
using MQTTnet.Core.Serializer;
|
||||
|
||||
namespace MQTTnet.Core.Client
|
||||
{
|
||||
public sealed class MqttClientOptions
|
||||
{
|
||||
public string Server { get; set; }
|
||||
|
||||
public int? Port { get; set; }
|
||||
|
||||
public MqttClientTlsOptions TlsOptions { get; set; } = new MqttClientTlsOptions();
|
||||
|
||||
public string UserName { get; set; }
|
||||
|
||||
public string Password { get; set; }
|
||||
|
||||
public string ClientId { get; set; } = Guid.NewGuid().ToString().Replace("-", string.Empty);
|
||||
|
||||
public bool CleanSession { get; set; } = true;
|
||||
|
||||
public TimeSpan KeepAlivePeriod { get; set; } = TimeSpan.FromSeconds(5);
|
||||
|
||||
public TimeSpan DefaultCommunicationTimeout { get; set; } = TimeSpan.FromSeconds(10);
|
||||
|
||||
public MqttProtocolVersion ProtocolVersion { get; set; } = MqttProtocolVersion.V311;
|
||||
|
||||
public MqttConnectionType ConnectionType { get; set; } = MqttConnectionType.Tcp;
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MQTTnet.Core.Client
|
||||
{
|
||||
public sealed class MqttClientTlsOptions
|
||||
{
|
||||
public bool UseTls { get; set; }
|
||||
|
||||
public bool CheckCertificateRevocation { get; set; }
|
||||
|
||||
public List<byte[]> Certificates { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
namespace MQTTnet.Core.Client
|
||||
{
|
||||
public enum MqttConnectionType
|
||||
{
|
||||
Tcp,
|
||||
Ws
|
||||
}
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Core.Diagnostics;
|
||||
using MQTTnet.Core.Exceptions;
|
||||
using MQTTnet.Core.Internal;
|
||||
using MQTTnet.Core.Packets;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace MQTTnet.Core.Client
|
||||
{
|
||||
public class MqttPacketDispatcher
|
||||
{
|
||||
private readonly ConcurrentDictionary<Type, TaskCompletionSource<MqttBasePacket>> _packetByResponseType = new ConcurrentDictionary<Type, TaskCompletionSource<MqttBasePacket>>();
|
||||
private readonly ConcurrentDictionary<Type, ConcurrentDictionary<ushort, TaskCompletionSource<MqttBasePacket>>> _packetByResponseTypeAndIdentifier = new ConcurrentDictionary<Type, ConcurrentDictionary<ushort, TaskCompletionSource<MqttBasePacket>>>();
|
||||
|
||||
public async Task<MqttBasePacket> WaitForPacketAsync(MqttBasePacket request, Type responseType, TimeSpan timeout)
|
||||
{
|
||||
if (request == null) throw new ArgumentNullException(nameof(request));
|
||||
|
||||
var packetAwaiter = AddPacketAwaiter(request, responseType);
|
||||
try
|
||||
{
|
||||
return await packetAwaiter.Task.TimeoutAfter(timeout).ConfigureAwait(false);
|
||||
}
|
||||
catch (MqttCommunicationTimedOutException)
|
||||
{
|
||||
MqttTrace.Warning(nameof(MqttPacketDispatcher), "Timeout while waiting for packet of type '{0}'.", responseType.Name);
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
RemovePacketAwaiter(request, responseType);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispatch(MqttBasePacket packet)
|
||||
{
|
||||
if (packet == null) throw new ArgumentNullException(nameof(packet));
|
||||
|
||||
var type = packet.GetType();
|
||||
if (packet is IMqttPacketWithIdentifier withIdentifier)
|
||||
{
|
||||
if (_packetByResponseTypeAndIdentifier.TryGetValue(type, out var byid))
|
||||
{
|
||||
if (byid.TryRemove(withIdentifier.PacketIdentifier, out var tcs))
|
||||
{
|
||||
tcs.TrySetResult(packet);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (_packetByResponseType.TryRemove(type, out var tcs))
|
||||
{
|
||||
tcs.TrySetResult(packet);
|
||||
return;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException($"Packet of type '{type.Name}' not handled or dispatched.");
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_packetByResponseTypeAndIdentifier.Clear();
|
||||
_packetByResponseType.Clear();
|
||||
}
|
||||
|
||||
private TaskCompletionSource<MqttBasePacket> AddPacketAwaiter(MqttBasePacket request, Type responseType)
|
||||
{
|
||||
var tcs = new TaskCompletionSource<MqttBasePacket>();
|
||||
|
||||
if (request is IMqttPacketWithIdentifier requestWithIdentifier)
|
||||
{
|
||||
var byId = _packetByResponseTypeAndIdentifier.GetOrAdd(responseType, key => new ConcurrentDictionary<ushort, TaskCompletionSource<MqttBasePacket>>());
|
||||
byId[requestWithIdentifier.PacketIdentifier] = tcs;
|
||||
}
|
||||
else
|
||||
{
|
||||
_packetByResponseType[responseType] = tcs;
|
||||
}
|
||||
|
||||
return tcs;
|
||||
}
|
||||
|
||||
private void RemovePacketAwaiter(MqttBasePacket request, Type responseType)
|
||||
{
|
||||
if (request is IMqttPacketWithIdentifier requestWithIdentifier)
|
||||
{
|
||||
var byId = _packetByResponseTypeAndIdentifier.GetOrAdd(responseType, key => new ConcurrentDictionary<ushort, TaskCompletionSource<MqttBasePacket>>());
|
||||
byId.TryRemove(requestWithIdentifier.PacketIdentifier, out var _);
|
||||
}
|
||||
else
|
||||
{
|
||||
_packetByResponseType.TryRemove(responseType, out var _);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace MQTTnet.Core.Diagnostics
|
||||
{
|
||||
public static class MqttTrace
|
||||
{
|
||||
public static event EventHandler<MqttTraceMessagePublishedEventArgs> TraceMessagePublished;
|
||||
|
||||
public static void Verbose(string source, string message, params object[] parameters)
|
||||
{
|
||||
Publish(source, MqttTraceLevel.Verbose, null, message, parameters);
|
||||
}
|
||||
|
||||
public static void Information(string source, string message, params object[] parameters)
|
||||
{
|
||||
Publish(source, MqttTraceLevel.Information, null, message, parameters);
|
||||
}
|
||||
|
||||
public static void Warning(string source, string message, params object[] parameters)
|
||||
{
|
||||
Publish(source, MqttTraceLevel.Warning, null, message, parameters);
|
||||
}
|
||||
|
||||
public static void Warning(string source, Exception exception, string message, params object[] parameters)
|
||||
{
|
||||
Publish(source, MqttTraceLevel.Warning, exception, message, parameters);
|
||||
}
|
||||
|
||||
public static void Error(string source, string message, params object[] parameters)
|
||||
{
|
||||
Publish(source, MqttTraceLevel.Error, null, message, parameters);
|
||||
}
|
||||
|
||||
public static void Error(string source, Exception exception, string message, params object[] parameters)
|
||||
{
|
||||
Publish(source, MqttTraceLevel.Error, exception, message, parameters);
|
||||
}
|
||||
|
||||
private static void Publish(string source, MqttTraceLevel traceLevel, Exception exception, string message, params object[] parameters)
|
||||
{
|
||||
var handler = TraceMessagePublished;
|
||||
if (handler == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (parameters?.Length > 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
message = string.Format(message, parameters);
|
||||
}
|
||||
catch (Exception formatException)
|
||||
{
|
||||
Error(nameof(MqttTrace), formatException, "Error while tracing message: " + message);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
handler.Invoke(null, new MqttTraceMessagePublishedEventArgs(Environment.CurrentManagedThreadId, source, traceLevel, message, exception));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
namespace MQTTnet.Core.Diagnostics
|
||||
{
|
||||
public enum MqttTraceLevel
|
||||
{
|
||||
Verbose,
|
||||
Information,
|
||||
Warning,
|
||||
Error
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace MQTTnet.Core.Diagnostics
|
||||
{
|
||||
public sealed 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; }
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
namespace MQTTnet.Core.Exceptions
|
||||
{
|
||||
public sealed class MqttCommunicationTimedOutException : MqttCommunicationException
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Core.Exceptions;
|
||||
|
||||
namespace MQTTnet.Core.Internal
|
||||
{
|
||||
public static class TaskExtensions
|
||||
{
|
||||
public static async Task TimeoutAfter(this Task task, TimeSpan timeout)
|
||||
{
|
||||
using (var cancellationTokenSource = new CancellationTokenSource())
|
||||
{
|
||||
try
|
||||
{
|
||||
var timeoutTask = Task.Delay(timeout, cancellationTokenSource.Token);
|
||||
var finishedTask = await Task.WhenAny(timeoutTask, task).ConfigureAwait(false);
|
||||
|
||||
if (finishedTask == timeoutTask)
|
||||
{
|
||||
throw new MqttCommunicationTimedOutException();
|
||||
}
|
||||
|
||||
if (task.IsCanceled)
|
||||
{
|
||||
throw new TaskCanceledException();
|
||||
}
|
||||
|
||||
if (task.IsFaulted)
|
||||
{
|
||||
throw new MqttCommunicationException(task.Exception.GetBaseException());
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
cancellationTokenSource.Cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<TResult> TimeoutAfter<TResult>(this Task<TResult> task, TimeSpan timeout)
|
||||
{
|
||||
using (var cancellationTokenSource = new CancellationTokenSource())
|
||||
{
|
||||
try
|
||||
{
|
||||
var timeoutTask = Task.Delay(timeout, cancellationTokenSource.Token);
|
||||
var finishedTask = await Task.WhenAny(timeoutTask, task).ConfigureAwait(false);
|
||||
|
||||
if (finishedTask == timeoutTask)
|
||||
{
|
||||
throw new MqttCommunicationTimedOutException();
|
||||
}
|
||||
|
||||
if (task.IsCanceled)
|
||||
{
|
||||
throw new TaskCanceledException();
|
||||
}
|
||||
|
||||
if (task.IsFaulted)
|
||||
{
|
||||
throw new MqttCommunicationException(task.Exception.GetBaseException());
|
||||
}
|
||||
|
||||
return task.Result;
|
||||
}
|
||||
finally
|
||||
{
|
||||
cancellationTokenSource.Cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard1.1</TargetFramework>
|
||||
<AssemblyName>MQTTnet.Core</AssemblyName>
|
||||
<RootNamespace>MQTTnet.Core</RootNamespace>
|
||||
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
|
||||
<DebugType>Full</DebugType>
|
||||
<Description></Description>
|
||||
<Product></Product>
|
||||
<Company></Company>
|
||||
<Authors></Authors>
|
||||
<Version>0.0.0.0</Version>
|
||||
<PackageId></PackageId>
|
||||
<Copyright></Copyright>
|
||||
<PackageProjectUrl></PackageProjectUrl>
|
||||
<PackageIconUrl></PackageIconUrl>
|
||||
<RepositoryUrl></RepositoryUrl>
|
||||
<PackageTags></PackageTags>
|
||||
<FileVersion>2.3.1.0</FileVersion>
|
||||
<AssemblyVersion>2.3.1.0</AssemblyVersion>
|
||||
<PackageLicenseUrl></PackageLicenseUrl>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Any CPU|AnyCPU'" />
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Any CPU|x64'" />
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x86'" />
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x86'" />
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Any CPU|x86'" />
|
||||
|
||||
</Project>
|
||||
@@ -1,24 +0,0 @@
|
||||
using System;
|
||||
using MQTTnet.Core.Protocol;
|
||||
|
||||
namespace MQTTnet.Core
|
||||
{
|
||||
public sealed class MqttApplicationMessage
|
||||
{
|
||||
public MqttApplicationMessage(string topic, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel, bool retain)
|
||||
{
|
||||
Topic = topic ?? throw new ArgumentNullException(nameof(topic));
|
||||
Payload = payload ?? throw new ArgumentNullException(nameof(payload));
|
||||
QualityOfServiceLevel = qualityOfServiceLevel;
|
||||
Retain = retain;
|
||||
}
|
||||
|
||||
public string Topic { get; }
|
||||
|
||||
public byte[] Payload { get; }
|
||||
|
||||
public MqttQualityOfServiceLevel QualityOfServiceLevel { get; }
|
||||
|
||||
public bool Retain { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using MQTTnet.Core.Protocol;
|
||||
|
||||
namespace MQTTnet.Core
|
||||
{
|
||||
public class MqttApplicationMessageFactory
|
||||
{
|
||||
public MqttApplicationMessage CreateApplicationMessage(string topic, MqttQualityOfServiceLevel qualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce, bool retain = false)
|
||||
{
|
||||
if (topic == null) throw new ArgumentNullException(nameof(topic));
|
||||
|
||||
return CreateApplicationMessage(topic, new byte[0], qualityOfServiceLevel, retain);
|
||||
}
|
||||
|
||||
public MqttApplicationMessage CreateApplicationMessage(string topic, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce, bool retain = false)
|
||||
{
|
||||
if (topic == null) throw new ArgumentNullException(nameof(topic));
|
||||
|
||||
if (payload == null)
|
||||
{
|
||||
payload = new byte[0];
|
||||
}
|
||||
|
||||
return new MqttApplicationMessage(topic, payload, qualityOfServiceLevel, retain);
|
||||
}
|
||||
|
||||
public MqttApplicationMessage CreateApplicationMessage<TPayload>(string topic, TPayload payload, MqttQualityOfServiceLevel qualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce, bool retain = false)
|
||||
{
|
||||
if (topic == null) throw new ArgumentNullException(nameof(topic));
|
||||
|
||||
var payloadString = Convert.ToString(payload, CultureInfo.InvariantCulture);
|
||||
var payloadBuffer = string.IsNullOrEmpty(payloadString) ? new byte[0] : Encoding.UTF8.GetBytes(payloadString);
|
||||
|
||||
return CreateApplicationMessage(topic, payloadBuffer, qualityOfServiceLevel, retain);
|
||||
}
|
||||
|
||||
public MqttApplicationMessage CreateApplicationMessage(string topic, Stream payload, MqttQualityOfServiceLevel qualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce, bool retain = false)
|
||||
{
|
||||
if (topic == null) throw new ArgumentNullException(nameof(topic));
|
||||
|
||||
byte[] payloadBuffer;
|
||||
if (payload == null || payload.Length == 0)
|
||||
{
|
||||
payloadBuffer = new byte[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
payloadBuffer = new byte[payload.Length - payload.Position];
|
||||
payload.Read(payloadBuffer, 0, payloadBuffer.Length);
|
||||
}
|
||||
|
||||
return CreateApplicationMessage(topic, payloadBuffer, qualityOfServiceLevel, retain);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
namespace MQTTnet.Core.Packets
|
||||
{
|
||||
public interface IMqttPacketWithIdentifier
|
||||
{
|
||||
ushort PacketIdentifier { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
using MQTTnet.Core.Protocol;
|
||||
|
||||
namespace MQTTnet.Core.Packets
|
||||
{
|
||||
public sealed class MqttConnAckPacket : MqttBasePacket
|
||||
{
|
||||
public bool IsSessionPresent { get; set; }
|
||||
|
||||
public MqttConnectReturnCode ConnectReturnCode { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return nameof(MqttConnAckPacket) + ": [ConnectReturnCode=" + ConnectReturnCode + "] [IsSessionPresent=" + IsSessionPresent + "]";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
namespace MQTTnet.Core.Packets
|
||||
{
|
||||
public sealed class MqttDisconnectPacket : MqttBasePacket
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace MQTTnet.Core.Packets
|
||||
{
|
||||
public static class MqttPacketExtensions
|
||||
{
|
||||
public static TResponsePacket CreateResponse<TResponsePacket>(this MqttBasePacket packet)
|
||||
{
|
||||
if (packet == null) throw new ArgumentNullException(nameof(packet));
|
||||
|
||||
var responsePacket = Activator.CreateInstance<TResponsePacket>();
|
||||
|
||||
if (responsePacket is IMqttPacketWithIdentifier responsePacketWithIdentifier)
|
||||
{
|
||||
var requestPacketWithIdentifier = packet as IMqttPacketWithIdentifier;
|
||||
if (requestPacketWithIdentifier == null)
|
||||
{
|
||||
throw new InvalidOperationException("Response packet has PacketIdentifier but request packet does not.");
|
||||
}
|
||||
|
||||
responsePacketWithIdentifier.PacketIdentifier = requestPacketWithIdentifier.PacketIdentifier;
|
||||
}
|
||||
|
||||
return responsePacket;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
using MQTTnet.Core.Protocol;
|
||||
|
||||
namespace MQTTnet.Core.Packets
|
||||
{
|
||||
public class MqttPacketHeader
|
||||
{
|
||||
public MqttControlPacketType ControlPacketType { get; set; }
|
||||
|
||||
public byte FixedHeader { get; set; }
|
||||
|
||||
public int BodyLength { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
namespace MQTTnet.Core.Packets
|
||||
{
|
||||
public sealed class MqttPingReqPacket : MqttBasePacket
|
||||
{
|
||||
public override string ToString()
|
||||
{
|
||||
return nameof(MqttPingReqPacket);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
namespace MQTTnet.Core.Packets
|
||||
{
|
||||
public sealed class MqttPingRespPacket : MqttBasePacket
|
||||
{
|
||||
public override string ToString()
|
||||
{
|
||||
return nameof(MqttPingRespPacket);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
namespace MQTTnet.Core.Packets
|
||||
{
|
||||
public sealed class MqttPubAckPacket : MqttBasePublishPacket
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
namespace MQTTnet.Core.Packets
|
||||
{
|
||||
public sealed class MqttPubCompPacket : MqttBasePublishPacket
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
namespace MQTTnet.Core.Packets
|
||||
{
|
||||
public sealed class MqttPubRecPacket : MqttBasePublishPacket
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
namespace MQTTnet.Core.Packets
|
||||
{
|
||||
public sealed class MqttPubRelPacket : MqttBasePublishPacket
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using MQTTnet.Core.Protocol;
|
||||
|
||||
namespace MQTTnet.Core.Packets
|
||||
{
|
||||
public sealed class MqttSubAckPacket : MqttBasePacket, IMqttPacketWithIdentifier
|
||||
{
|
||||
public ushort PacketIdentifier { get; set; }
|
||||
|
||||
public IList<MqttSubscribeReturnCode> SubscribeReturnCodes { get; set; } = new List<MqttSubscribeReturnCode>();
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var subscribeReturnCodesText = string.Join(",", SubscribeReturnCodes.Select(f => f.ToString()));
|
||||
return nameof(MqttSubAckPacket) + ": [PacketIdentifier=" + PacketIdentifier + "] [SubscribeReturnCodes=" + subscribeReturnCodesText + "]";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace MQTTnet.Core.Packets
|
||||
{
|
||||
public sealed class MqttSubscribePacket : MqttBasePacket, IMqttPacketWithIdentifier
|
||||
{
|
||||
public ushort PacketIdentifier { get; set; }
|
||||
|
||||
public IList<TopicFilter> TopicFilters { get; set; } = new List<TopicFilter>();
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var topicFiltersText = string.Join(",", TopicFilters.Select(f => $"{f.Topic}@{f.QualityOfServiceLevel}"));
|
||||
return nameof(MqttSubscribePacket) + ": [PacketIdentifier=" + PacketIdentifier + "] [TopicFilters=" + topicFiltersText + "]";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
namespace MQTTnet.Core.Packets
|
||||
{
|
||||
public sealed class MqttUnsubAckPacket : MqttBasePacket, IMqttPacketWithIdentifier
|
||||
{
|
||||
public ushort PacketIdentifier { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MQTTnet.Core.Packets
|
||||
{
|
||||
public sealed class MqttUnsubscribePacket : MqttBasePacket, IMqttPacketWithIdentifier
|
||||
{
|
||||
public ushort PacketIdentifier { get; set; }
|
||||
|
||||
public IList<string> TopicFilters { get; set; } = new List<string>();
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace MQTTnet.Core.Serializer
|
||||
{
|
||||
public sealed class ByteReader
|
||||
{
|
||||
private readonly int _source;
|
||||
private int _index;
|
||||
|
||||
public ByteReader(int source)
|
||||
{
|
||||
_source = source;
|
||||
}
|
||||
|
||||
public bool Read()
|
||||
{
|
||||
if (_index >= 8)
|
||||
{
|
||||
throw new InvalidOperationException("End of byte reached.");
|
||||
}
|
||||
|
||||
var result = ((1 << _index) & _source) > 0;
|
||||
_index++;
|
||||
return result;
|
||||
}
|
||||
|
||||
public int Read(int count)
|
||||
{
|
||||
if (_index + count > 8)
|
||||
{
|
||||
throw new InvalidOperationException("End of byte will be reached.");
|
||||
}
|
||||
|
||||
var result = 0;
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
if (((1 << _index) & _source) > 0)
|
||||
{
|
||||
result |= 1 << i;
|
||||
}
|
||||
|
||||
_index++;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace MQTTnet.Core.Serializer
|
||||
{
|
||||
public sealed class ByteWriter
|
||||
{
|
||||
private int _index;
|
||||
private int _byte;
|
||||
|
||||
public byte Value => (byte)_byte;
|
||||
|
||||
public void Write(int @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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Core.Adapter;
|
||||
using MQTTnet.Core.Exceptions;
|
||||
using MQTTnet.Core.Protocol;
|
||||
using MQTTnet.Core.Packets;
|
||||
|
||||
namespace MQTTnet.Core.Serializer
|
||||
{
|
||||
public sealed class MqttPacketReader : BinaryReader
|
||||
{
|
||||
private readonly ReceivedMqttPacket _receivedMqttPacket;
|
||||
|
||||
public MqttPacketReader(ReceivedMqttPacket receivedMqttPacket)
|
||||
: base(receivedMqttPacket.Body, Encoding.UTF8, true)
|
||||
{
|
||||
_receivedMqttPacket = receivedMqttPacket;
|
||||
}
|
||||
|
||||
public bool EndOfRemainingData => BaseStream.Position == _receivedMqttPacket.Header.BodyLength;
|
||||
|
||||
public static MqttPacketHeader ReadHeaderFromSource(Stream stream, CancellationToken cancellationToken)
|
||||
{
|
||||
var fixedHeader = (byte)stream.ReadByte();
|
||||
var controlPacketType = (MqttControlPacketType)(fixedHeader >> 4);
|
||||
var bodyLength = ReadBodyLengthFromSource(stream, cancellationToken);
|
||||
|
||||
return new MqttPacketHeader
|
||||
{
|
||||
FixedHeader = fixedHeader,
|
||||
ControlPacketType = controlPacketType,
|
||||
BodyLength = bodyLength
|
||||
};
|
||||
}
|
||||
|
||||
public override ushort ReadUInt16()
|
||||
{
|
||||
var buffer = ReadBytes(2);
|
||||
|
||||
var temp = buffer[0];
|
||||
buffer[0] = buffer[1];
|
||||
buffer[1] = temp;
|
||||
|
||||
return BitConverter.ToUInt16(buffer, 0);
|
||||
}
|
||||
|
||||
public string ReadStringWithLengthPrefix()
|
||||
{
|
||||
var buffer = ReadWithLengthPrefix();
|
||||
return Encoding.UTF8.GetString(buffer, 0, buffer.Length);
|
||||
}
|
||||
|
||||
public byte[] ReadWithLengthPrefix()
|
||||
{
|
||||
var length = ReadUInt16();
|
||||
return ReadBytes(length);
|
||||
}
|
||||
|
||||
public byte[] ReadRemainingData()
|
||||
{
|
||||
return ReadBytes(_receivedMqttPacket.Header.BodyLength - (int)BaseStream.Position);
|
||||
}
|
||||
|
||||
private static int ReadBodyLengthFromSource(Stream stream, CancellationToken cancellationToken)
|
||||
{
|
||||
// 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
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
throw new TaskCanceledException();
|
||||
}
|
||||
|
||||
encodedByte = (byte)stream.ReadByte();
|
||||
value += (encodedByte & 127) * multiplier;
|
||||
multiplier *= 128;
|
||||
if (multiplier > 128 * 128 * 128)
|
||||
{
|
||||
throw new MqttProtocolViolationException("Remaining length is invalid.");
|
||||
}
|
||||
} while ((encodedByte & 128) != 0);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,609 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using MQTTnet.Core.Adapter;
|
||||
using MQTTnet.Core.Exceptions;
|
||||
using MQTTnet.Core.Packets;
|
||||
using MQTTnet.Core.Protocol;
|
||||
|
||||
namespace MQTTnet.Core.Serializer
|
||||
{
|
||||
public sealed class MqttPacketSerializer : IMqttPacketSerializer
|
||||
{
|
||||
private static byte[] ProtocolVersionV311Name { get; } = Encoding.UTF8.GetBytes("MQTT");
|
||||
private static byte[] ProtocolVersionV310Name { get; } = Encoding.UTF8.GetBytes("MQIs");
|
||||
|
||||
public MqttProtocolVersion ProtocolVersion { get; set; } = MqttProtocolVersion.V311;
|
||||
|
||||
public byte[] Serialize(MqttBasePacket packet)
|
||||
{
|
||||
if (packet == null) throw new ArgumentNullException(nameof(packet));
|
||||
|
||||
using (var stream = new MemoryStream())
|
||||
using (var writer = new MqttPacketWriter(stream))
|
||||
{
|
||||
var header = new List<byte> { SerializePacket(packet, writer) };
|
||||
var body = stream.ToArray();
|
||||
MqttPacketWriter.BuildLengthHeader(body.Length, header);
|
||||
var headerArray = header.ToArray();
|
||||
var writeBuffer = new byte[header.Count + body.Length];
|
||||
Buffer.BlockCopy(headerArray, 0, writeBuffer, 0, headerArray.Length);
|
||||
Buffer.BlockCopy(body, 0, writeBuffer, headerArray.Length, body.Length);
|
||||
|
||||
return writeBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
private byte SerializePacket(MqttBasePacket packet, MqttPacketWriter writer)
|
||||
{
|
||||
if (packet is MqttConnectPacket connectPacket)
|
||||
{
|
||||
return Serialize(connectPacket, writer);
|
||||
}
|
||||
|
||||
if (packet is MqttConnAckPacket connAckPacket)
|
||||
{
|
||||
return Serialize(connAckPacket, writer);
|
||||
}
|
||||
|
||||
if (packet is MqttDisconnectPacket disconnectPacket)
|
||||
{
|
||||
return Serialize(disconnectPacket, writer);
|
||||
}
|
||||
|
||||
if (packet is MqttPingReqPacket pingReqPacket)
|
||||
{
|
||||
return Serialize(pingReqPacket, writer);
|
||||
}
|
||||
|
||||
if (packet is MqttPingRespPacket pingRespPacket)
|
||||
{
|
||||
return Serialize(pingRespPacket, writer);
|
||||
}
|
||||
|
||||
if (packet is MqttPublishPacket publishPacket)
|
||||
{
|
||||
return Serialize(publishPacket, writer);
|
||||
}
|
||||
|
||||
if (packet is MqttPubAckPacket pubAckPacket)
|
||||
{
|
||||
return Serialize(pubAckPacket, writer);
|
||||
}
|
||||
|
||||
if (packet is MqttPubRecPacket pubRecPacket)
|
||||
{
|
||||
return Serialize(pubRecPacket, writer);
|
||||
}
|
||||
|
||||
if (packet is MqttPubRelPacket pubRelPacket)
|
||||
{
|
||||
return Serialize(pubRelPacket, writer);
|
||||
}
|
||||
|
||||
if (packet is MqttPubCompPacket pubCompPacket)
|
||||
{
|
||||
return Serialize(pubCompPacket, writer);
|
||||
}
|
||||
|
||||
if (packet is MqttSubscribePacket subscribePacket)
|
||||
{
|
||||
return Serialize(subscribePacket, writer);
|
||||
}
|
||||
|
||||
if (packet is MqttSubAckPacket subAckPacket)
|
||||
{
|
||||
return Serialize(subAckPacket, writer);
|
||||
}
|
||||
|
||||
if (packet is MqttUnsubscribePacket unsubscribePacket)
|
||||
{
|
||||
return Serialize(unsubscribePacket, writer);
|
||||
}
|
||||
|
||||
if (packet is MqttUnsubAckPacket unsubAckPacket)
|
||||
{
|
||||
return Serialize(unsubAckPacket, writer);
|
||||
}
|
||||
|
||||
throw new MqttProtocolViolationException("Packet type invalid.");
|
||||
}
|
||||
|
||||
public MqttBasePacket Deserialize(ReceivedMqttPacket receivedMqttPacket)
|
||||
{
|
||||
if (receivedMqttPacket == null) throw new ArgumentNullException(nameof(receivedMqttPacket));
|
||||
|
||||
using (var reader = new MqttPacketReader(receivedMqttPacket))
|
||||
{
|
||||
return Deserialize(receivedMqttPacket.Header, reader);
|
||||
}
|
||||
}
|
||||
|
||||
private static MqttBasePacket Deserialize(MqttPacketHeader header, MqttPacketReader reader)
|
||||
{
|
||||
switch (header.ControlPacketType)
|
||||
{
|
||||
case MqttControlPacketType.Connect:
|
||||
{
|
||||
return DeserializeConnect(reader);
|
||||
}
|
||||
|
||||
case MqttControlPacketType.ConnAck:
|
||||
{
|
||||
return DeserializeConnAck(reader);
|
||||
}
|
||||
|
||||
case MqttControlPacketType.Disconnect:
|
||||
{
|
||||
return new MqttDisconnectPacket();
|
||||
}
|
||||
|
||||
case MqttControlPacketType.Publish:
|
||||
{
|
||||
return DeserializePublish(reader, header);
|
||||
}
|
||||
|
||||
case MqttControlPacketType.PubAck:
|
||||
{
|
||||
return new MqttPubAckPacket
|
||||
{
|
||||
PacketIdentifier = reader.ReadUInt16()
|
||||
};
|
||||
}
|
||||
|
||||
case MqttControlPacketType.PubRec:
|
||||
{
|
||||
return new MqttPubRecPacket
|
||||
{
|
||||
PacketIdentifier = reader.ReadUInt16()
|
||||
};
|
||||
}
|
||||
|
||||
case MqttControlPacketType.PubRel:
|
||||
{
|
||||
return new MqttPubRelPacket
|
||||
{
|
||||
PacketIdentifier = reader.ReadUInt16()
|
||||
};
|
||||
}
|
||||
|
||||
case MqttControlPacketType.PubComp:
|
||||
{
|
||||
return new MqttPubCompPacket
|
||||
{
|
||||
PacketIdentifier = reader.ReadUInt16()
|
||||
};
|
||||
}
|
||||
|
||||
case MqttControlPacketType.PingReq:
|
||||
{
|
||||
return new MqttPingReqPacket();
|
||||
}
|
||||
|
||||
case MqttControlPacketType.PingResp:
|
||||
{
|
||||
return new MqttPingRespPacket();
|
||||
}
|
||||
|
||||
case MqttControlPacketType.Subscribe:
|
||||
{
|
||||
return DeserializeSubscribe(reader);
|
||||
}
|
||||
|
||||
case MqttControlPacketType.SubAck:
|
||||
{
|
||||
return DeserializeSubAck(reader);
|
||||
}
|
||||
|
||||
case MqttControlPacketType.Unsubscibe:
|
||||
{
|
||||
return DeserializeUnsubscribe(reader);
|
||||
}
|
||||
|
||||
case MqttControlPacketType.UnsubAck:
|
||||
{
|
||||
return new MqttUnsubAckPacket
|
||||
{
|
||||
PacketIdentifier = reader.ReadUInt16()
|
||||
};
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
throw new MqttProtocolViolationException(
|
||||
$"Packet type ({(int)header.ControlPacketType}) not supported.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static MqttBasePacket DeserializeUnsubscribe(MqttPacketReader reader)
|
||||
{
|
||||
var packet = new MqttUnsubscribePacket
|
||||
{
|
||||
PacketIdentifier = reader.ReadUInt16(),
|
||||
};
|
||||
|
||||
while (!reader.EndOfRemainingData)
|
||||
{
|
||||
packet.TopicFilters.Add(reader.ReadStringWithLengthPrefix());
|
||||
}
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
private static MqttBasePacket DeserializeSubscribe(MqttPacketReader reader)
|
||||
{
|
||||
var packet = new MqttSubscribePacket
|
||||
{
|
||||
PacketIdentifier = reader.ReadUInt16()
|
||||
};
|
||||
|
||||
while (!reader.EndOfRemainingData)
|
||||
{
|
||||
packet.TopicFilters.Add(new TopicFilter(
|
||||
reader.ReadStringWithLengthPrefix(),
|
||||
(MqttQualityOfServiceLevel)reader.ReadByte()));
|
||||
}
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
private static MqttBasePacket DeserializePublish(MqttPacketReader reader, MqttPacketHeader mqttPacketHeader)
|
||||
{
|
||||
var fixedHeader = new ByteReader(mqttPacketHeader.FixedHeader);
|
||||
var retain = fixedHeader.Read();
|
||||
var qualityOfServiceLevel = (MqttQualityOfServiceLevel)fixedHeader.Read(2);
|
||||
var dup = fixedHeader.Read();
|
||||
|
||||
var topic = reader.ReadStringWithLengthPrefix();
|
||||
|
||||
ushort packetIdentifier = 0;
|
||||
if (qualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce)
|
||||
{
|
||||
packetIdentifier = reader.ReadUInt16();
|
||||
}
|
||||
|
||||
var packet = new MqttPublishPacket
|
||||
{
|
||||
Retain = retain,
|
||||
QualityOfServiceLevel = qualityOfServiceLevel,
|
||||
Dup = dup,
|
||||
Topic = topic,
|
||||
Payload = reader.ReadRemainingData(),
|
||||
PacketIdentifier = packetIdentifier
|
||||
};
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
private static MqttBasePacket DeserializeConnect(MqttPacketReader reader)
|
||||
{
|
||||
reader.ReadBytes(2); // Skip 2 bytes
|
||||
|
||||
MqttProtocolVersion protocolVersion;
|
||||
var protocolName = reader.ReadBytes(4);
|
||||
if (protocolName.SequenceEqual(ProtocolVersionV310Name))
|
||||
{
|
||||
reader.ReadBytes(2);
|
||||
protocolVersion = MqttProtocolVersion.V310;
|
||||
}
|
||||
else if (protocolName.SequenceEqual(ProtocolVersionV311Name))
|
||||
{
|
||||
protocolVersion = MqttProtocolVersion.V311;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new MqttProtocolViolationException("Protocol name is not supported.");
|
||||
}
|
||||
|
||||
var protocolLevel = reader.ReadByte();
|
||||
var connectFlags = reader.ReadByte();
|
||||
|
||||
var connectFlagsReader = new ByteReader(connectFlags);
|
||||
connectFlagsReader.Read(); // Reserved.
|
||||
|
||||
var packet = new MqttConnectPacket
|
||||
{
|
||||
ProtocolVersion = protocolVersion,
|
||||
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 = reader.ReadUInt16();
|
||||
packet.ClientId = reader.ReadStringWithLengthPrefix();
|
||||
|
||||
if (willFlag)
|
||||
{
|
||||
packet.WillMessage = new MqttApplicationMessage(
|
||||
reader.ReadStringWithLengthPrefix(),
|
||||
reader.ReadWithLengthPrefix(),
|
||||
(MqttQualityOfServiceLevel)willQoS,
|
||||
willRetain);
|
||||
}
|
||||
|
||||
if (usernameFlag)
|
||||
{
|
||||
packet.Username = reader.ReadStringWithLengthPrefix();
|
||||
}
|
||||
|
||||
if (passwordFlag)
|
||||
{
|
||||
packet.Password = reader.ReadStringWithLengthPrefix();
|
||||
}
|
||||
|
||||
ValidateConnectPacket(packet);
|
||||
return packet;
|
||||
}
|
||||
|
||||
private static MqttBasePacket DeserializeSubAck(MqttPacketReader reader)
|
||||
{
|
||||
var packet = new MqttSubAckPacket
|
||||
{
|
||||
PacketIdentifier = reader.ReadUInt16()
|
||||
};
|
||||
|
||||
while (!reader.EndOfRemainingData)
|
||||
{
|
||||
packet.SubscribeReturnCodes.Add((MqttSubscribeReturnCode)reader.ReadByte());
|
||||
}
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
private static MqttBasePacket DeserializeConnAck(MqttPacketReader reader)
|
||||
{
|
||||
var variableHeader1 = reader.ReadByte();
|
||||
var variableHeader2 = reader.ReadByte();
|
||||
|
||||
var packet = new MqttConnAckPacket
|
||||
{
|
||||
IsSessionPresent = new ByteReader(variableHeader1).Read(),
|
||||
ConnectReturnCode = (MqttConnectReturnCode)variableHeader2
|
||||
};
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
private static void ValidateConnectPacket(MqttConnectPacket packet)
|
||||
{
|
||||
if (string.IsNullOrEmpty(packet.ClientId) && !packet.CleanSession)
|
||||
{
|
||||
throw new MqttProtocolViolationException("CleanSession must be set if ClientId is empty [MQTT-3.1.3-7].");
|
||||
}
|
||||
}
|
||||
|
||||
private static void ValidatePublishPacket(MqttPublishPacket packet)
|
||||
{
|
||||
if (packet.QualityOfServiceLevel == 0 && packet.Dup)
|
||||
{
|
||||
throw new MqttProtocolViolationException("Dup flag must be false for QoS 0 packets [MQTT-3.3.1-2].");
|
||||
}
|
||||
}
|
||||
|
||||
private byte Serialize(MqttConnectPacket packet, MqttPacketWriter writer)
|
||||
{
|
||||
ValidateConnectPacket(packet);
|
||||
|
||||
// Write variable header
|
||||
writer.Write(0x00, 0x04); // 3.1.2.1 Protocol Name
|
||||
if (ProtocolVersion == MqttProtocolVersion.V311)
|
||||
{
|
||||
writer.Write(ProtocolVersionV311Name);
|
||||
writer.Write(0x04); // 3.1.2.2 Protocol Level (4)
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.Write(ProtocolVersionV310Name);
|
||||
writer.Write(0x64, 0x70, 0x03); // Protocol Level (0x03)
|
||||
}
|
||||
|
||||
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((int)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);
|
||||
|
||||
writer.Write(connectFlags);
|
||||
writer.Write(packet.KeepAlivePeriod);
|
||||
writer.WriteWithLengthPrefix(packet.ClientId);
|
||||
|
||||
if (packet.WillMessage != null)
|
||||
{
|
||||
writer.WriteWithLengthPrefix(packet.WillMessage.Topic);
|
||||
writer.WriteWithLengthPrefix(packet.WillMessage.Payload);
|
||||
}
|
||||
|
||||
if (packet.Username != null)
|
||||
{
|
||||
writer.WriteWithLengthPrefix(packet.Username);
|
||||
}
|
||||
|
||||
if (packet.Password != null)
|
||||
{
|
||||
writer.WriteWithLengthPrefix(packet.Password);
|
||||
}
|
||||
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Connect);
|
||||
}
|
||||
|
||||
private byte Serialize(MqttConnAckPacket packet, MqttPacketWriter writer)
|
||||
{
|
||||
var connectAcknowledgeFlags = new ByteWriter();
|
||||
|
||||
if (ProtocolVersion == MqttProtocolVersion.V311)
|
||||
{
|
||||
connectAcknowledgeFlags.Write(packet.IsSessionPresent);
|
||||
}
|
||||
|
||||
writer.Write(connectAcknowledgeFlags);
|
||||
writer.Write((byte)packet.ConnectReturnCode);
|
||||
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.ConnAck);
|
||||
}
|
||||
|
||||
private static byte Serialize(MqttPubRelPacket packet, MqttPacketWriter writer)
|
||||
{
|
||||
writer.Write(packet.PacketIdentifier);
|
||||
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubRel, 0x02);
|
||||
}
|
||||
|
||||
private static byte Serialize(MqttDisconnectPacket packet, MqttPacketWriter writer)
|
||||
{
|
||||
return SerializeEmptyPacketAsync(MqttControlPacketType.Disconnect, writer);
|
||||
}
|
||||
|
||||
private static byte Serialize(MqttPingReqPacket packet, MqttPacketWriter writer)
|
||||
{
|
||||
return SerializeEmptyPacketAsync(MqttControlPacketType.PingReq, writer);
|
||||
}
|
||||
|
||||
private static byte Serialize(MqttPingRespPacket packet, MqttPacketWriter writer)
|
||||
{
|
||||
return SerializeEmptyPacketAsync(MqttControlPacketType.PingResp, writer);
|
||||
}
|
||||
|
||||
private static byte Serialize(MqttPublishPacket packet, MqttPacketWriter writer)
|
||||
{
|
||||
ValidatePublishPacket(packet);
|
||||
|
||||
writer.WriteWithLengthPrefix(packet.Topic);
|
||||
|
||||
if (packet.QualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce)
|
||||
{
|
||||
writer.Write(packet.PacketIdentifier);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (packet.PacketIdentifier > 0)
|
||||
{
|
||||
throw new MqttProtocolViolationException("Packet identifier must be empty if QoS == 0 [MQTT-2.3.1-5].");
|
||||
}
|
||||
}
|
||||
|
||||
if (packet.Payload?.Length > 0)
|
||||
{
|
||||
writer.Write(packet.Payload);
|
||||
}
|
||||
|
||||
byte fixedHeader = 0;
|
||||
|
||||
if (packet.Retain)
|
||||
{
|
||||
fixedHeader |= 0x01;
|
||||
}
|
||||
|
||||
fixedHeader |= (byte)((byte)packet.QualityOfServiceLevel << 1);
|
||||
|
||||
if (packet.Dup)
|
||||
{
|
||||
fixedHeader |= 0x08;
|
||||
}
|
||||
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Publish, fixedHeader);
|
||||
}
|
||||
|
||||
private static byte Serialize(MqttPubAckPacket packet, MqttPacketWriter writer)
|
||||
{
|
||||
writer.Write(packet.PacketIdentifier);
|
||||
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubAck);
|
||||
}
|
||||
|
||||
private static byte Serialize(MqttPubRecPacket packet, MqttPacketWriter writer)
|
||||
{
|
||||
writer.Write(packet.PacketIdentifier);
|
||||
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubRec);
|
||||
}
|
||||
|
||||
private static byte Serialize(MqttPubCompPacket packet, MqttPacketWriter writer)
|
||||
{
|
||||
writer.Write(packet.PacketIdentifier);
|
||||
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.PubComp);
|
||||
}
|
||||
|
||||
private static byte Serialize(MqttSubscribePacket packet, MqttPacketWriter writer)
|
||||
{
|
||||
if (!packet.TopicFilters.Any()) throw new MqttProtocolViolationException("At least one topic filter must be set [MQTT-3.8.3-3].");
|
||||
|
||||
writer.Write(packet.PacketIdentifier);
|
||||
|
||||
if (packet.TopicFilters?.Count > 0)
|
||||
{
|
||||
foreach (var topicFilter in packet.TopicFilters)
|
||||
{
|
||||
writer.WriteWithLengthPrefix(topicFilter.Topic);
|
||||
writer.Write((byte)topicFilter.QualityOfServiceLevel);
|
||||
}
|
||||
}
|
||||
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Subscribe, 0x02);
|
||||
}
|
||||
|
||||
private static byte Serialize(MqttSubAckPacket packet, MqttPacketWriter writer)
|
||||
{
|
||||
writer.Write(packet.PacketIdentifier);
|
||||
|
||||
if (packet.SubscribeReturnCodes?.Any() == true)
|
||||
{
|
||||
foreach (var packetSubscribeReturnCode in packet.SubscribeReturnCodes)
|
||||
{
|
||||
writer.Write((byte)packetSubscribeReturnCode);
|
||||
}
|
||||
}
|
||||
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.SubAck);
|
||||
}
|
||||
|
||||
private static byte Serialize(MqttUnsubscribePacket packet, MqttPacketWriter writer)
|
||||
{
|
||||
if (!packet.TopicFilters.Any()) throw new MqttProtocolViolationException("At least one topic filter must be set [MQTT-3.10.3-2].");
|
||||
|
||||
writer.Write(packet.PacketIdentifier);
|
||||
|
||||
if (packet.TopicFilters?.Any() == true)
|
||||
{
|
||||
foreach (var topicFilter in packet.TopicFilters)
|
||||
{
|
||||
writer.WriteWithLengthPrefix(topicFilter);
|
||||
}
|
||||
}
|
||||
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.Unsubscibe, 0x02);
|
||||
}
|
||||
|
||||
private static byte Serialize(MqttUnsubAckPacket packet, MqttPacketWriter writer)
|
||||
{
|
||||
writer.Write(packet.PacketIdentifier);
|
||||
|
||||
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.UnsubAck);
|
||||
}
|
||||
|
||||
private static byte SerializeEmptyPacketAsync(MqttControlPacketType type, MqttPacketWriter writer)
|
||||
{
|
||||
return MqttPacketWriter.BuildFixedHeader(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using MQTTnet.Core.Protocol;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MQTTnet.Core.Serializer
|
||||
{
|
||||
public sealed class MqttPacketWriter : BinaryWriter
|
||||
{
|
||||
public MqttPacketWriter(Stream stream)
|
||||
: base(stream)
|
||||
{
|
||||
}
|
||||
|
||||
public static byte BuildFixedHeader(MqttControlPacketType packetType, byte flags = 0)
|
||||
{
|
||||
var fixedHeader = (int)packetType << 4;
|
||||
fixedHeader |= flags;
|
||||
return (byte)fixedHeader;
|
||||
}
|
||||
|
||||
public override void Write(ushort value)
|
||||
{
|
||||
var buffer = BitConverter.GetBytes(value);
|
||||
Write(buffer[1], buffer[0]);
|
||||
}
|
||||
|
||||
public new void Write(params byte[] values)
|
||||
{
|
||||
base.Write(values);
|
||||
}
|
||||
|
||||
public new void Write(byte value)
|
||||
{
|
||||
base.Write(value);
|
||||
}
|
||||
|
||||
public void Write(ByteWriter value)
|
||||
{
|
||||
if (value == null) throw new ArgumentNullException(nameof(value));
|
||||
|
||||
Write(value.Value);
|
||||
}
|
||||
|
||||
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 static void BuildLengthHeader(int length, List<byte> header)
|
||||
{
|
||||
if (length == 0)
|
||||
{
|
||||
header.Add(0);
|
||||
return;
|
||||
}
|
||||
|
||||
// Alorithm taken from http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html.
|
||||
var x = length;
|
||||
do
|
||||
{
|
||||
var encodedByte = x % 128;
|
||||
x = x / 128;
|
||||
if (x > 0)
|
||||
{
|
||||
encodedByte = encodedByte | 128;
|
||||
}
|
||||
|
||||
header.Add((byte)encodedByte);
|
||||
} while (x > 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
using MQTTnet.Core.Serializer;
|
||||
|
||||
namespace MQTTnet.Core.Server
|
||||
{
|
||||
public class ConnectedMqttClient
|
||||
{
|
||||
public string ClientId { get; set; }
|
||||
|
||||
public MqttProtocolVersion ProtocolVersion { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using MQTTnet.Core.Adapter;
|
||||
|
||||
namespace MQTTnet.Core.Server
|
||||
{
|
||||
public interface IMqttServer
|
||||
{
|
||||
event EventHandler<MqttApplicationMessageReceivedEventArgs> ApplicationMessageReceived;
|
||||
event EventHandler<MqttClientConnectedEventArgs> ClientConnected;
|
||||
|
||||
IList<ConnectedMqttClient> GetConnectedClients();
|
||||
void InjectClient(string identifier, IMqttCommunicationAdapter adapter);
|
||||
void Publish(MqttApplicationMessage applicationMessage);
|
||||
void Start();
|
||||
void Stop();
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
namespace MQTTnet.Core.Server
|
||||
{
|
||||
public interface IMqttServerFactory
|
||||
{
|
||||
IMqttServer CreateMqttServer(MqttServerOptions options);
|
||||
}
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Core.Adapter;
|
||||
using MQTTnet.Core.Diagnostics;
|
||||
using MQTTnet.Core.Exceptions;
|
||||
using MQTTnet.Core.Packets;
|
||||
using System.Linq;
|
||||
|
||||
namespace MQTTnet.Core.Server
|
||||
{
|
||||
public sealed class MqttClientMessageQueue
|
||||
{
|
||||
private readonly BlockingCollection<MqttPublishPacket> _pendingPublishPackets = new BlockingCollection<MqttPublishPacket>();
|
||||
|
||||
private readonly MqttServerOptions _options;
|
||||
private CancellationTokenSource _cancellationTokenSource;
|
||||
|
||||
public MqttClientMessageQueue(MqttServerOptions options)
|
||||
{
|
||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
||||
}
|
||||
|
||||
public void Start(IMqttCommunicationAdapter adapter)
|
||||
{
|
||||
if (_cancellationTokenSource != null)
|
||||
{
|
||||
throw new InvalidOperationException($"{nameof(MqttClientMessageQueue)} already started.");
|
||||
}
|
||||
|
||||
if (adapter == null) throw new ArgumentNullException(nameof(adapter));
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
Task.Run(() => SendPendingPublishPacketsAsync(_cancellationTokenSource.Token, adapter), _cancellationTokenSource.Token);
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
_cancellationTokenSource?.Cancel();
|
||||
_cancellationTokenSource = null;
|
||||
_pendingPublishPackets?.Dispose();
|
||||
}
|
||||
|
||||
public void Enqueue(MqttPublishPacket publishPacket)
|
||||
{
|
||||
if (publishPacket == null) throw new ArgumentNullException(nameof(publishPacket));
|
||||
|
||||
_pendingPublishPackets.Add(publishPacket);
|
||||
}
|
||||
|
||||
private async Task SendPendingPublishPacketsAsync(CancellationToken cancellationToken, IMqttCommunicationAdapter adapter)
|
||||
{
|
||||
var consumable = _pendingPublishPackets.GetConsumingEnumerable();
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
if (_pendingPublishPackets.Count == 0)
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromMilliseconds(5), cancellationToken).ConfigureAwait(false);
|
||||
continue;
|
||||
}
|
||||
|
||||
var packets = consumable.Take(_pendingPublishPackets.Count).ToList();
|
||||
try
|
||||
{
|
||||
await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, packets).ConfigureAwait(false);
|
||||
}
|
||||
catch (MqttCommunicationException exception)
|
||||
{
|
||||
MqttTrace.Warning(nameof(MqttClientMessageQueue), exception, "Sending publish packet failed.");
|
||||
foreach (var publishPacket in packets)
|
||||
{
|
||||
publishPacket.Dup = true;
|
||||
_pendingPublishPackets.Add(publishPacket, cancellationToken);
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
MqttTrace.Error(nameof(MqttClientMessageQueue), exception, "Sending publish packet failed.");
|
||||
foreach (var publishPacket in packets)
|
||||
{
|
||||
publishPacket.Dup = true;
|
||||
_pendingPublishPackets.Add(publishPacket, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,192 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Core.Adapter;
|
||||
using MQTTnet.Core.Diagnostics;
|
||||
using MQTTnet.Core.Exceptions;
|
||||
using MQTTnet.Core.Internal;
|
||||
using MQTTnet.Core.Packets;
|
||||
using MQTTnet.Core.Protocol;
|
||||
|
||||
namespace MQTTnet.Core.Server
|
||||
{
|
||||
public sealed class MqttClientSession : IDisposable
|
||||
{
|
||||
private readonly HashSet<ushort> _unacknowledgedPublishPackets = new HashSet<ushort>();
|
||||
|
||||
private readonly MqttClientSubscriptionsManager _subscriptionsManager = new MqttClientSubscriptionsManager();
|
||||
private readonly MqttClientMessageQueue _messageQueue;
|
||||
private readonly Action<MqttClientSession, MqttPublishPacket> _publishPacketReceivedCallback;
|
||||
private readonly MqttServerOptions _options;
|
||||
|
||||
private CancellationTokenSource _cancellationTokenSource;
|
||||
private string _identifier;
|
||||
private MqttApplicationMessage _willApplicationMessage;
|
||||
|
||||
public MqttClientSession(string clientId, MqttServerOptions options, Action<MqttClientSession, MqttPublishPacket> publishPacketReceivedCallback)
|
||||
{
|
||||
ClientId = clientId;
|
||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
||||
_publishPacketReceivedCallback = publishPacketReceivedCallback ?? throw new ArgumentNullException(nameof(publishPacketReceivedCallback));
|
||||
|
||||
_messageQueue = new MqttClientMessageQueue(options);
|
||||
}
|
||||
|
||||
public string ClientId { get; }
|
||||
|
||||
public bool IsConnected => Adapter != null;
|
||||
|
||||
public IMqttCommunicationAdapter Adapter { get; private set; }
|
||||
|
||||
public async Task RunAsync(string identifier, MqttApplicationMessage willApplicationMessage, IMqttCommunicationAdapter adapter)
|
||||
{
|
||||
if (adapter == null) throw new ArgumentNullException(nameof(adapter));
|
||||
|
||||
_willApplicationMessage = willApplicationMessage;
|
||||
|
||||
try
|
||||
{
|
||||
_identifier = identifier;
|
||||
Adapter = adapter;
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
_messageQueue.Start(adapter);
|
||||
while (!_cancellationTokenSource.IsCancellationRequested)
|
||||
{
|
||||
var packet = await adapter.ReceivePacketAsync(TimeSpan.Zero, _cancellationTokenSource.Token).ConfigureAwait(false);
|
||||
await HandleIncomingPacketAsync(packet).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (MqttCommunicationException)
|
||||
{
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
MqttTrace.Error(nameof(MqttClientSession), exception, "Client '{0}': Unhandled exception while processing client packets.", _identifier);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (willApplicationMessage != null)
|
||||
{
|
||||
_publishPacketReceivedCallback(this, _willApplicationMessage.ToPublishPacket());
|
||||
}
|
||||
|
||||
_messageQueue.Stop();
|
||||
_cancellationTokenSource.Cancel();
|
||||
Adapter = null;
|
||||
|
||||
MqttTrace.Information(nameof(MqttClientSession), "Client '{0}': Disconnected.", _identifier);
|
||||
}
|
||||
}
|
||||
|
||||
public void EnqueuePublishPacket(MqttPublishPacket publishPacket)
|
||||
{
|
||||
if (publishPacket == null) throw new ArgumentNullException(nameof(publishPacket));
|
||||
|
||||
if (!_subscriptionsManager.IsSubscribed(publishPacket))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_messageQueue.Enqueue(publishPacket);
|
||||
MqttTrace.Verbose(nameof(MqttClientSession), "Client '{0}': Enqueued pending publish packet.", _identifier);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_cancellationTokenSource?.Cancel();
|
||||
_cancellationTokenSource?.Dispose();
|
||||
}
|
||||
|
||||
private Task HandleIncomingPacketAsync(MqttBasePacket packet)
|
||||
{
|
||||
if (packet is MqttSubscribePacket subscribePacket)
|
||||
{
|
||||
return Adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, _cancellationTokenSource.Token, _subscriptionsManager.Subscribe(subscribePacket));
|
||||
}
|
||||
|
||||
if (packet is MqttUnsubscribePacket unsubscribePacket)
|
||||
{
|
||||
return Adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, _cancellationTokenSource.Token, _subscriptionsManager.Unsubscribe(unsubscribePacket));
|
||||
}
|
||||
|
||||
if (packet is MqttPublishPacket publishPacket)
|
||||
{
|
||||
return HandleIncomingPublishPacketAsync(publishPacket);
|
||||
}
|
||||
|
||||
if (packet is MqttPubRelPacket pubRelPacket)
|
||||
{
|
||||
return HandleIncomingPubRelPacketAsync(pubRelPacket);
|
||||
}
|
||||
|
||||
if (packet is MqttPubRecPacket pubRecPacket)
|
||||
{
|
||||
return Adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, _cancellationTokenSource.Token, pubRecPacket.CreateResponse<MqttPubRelPacket>());
|
||||
}
|
||||
|
||||
if (packet is MqttPubAckPacket || packet is MqttPubCompPacket)
|
||||
{
|
||||
// Discard message.
|
||||
return Task.FromResult((object)null);
|
||||
}
|
||||
|
||||
if (packet is MqttPingReqPacket)
|
||||
{
|
||||
return Adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, _cancellationTokenSource.Token, new MqttPingRespPacket());
|
||||
}
|
||||
|
||||
if (packet is MqttDisconnectPacket || packet is MqttConnectPacket)
|
||||
{
|
||||
_cancellationTokenSource.Cancel();
|
||||
return Task.FromResult((object)null);
|
||||
}
|
||||
|
||||
MqttTrace.Warning(nameof(MqttClientSession), "Client '{0}': Received not supported packet ({1}). Closing connection.", _identifier, packet);
|
||||
_cancellationTokenSource.Cancel();
|
||||
|
||||
return Task.FromResult((object)null);
|
||||
}
|
||||
|
||||
private Task HandleIncomingPublishPacketAsync(MqttPublishPacket publishPacket)
|
||||
{
|
||||
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce)
|
||||
{
|
||||
_publishPacketReceivedCallback(this, publishPacket);
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtLeastOnce)
|
||||
{
|
||||
_publishPacketReceivedCallback(this, publishPacket);
|
||||
return Adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, _cancellationTokenSource.Token, new MqttPubAckPacket { PacketIdentifier = publishPacket.PacketIdentifier });
|
||||
}
|
||||
|
||||
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.ExactlyOnce)
|
||||
{
|
||||
// QoS 2 is implement as method "B" [4.3.3 QoS 2: Exactly once delivery]
|
||||
lock (_unacknowledgedPublishPackets)
|
||||
{
|
||||
_unacknowledgedPublishPackets.Add(publishPacket.PacketIdentifier);
|
||||
}
|
||||
|
||||
_publishPacketReceivedCallback(this, publishPacket);
|
||||
|
||||
return Adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, _cancellationTokenSource.Token, new MqttPubRecPacket { PacketIdentifier = publishPacket.PacketIdentifier });
|
||||
}
|
||||
|
||||
throw new MqttCommunicationException("Received a not supported QoS level.");
|
||||
}
|
||||
|
||||
private Task HandleIncomingPubRelPacketAsync(MqttPubRelPacket pubRelPacket)
|
||||
{
|
||||
lock (_unacknowledgedPublishPackets)
|
||||
{
|
||||
_unacknowledgedPublishPackets.Remove(pubRelPacket.PacketIdentifier);
|
||||
}
|
||||
|
||||
return Adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, _cancellationTokenSource.Token, new MqttPubCompPacket { PacketIdentifier = pubRelPacket.PacketIdentifier });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
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.Internal;
|
||||
using MQTTnet.Core.Packets;
|
||||
using MQTTnet.Core.Protocol;
|
||||
|
||||
namespace MQTTnet.Core.Server
|
||||
{
|
||||
public sealed class MqttClientSessionsManager
|
||||
{
|
||||
private readonly object _syncRoot = new object();
|
||||
private readonly Dictionary<string, MqttClientSession> _clientSessions = new Dictionary<string, MqttClientSession>();
|
||||
private readonly MqttServerOptions _options;
|
||||
|
||||
public MqttClientSessionsManager(MqttServerOptions options)
|
||||
{
|
||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
||||
}
|
||||
|
||||
public event EventHandler<MqttApplicationMessageReceivedEventArgs> ApplicationMessageReceived;
|
||||
|
||||
public async Task RunClientSessionAsync(MqttClientConnectedEventArgs eventArgs)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!(await eventArgs.ClientAdapter.ReceivePacketAsync(_options.DefaultCommunicationTimeout, CancellationToken.None).ConfigureAwait(false) is MqttConnectPacket connectPacket))
|
||||
{
|
||||
throw new MqttProtocolViolationException("The first packet from a client must be a 'CONNECT' packet [MQTT-3.1.0-1].");
|
||||
}
|
||||
|
||||
// Switch to the required protocol version before sending any response.
|
||||
eventArgs.ClientAdapter.PacketSerializer.ProtocolVersion = connectPacket.ProtocolVersion;
|
||||
|
||||
var connectReturnCode = ValidateConnection(connectPacket);
|
||||
if (connectReturnCode != MqttConnectReturnCode.ConnectionAccepted)
|
||||
{
|
||||
await eventArgs.ClientAdapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, CancellationToken.None, new MqttConnAckPacket
|
||||
{
|
||||
ConnectReturnCode = connectReturnCode
|
||||
}).ConfigureAwait(false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var clientSession = GetOrCreateClientSession(connectPacket);
|
||||
|
||||
await eventArgs.ClientAdapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, CancellationToken.None, new MqttConnAckPacket
|
||||
{
|
||||
ConnectReturnCode = connectReturnCode,
|
||||
IsSessionPresent = clientSession.IsExistingSession
|
||||
}).ConfigureAwait(false);
|
||||
|
||||
await clientSession.Session.RunAsync(eventArgs.Identifier, connectPacket.WillMessage, eventArgs.ClientAdapter).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
MqttTrace.Error(nameof(MqttServer), exception, exception.Message);
|
||||
}
|
||||
finally
|
||||
{
|
||||
await eventArgs.ClientAdapter.DisconnectAsync(_options.DefaultCommunicationTimeout).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
lock (_syncRoot)
|
||||
{
|
||||
_clientSessions.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public IList<ConnectedMqttClient> GetConnectedClients()
|
||||
{
|
||||
lock (_syncRoot)
|
||||
{
|
||||
return _clientSessions.Where(s => s.Value.IsConnected).Select(s => new ConnectedMqttClient
|
||||
{
|
||||
ClientId = s.Value.ClientId,
|
||||
ProtocolVersion = s.Value.Adapter.PacketSerializer.ProtocolVersion
|
||||
}).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
private MqttConnectReturnCode ValidateConnection(MqttConnectPacket connectPacket)
|
||||
{
|
||||
if (_options.ConnectionValidator != null)
|
||||
{
|
||||
return _options.ConnectionValidator(connectPacket);
|
||||
}
|
||||
|
||||
return MqttConnectReturnCode.ConnectionAccepted;
|
||||
}
|
||||
|
||||
private GetOrCreateClientSessionResult GetOrCreateClientSession(MqttConnectPacket connectPacket)
|
||||
{
|
||||
lock (_syncRoot)
|
||||
{
|
||||
MqttClientSession clientSession;
|
||||
var isSessionPresent = _clientSessions.TryGetValue(connectPacket.ClientId, out clientSession);
|
||||
if (isSessionPresent)
|
||||
{
|
||||
if (connectPacket.CleanSession)
|
||||
{
|
||||
_clientSessions.Remove(connectPacket.ClientId);
|
||||
clientSession.Dispose();
|
||||
clientSession = null;
|
||||
MqttTrace.Verbose(nameof(MqttClientSessionsManager), "Disposed existing session of client '{0}'.", connectPacket.ClientId);
|
||||
}
|
||||
else
|
||||
{
|
||||
MqttTrace.Verbose(nameof(MqttClientSessionsManager), "Reusing existing session of client '{0}'.", connectPacket.ClientId);
|
||||
}
|
||||
}
|
||||
|
||||
var isExistingSession = true;
|
||||
if (clientSession == null)
|
||||
{
|
||||
isExistingSession = false;
|
||||
|
||||
clientSession = new MqttClientSession(connectPacket.ClientId, _options, DispatchPublishPacket);
|
||||
_clientSessions[connectPacket.ClientId] = clientSession;
|
||||
|
||||
MqttTrace.Verbose(nameof(MqttClientSessionsManager), "Created a new session for client '{0}'.", connectPacket.ClientId);
|
||||
}
|
||||
|
||||
return new GetOrCreateClientSessionResult { IsExistingSession = isExistingSession, Session = clientSession };
|
||||
}
|
||||
}
|
||||
|
||||
public void DispatchPublishPacket(MqttClientSession senderClientSession, MqttPublishPacket publishPacket)
|
||||
{
|
||||
try
|
||||
{
|
||||
var eventArgs = new MqttApplicationMessageReceivedEventArgs(senderClientSession?.ClientId, publishPacket.ToApplicationMessage());
|
||||
ApplicationMessageReceived?.Invoke(this, eventArgs);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
MqttTrace.Error(nameof(MqttClientSessionsManager), exception, "Error while processing application message");
|
||||
}
|
||||
|
||||
lock (_syncRoot)
|
||||
{
|
||||
foreach (var clientSession in _clientSessions.Values.ToList())
|
||||
{
|
||||
clientSession.EnqueuePublishPacket(publishPacket);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using MQTTnet.Core.Packets;
|
||||
using MQTTnet.Core.Protocol;
|
||||
|
||||
namespace MQTTnet.Core.Server
|
||||
{
|
||||
public sealed class MqttClientSubscriptionsManager
|
||||
{
|
||||
private readonly Dictionary<string, MqttQualityOfServiceLevel> _subscribedTopics = new Dictionary<string, MqttQualityOfServiceLevel>();
|
||||
|
||||
public MqttSubAckPacket Subscribe(MqttSubscribePacket subscribePacket)
|
||||
{
|
||||
if (subscribePacket == null) throw new ArgumentNullException(nameof(subscribePacket));
|
||||
|
||||
var responsePacket = subscribePacket.CreateResponse<MqttSubAckPacket>();
|
||||
|
||||
lock (_subscribedTopics)
|
||||
{
|
||||
foreach (var topicFilter in subscribePacket.TopicFilters)
|
||||
{
|
||||
_subscribedTopics[topicFilter.Topic] = topicFilter.QualityOfServiceLevel;
|
||||
responsePacket.SubscribeReturnCodes.Add(MqttSubscribeReturnCode.SuccessMaximumQoS1); // TODO: Add support for QoS 2.
|
||||
}
|
||||
}
|
||||
|
||||
return responsePacket;
|
||||
}
|
||||
|
||||
public MqttUnsubAckPacket Unsubscribe(MqttUnsubscribePacket unsubscribePacket)
|
||||
{
|
||||
if (unsubscribePacket == null) throw new ArgumentNullException(nameof(unsubscribePacket));
|
||||
|
||||
lock (_subscribedTopics)
|
||||
{
|
||||
foreach (var topicFilter in unsubscribePacket.TopicFilters)
|
||||
{
|
||||
_subscribedTopics.Remove(topicFilter);
|
||||
}
|
||||
}
|
||||
|
||||
return unsubscribePacket.CreateResponse<MqttUnsubAckPacket>();
|
||||
}
|
||||
|
||||
public bool IsSubscribed(MqttPublishPacket publishPacket)
|
||||
{
|
||||
if (publishPacket == null) throw new ArgumentNullException(nameof(publishPacket));
|
||||
|
||||
lock (_subscribedTopics)
|
||||
{
|
||||
foreach (var subscribedTopic in _subscribedTopics)
|
||||
{
|
||||
if (publishPacket.QualityOfServiceLevel > subscribedTopic.Value)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!MqttTopicFilterComparer.IsMatch(publishPacket.Topic, subscribedTopic.Key))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MQTTnet.Core.Adapter;
|
||||
using MQTTnet.Core.Diagnostics;
|
||||
using MQTTnet.Core.Internal;
|
||||
|
||||
namespace MQTTnet.Core.Server
|
||||
{
|
||||
public sealed class MqttServer : IMqttServer
|
||||
{
|
||||
private readonly MqttClientSessionsManager _clientSessionsManager;
|
||||
private readonly ICollection<IMqttServerAdapter> _adapters;
|
||||
private readonly MqttServerOptions _options;
|
||||
|
||||
private CancellationTokenSource _cancellationTokenSource;
|
||||
|
||||
public MqttServer(MqttServerOptions options, ICollection<IMqttServerAdapter> adapters)
|
||||
{
|
||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
||||
_adapters = adapters ?? throw new ArgumentNullException(nameof(adapters));
|
||||
|
||||
_clientSessionsManager = new MqttClientSessionsManager(options);
|
||||
_clientSessionsManager.ApplicationMessageReceived += (s, e) => ApplicationMessageReceived?.Invoke(s, e);
|
||||
}
|
||||
|
||||
public IList<ConnectedMqttClient> GetConnectedClients()
|
||||
{
|
||||
return _clientSessionsManager.GetConnectedClients();
|
||||
}
|
||||
|
||||
public event EventHandler<MqttClientConnectedEventArgs> ClientConnected;
|
||||
|
||||
public event EventHandler<MqttApplicationMessageReceivedEventArgs> ApplicationMessageReceived;
|
||||
|
||||
public void Publish(MqttApplicationMessage applicationMessage)
|
||||
{
|
||||
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
|
||||
|
||||
_clientSessionsManager.DispatchPublishPacket(null, applicationMessage.ToPublishPacket());
|
||||
}
|
||||
|
||||
public void InjectClient(string identifier, IMqttCommunicationAdapter adapter)
|
||||
{
|
||||
if (adapter == null) throw new ArgumentNullException(nameof(adapter));
|
||||
|
||||
if (_cancellationTokenSource == null) throw new InvalidOperationException("The MQTT server is not started.");
|
||||
|
||||
OnClientConnected(this, new MqttClientConnectedEventArgs(identifier, adapter));
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
if (_cancellationTokenSource != null) throw new InvalidOperationException("The MQTT server is already started.");
|
||||
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
foreach (var adapter in _adapters)
|
||||
{
|
||||
adapter.ClientConnected += OnClientConnected;
|
||||
adapter.Start(_options);
|
||||
}
|
||||
|
||||
MqttTrace.Information(nameof(MqttServer), "Started.");
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
_cancellationTokenSource?.Cancel(false);
|
||||
_cancellationTokenSource?.Dispose();
|
||||
_cancellationTokenSource = null;
|
||||
|
||||
foreach (var adapter in _adapters)
|
||||
{
|
||||
adapter.ClientConnected -= OnClientConnected;
|
||||
adapter.Stop();
|
||||
}
|
||||
|
||||
_clientSessionsManager.Clear();
|
||||
|
||||
MqttTrace.Information(nameof(MqttServer), "Stopped.");
|
||||
}
|
||||
|
||||
private void OnClientConnected(object sender, MqttClientConnectedEventArgs eventArgs)
|
||||
{
|
||||
MqttTrace.Information(nameof(MqttServer), "Client '{0}': Connected.", eventArgs.Identifier);
|
||||
ClientConnected?.Invoke(this, eventArgs);
|
||||
|
||||
Task.Run(() => _clientSessionsManager.RunClientSessionAsync(eventArgs), _cancellationTokenSource.Token);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
namespace MQTTnet.Core.Server
|
||||
{
|
||||
public sealed class MqttServerDefaultEndpointOptions
|
||||
{
|
||||
public bool IsEnabled { get; set; } = true;
|
||||
|
||||
public int? Port { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
using System;
|
||||
using MQTTnet.Core.Packets;
|
||||
using MQTTnet.Core.Protocol;
|
||||
|
||||
namespace MQTTnet.Core.Server
|
||||
{
|
||||
public sealed class MqttServerOptions
|
||||
{
|
||||
public MqttServerDefaultEndpointOptions DefaultEndpointOptions { get; } = new MqttServerDefaultEndpointOptions();
|
||||
|
||||
public MqttServerTlsEndpointOptions TlsEndpointOptions { get; } = new MqttServerTlsEndpointOptions();
|
||||
|
||||
public int ConnectionBacklog { get; set; } = 10;
|
||||
|
||||
public TimeSpan DefaultCommunicationTimeout { get; set; } = TimeSpan.FromSeconds(15);
|
||||
|
||||
public Func<MqttConnectPacket, MqttConnectReturnCode> ConnectionValidator { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace MQTTnet.Core.Server
|
||||
{
|
||||
public static class MqttServerOptionsExtensions
|
||||
{
|
||||
public static int GetTlsEndpointPort(this MqttServerOptions options)
|
||||
{
|
||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||
|
||||
if (!options.TlsEndpointOptions.Port.HasValue)
|
||||
{
|
||||
return 8883;
|
||||
}
|
||||
|
||||
return options.TlsEndpointOptions.Port.Value;
|
||||
}
|
||||
|
||||
public static int GetDefaultEndpointPort(this MqttServerOptions options)
|
||||
{
|
||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||
|
||||
if (!options.DefaultEndpointOptions.Port.HasValue)
|
||||
{
|
||||
return 1883;
|
||||
}
|
||||
|
||||
return options.DefaultEndpointOptions.Port.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
namespace MQTTnet.Core.Server
|
||||
{
|
||||
public sealed class MqttServerTlsEndpointOptions
|
||||
{
|
||||
public bool IsEnabled { get; set; }
|
||||
|
||||
public int? Port { get; set; }
|
||||
|
||||
public byte[] Certificate { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace MQTTnet.Core.Server
|
||||
{
|
||||
public static class MqttTopicFilterComparer
|
||||
{
|
||||
private const char TopicLevelSeparator = '/';
|
||||
|
||||
public static bool IsMatch(string topic, string filter)
|
||||
{
|
||||
if (topic == null) throw new ArgumentNullException(nameof(topic));
|
||||
if (filter == null) throw new ArgumentNullException(nameof(filter));
|
||||
|
||||
if (string.Equals(topic, filter, StringComparison.Ordinal))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var fragmentsTopic = topic.Split(new[] { TopicLevelSeparator }, StringSplitOptions.None);
|
||||
var fragmentsFilter = filter.Split(new[] { TopicLevelSeparator }, StringSplitOptions.None);
|
||||
|
||||
for (var i = 0; i < fragmentsFilter.Length; i++)
|
||||
{
|
||||
if (fragmentsFilter[i] == "+")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fragmentsFilter[i] == "#" && i == fragmentsFilter.Length - 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (i >= fragmentsTopic.Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!string.Equals(fragmentsFilter[i], fragmentsTopic[i]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (fragmentsTopic.Length > fragmentsFilter.Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
+136
-87
@@ -1,39 +1,52 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.26730.16
|
||||
VisualStudioVersion = 15.0.27004.2010
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.Core.Tests", "Tests\MQTTnet.Core.Tests\MQTTnet.Core.Tests.csproj", "{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.NetFramework", "Frameworks\MQTTnet.NetFramework\MQTTnet.NetFramework.csproj", "{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.UniversalWindows", "Frameworks\MQTTnet.UniversalWindows\MQTTnet.UniversalWindows.csproj", "{BD60C727-D8E8-40C3-B8E3-C95A864AE611}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Core.Tests", "Tests\MQTTnet.Core.Tests\MQTTnet.Core.Tests.csproj", "{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{9248C2E1-B9D6-40BF-81EC-86004D7765B4}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Frameworks", "Frameworks", "{32A630A7-2598-41D7-B625-204CD906F5FB}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Core", "MQTTnet.Core\MQTTnet.Core.csproj", "{2ECB99E4-72D0-4C23-99BA-93D511D3967D}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.TestApp.NetFramework", "Tests\MQTTnet.TestApp.NetFramework\MQTTnet.TestApp.NetFramework.csproj", "{D9D74F33-6943-49B2-B765-7BD589082098}"
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Source", "Source", "{32A630A7-2598-41D7-B625-204CD906F5FB}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.TestApp.UniversalWindows", "Tests\MQTTnet.TestApp.UniversalWindows\MQTTnet.TestApp.UniversalWindows.csproj", "{FF1F72D6-9524-4422-9497-3CC0002216ED}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.NetStandard", "Frameworks\MQTTnet.Netstandard\MQTTnet.NetStandard.csproj", "{3587E506-55A2-4EB3-99C7-DC01E42D25D2}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet", "Source\MQTTnet\MQTTnet.csproj", "{3587E506-55A2-4EB3-99C7-DC01E42D25D2}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{67C28AC1-BC3A-420A-BE9C-FA2401431CF9}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
Build\build.ps1 = Build\build.ps1
|
||||
Build\MQTTnet.AspNetCore.nuspec = Build\MQTTnet.AspNetCore.nuspec
|
||||
Build\MQTTnet.Extensions.ManagedClient.nuspec = Build\MQTTnet.Extensions.ManagedClient.nuspec
|
||||
Build\MQTTnet.Extensions.Rpc.nuspec = Build\MQTTnet.Extensions.Rpc.nuspec
|
||||
Build\MQTTnet.nuspec = Build\MQTTnet.nuspec
|
||||
Build\upload.ps1 = Build\upload.ps1
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{B3F60ECB-45BA-4C66-8903-8BB89CA67998}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
.bettercodehub.yml = .bettercodehub.yml
|
||||
appveyor.yml = appveyor.yml
|
||||
LICENSE = LICENSE
|
||||
README.md = README.md
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.TestApp.NetCore", "Tests\MQTTnet.TestApp.NetCore\MQTTnet.TestApp.NetCore.csproj", "{3D283AAD-AAA8-4339-8394-52F80B6304DB}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.TestApp.AspNetCore2", "Tests\MQTTnet.TestApp.AspNetCore2\MQTTnet.TestApp.AspNetCore2.csproj", "{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.AspNetCore", "Source\MQTTnet.AspnetCore\MQTTnet.AspNetCore.csproj", "{F10C4060-F7EE-4A83-919F-FF723E72F94A}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Extensions", "Extensions", "{12816BCC-AF9E-44A9-9AE5-C246AF2A0587}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Extensions.Rpc", "Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj", "{C444E9C8-95FA-430E-9126-274129DE16CD}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Benchmarks", "Tests\MQTTnet.Benchmarks\MQTTnet.Benchmarks.csproj", "{998D04DD-7CB0-45F5-A393-E2495C16399E}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Extensions.ManagedClient", "Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj", "{C400533A-8EBA-4F0B-BF4D-295C3708604B}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.AspNetCore.Tests", "Tests\MQTTnet.AspNetCore.Tests\MQTTnet.AspNetCore.Tests.csproj", "{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -50,84 +63,21 @@ Global
|
||||
{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|x64
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|x64.Build.0 = Debug|x64
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|x86.Build.0 = Debug|x86
|
||||
{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|x64
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|x64.Build.0 = Release|x64
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|x86.ActiveCfg = Release|x86
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|x86.Build.0 = Release|x86
|
||||
{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|x64
|
||||
{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}.Debug|x64.Build.0 = Debug|x64
|
||||
{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}.Debug|x86.Build.0 = Debug|x86
|
||||
{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|x64
|
||||
{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}.Release|x64.Build.0 = Release|x64
|
||||
{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}.Release|x86.ActiveCfg = Release|x86
|
||||
{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D}.Release|x86.Build.0 = Release|x86
|
||||
{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
|
||||
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Release|x64.Build.0 = Release|Any CPU
|
||||
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Release|x86.Build.0 = Release|Any CPU
|
||||
{D9D74F33-6943-49B2-B765-7BD589082098}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D9D74F33-6943-49B2-B765-7BD589082098}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D9D74F33-6943-49B2-B765-7BD589082098}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{D9D74F33-6943-49B2-B765-7BD589082098}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{D9D74F33-6943-49B2-B765-7BD589082098}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{D9D74F33-6943-49B2-B765-7BD589082098}.Debug|x64.Build.0 = Debug|x64
|
||||
{D9D74F33-6943-49B2-B765-7BD589082098}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{D9D74F33-6943-49B2-B765-7BD589082098}.Debug|x86.Build.0 = Debug|x86
|
||||
{D9D74F33-6943-49B2-B765-7BD589082098}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D9D74F33-6943-49B2-B765-7BD589082098}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D9D74F33-6943-49B2-B765-7BD589082098}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{D9D74F33-6943-49B2-B765-7BD589082098}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{D9D74F33-6943-49B2-B765-7BD589082098}.Release|x64.ActiveCfg = Release|x64
|
||||
{D9D74F33-6943-49B2-B765-7BD589082098}.Release|x64.Build.0 = Release|x64
|
||||
{D9D74F33-6943-49B2-B765-7BD589082098}.Release|x86.ActiveCfg = Release|x86
|
||||
{D9D74F33-6943-49B2-B765-7BD589082098}.Release|x86.Build.0 = Release|x86
|
||||
{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
|
||||
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
|
||||
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|ARM.ActiveCfg = Debug|ARM
|
||||
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|ARM.Build.0 = Debug|ARM
|
||||
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|ARM.Deploy.0 = Debug|ARM
|
||||
@@ -180,18 +130,117 @@ Global
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|x64.Build.0 = Release|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|x86.Build.0 = Release|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|x64.Build.0 = Release|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|x86.Build.0 = Release|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|x64.Build.0 = Release|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|x86.Build.0 = Release|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Release|x64.Build.0 = Release|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Release|x86.Build.0 = Release|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|x64.Build.0 = Release|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|x86.Build.0 = Release|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|x64.Build.0 = Release|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|x86.Build.0 = Release|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|x64.Build.0 = Release|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
||||
{A480EF90-0EAA-4D9A-B271-47A9C47F6F7D} = {32A630A7-2598-41D7-B625-204CD906F5FB}
|
||||
{BD60C727-D8E8-40C3-B8E3-C95A864AE611} = {32A630A7-2598-41D7-B625-204CD906F5FB}
|
||||
{D9D74F33-6943-49B2-B765-7BD589082098} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
||||
{FF1F72D6-9524-4422-9497-3CC0002216ED} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2} = {32A630A7-2598-41D7-B625-204CD906F5FB}
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A} = {32A630A7-2598-41D7-B625-204CD906F5FB}
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD} = {12816BCC-AF9E-44A9-9AE5-C246AF2A0587}
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B} = {12816BCC-AF9E-44A9-9AE5-C246AF2A0587}
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {07536672-5CBC-4BE3-ACE0-708A431A7894}
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_ACCESSORHOLDER_ATTRIBUTE_ON_SAME_LINE_EX/@EntryValue">NEVER</s:String>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_WITHIN_METHOD_CALL_PARENTHESES/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_WITHIN_METHOD_PARENTHESES/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_WITHIN_PARENTHESES/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpAttributeForSingleLineMethodUpgrade/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpRenamePlacementToArrangementMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAddAccessorOwnerDeclarationBracesMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002ECSharpPlaceAttributeOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
@@ -3,151 +3,110 @@
|
||||
</p>
|
||||
|
||||
[](https://www.nuget.org/packages/MQTTnet)
|
||||
[](https://ci.appveyor.com/project/chkr1011/mqttnet/branch/develop)
|
||||
[](https://bettercodehub.com/)
|
||||
[](https://opencollective.com/mqttnet)
|
||||
[](https://opencollective.com/mqttnet)
|
||||
|
||||
# MQTTnet
|
||||
MQTTnet is a .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker). The implementation is based on the documentation from http://mqtt.org/.
|
||||
|
||||
# Features
|
||||
MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker). The implementation is based on the documentation from <http://mqtt.org/>.
|
||||
|
||||
## Features
|
||||
|
||||
### General
|
||||
|
||||
* Async support
|
||||
* TLS 1.2 support for client and server (but not UWP servers)
|
||||
* Extensible communication channels (i.e. In-Memory, TCP, TCP+TLS, WS)
|
||||
* Lightweight (only the low level implementation of MQTT, no overhead)
|
||||
* Performance optimized (processing ~27.000 messages / second)*
|
||||
* Performance optimized (processing ~70.000 messages / second)*
|
||||
* Interfaces included for mocking and testing
|
||||
* Access to internal trace messages
|
||||
* Unit tested (57+ tests)
|
||||
* Unit tested (~90 tests)
|
||||
|
||||
\* Tested on local machine with MQTTnet client and server running in the same process using the TCP channel. The app for verification is part of this repository and stored in _/Tests/MQTTnet.TestApp.NetFramework_.
|
||||
\* Tested on local machine (Intel i7 8700K) with MQTTnet client and server running in the same process using the TCP channel. The app for verification is part of this repository and stored in _/Tests/MQTTnet.TestApp.NetCore_.
|
||||
|
||||
### Client
|
||||
|
||||
* Communication via TCP (+TLS) or WS (WebSocket) supported
|
||||
* Included core _MqttClient_ with low level functionality
|
||||
* Also included _ManagedMqttClient_ which maintains the connection and subscriptions automatically. Also application messages are queued and re-scheduled for higher QoS levels automatically.
|
||||
* Rx support (via another project)
|
||||
* Communication via TCP (+TLS) or WS (WebSocket)
|
||||
* Compatible with Microsoft Azure IoT Hub
|
||||
|
||||
### Server (broker)
|
||||
|
||||
* List of connected clients available
|
||||
* Supports connected clients with different protocol versions at the same time
|
||||
* Able to publish its own messages (no loopback client required)
|
||||
* Able to receive every messages (no loopback client required)
|
||||
* Able to receive every message (no loopback client required)
|
||||
* Extensible client credential validation
|
||||
* Retained messages are supported including persisting via interface methods (own implementation required)
|
||||
* WebSockets supported (via ASP.NET Core 2.0, separate nuget)
|
||||
* A custom message interceptor can be added which allows transforming or extending every received application message
|
||||
* Validate subscriptions and deny subscribing of certain topics depending on requesting clients
|
||||
|
||||
## Supported frameworks
|
||||
|
||||
# Supported frameworks
|
||||
* .NET Standard 1.3+
|
||||
* .NET Core 1.1+
|
||||
* .NET Core App 1.1+
|
||||
* .NET Framework 4.5.2+ (x86, x64, AnyCPU)
|
||||
* Universal Windows (UWP) 10.0.10240+ (x86, x64, ARM, AnyCPU)
|
||||
* Mono 5.2+
|
||||
* Universal Windows Platform (UWP) 10.0.10240+ (x86, x64, ARM, AnyCPU, Windows 10 IoT Core)
|
||||
* Xamarin.Android 7.5+
|
||||
* Xamarin.iOS 10.14+
|
||||
|
||||
# Supported MQTT versions
|
||||
## Supported MQTT versions
|
||||
|
||||
* 5.0.0 (planned)
|
||||
* 3.1.1
|
||||
* 3.1.0
|
||||
|
||||
# Nuget
|
||||
This library is available as a nuget package: https://www.nuget.org/packages/MQTTnet/
|
||||
## Nuget
|
||||
|
||||
# Contributions
|
||||
If you want to contribute to this project just create a pull request.
|
||||
This library is available as a nuget package: <https://www.nuget.org/packages/MQTTnet/>
|
||||
|
||||
## Examples
|
||||
|
||||
Please find examples and the documentation at the Wiki of this repository (<https://github.com/chkr1011/MQTTnet/wiki>).
|
||||
|
||||
## Contributions
|
||||
|
||||
If you want to contribute to this project just create a pull request. But only pull requests which are matching the code style of this library will be accepted. Before creating a pull request please have a look at the library to get an overview of the required style.
|
||||
Also additions and updates in the Wiki are welcome.
|
||||
|
||||
This project also listed at Open Collective (https://opencollective.com/mqttnet).
|
||||
|
||||
## References
|
||||
|
||||
# References
|
||||
This library is used in the following projects:
|
||||
|
||||
* MQTT Client Rx (Wrapper for Reactive Extensions, https://github.com/1iveowl/MQTTClient.rx)
|
||||
* HA4IoT (Open Source Home Automation system for .NET, https://github.com/chkr1011/HA4IoT)
|
||||
* MQTT Client Rx (Wrapper for Reactive Extensions, <https://github.com/1iveowl/MQTTClient.rx>)
|
||||
* MQTT Tester (MQTT client test app for [Android](https://play.google.com/store/apps/details?id=com.liveowl.mqtttester) and [iOS](https://itunes.apple.com/us/app/mqtt-tester/id1278621826?mt=8))
|
||||
* HA4IoT (Open Source Home Automation system for .NET, <https://github.com/chkr1011/HA4IoT>)
|
||||
|
||||
If you use this library and want to see your project here please let me know.
|
||||
|
||||
# Examples
|
||||
## MqttClient
|
||||
## MIT License
|
||||
|
||||
```csharp
|
||||
var options = new MqttClientOptions
|
||||
{
|
||||
Server = "localhost"
|
||||
};
|
||||
Copyright (c) 2017-2018 Christian Kratky
|
||||
|
||||
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();
|
||||
};
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
client.Connected += async (s, e) =>
|
||||
{
|
||||
Console.WriteLine("### CONNECTED WITH SERVER, SUBSCRIBING ###");
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
await client.SubscribeAsync(new List<TopicFilter>
|
||||
{
|
||||
new TopicFilter("#", MqttQualityOfServiceLevel.AtMostOnce)
|
||||
});
|
||||
};
|
||||
|
||||
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 ###");
|
||||
|
||||
var messageFactory = new MqttApplicationMessageFactory();
|
||||
while (true)
|
||||
{
|
||||
Console.ReadLine();
|
||||
|
||||
var applicationMessage = messageFactory.CreateApplicationMessage("myTopic", "Hello World", MqttQualityOfServiceLevel.AtLeastOnce);
|
||||
await client.PublishAsync(applicationMessage);
|
||||
}
|
||||
```
|
||||
|
||||
## MqttServer
|
||||
|
||||
```csharp
|
||||
var options = new MqttServerOptions
|
||||
{
|
||||
ConnectionValidator = p =>
|
||||
{
|
||||
if (p.ClientId == "SpecialClient")
|
||||
{
|
||||
if (p.Username != "USER" || p.Password != "PASS")
|
||||
{
|
||||
return MqttConnectReturnCode.ConnectionRefusedBadUsernameOrPassword;
|
||||
}
|
||||
}
|
||||
|
||||
return MqttConnectReturnCode.ConnectionAccepted;
|
||||
}
|
||||
};
|
||||
|
||||
var mqttServer = new MqttServerFactory().CreateMqttServer(options);
|
||||
mqttServer.Start();
|
||||
|
||||
Console.WriteLine("Press any key to exit.");
|
||||
Console.ReadLine();
|
||||
|
||||
mqttServer.Stop();
|
||||
```
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System.Linq;
|
||||
using MQTTnet.Server;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MQTTnet.AspNetCore
|
||||
{
|
||||
public static class ApplicationBuilderExtensions
|
||||
{
|
||||
public static IApplicationBuilder UseMqttEndpoint(this IApplicationBuilder app, string path = "/mqtt")
|
||||
{
|
||||
app.UseWebSockets();
|
||||
app.Use(async (context, next) =>
|
||||
{
|
||||
if (!context.WebSockets.IsWebSocketRequest || context.Request.Path != path)
|
||||
{
|
||||
await next();
|
||||
return;
|
||||
}
|
||||
|
||||
string subProtocol = null;
|
||||
|
||||
if (context.Request.Headers.TryGetValue("Sec-WebSocket-Protocol", out var requestedSubProtocolValues))
|
||||
{
|
||||
subProtocol = SelectSubProtocol(requestedSubProtocolValues);
|
||||
}
|
||||
|
||||
var adapter = app.ApplicationServices.GetRequiredService<MqttWebSocketServerAdapter>();
|
||||
using (var webSocket = await context.WebSockets.AcceptWebSocketAsync(subProtocol))
|
||||
{
|
||||
var endpoint = $"{context.Connection.RemoteIpAddress}:{context.Connection.RemotePort}";
|
||||
await adapter.RunWebSocketConnectionAsync(webSocket, endpoint);
|
||||
}
|
||||
});
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
public static string SelectSubProtocol(IList<string> requestedSubProtocolValues)
|
||||
{
|
||||
// Order the protocols to also match "mqtt", "mqttv-3.1", "mqttv-3.11" etc.
|
||||
return requestedSubProtocolValues
|
||||
.OrderByDescending(p => p.Length)
|
||||
.FirstOrDefault(p => p.ToLower().StartsWith("mqtt"));
|
||||
}
|
||||
|
||||
public static IApplicationBuilder UseMqttServer(this IApplicationBuilder app, Action<IMqttServer> configure)
|
||||
{
|
||||
var server = app.ApplicationServices.GetRequiredService<IMqttServer>();
|
||||
|
||||
configure(server);
|
||||
|
||||
return app;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.AspNetCore.Client.Tcp;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Serializer;
|
||||
|
||||
namespace MQTTnet.AspNetCore.Client
|
||||
{
|
||||
public class MqttClientConnectionContextFactory : IMqttClientAdapterFactory
|
||||
{
|
||||
public IMqttChannelAdapter CreateClientAdapter(IMqttClientOptions options, IMqttNetChildLogger logger)
|
||||
{
|
||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||
|
||||
var serializer = new MqttPacketSerializer { ProtocolVersion = options.ProtocolVersion };
|
||||
|
||||
switch (options.ChannelOptions)
|
||||
{
|
||||
case MqttClientTcpOptions tcpOptions:
|
||||
{
|
||||
var endpoint = new DnsEndPoint(tcpOptions.Server, tcpOptions.GetPort());
|
||||
var tcpConnection = new TcpConnection(endpoint);
|
||||
return new MqttConnectionContext(serializer, tcpConnection);
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace MQTTnet.AspNetCore.Client.Tcp
|
||||
{
|
||||
public static class BufferExtensions
|
||||
{
|
||||
public static ArraySegment<byte> GetArray(this Memory<byte> memory)
|
||||
{
|
||||
return ((ReadOnlyMemory<byte>)memory).GetArray();
|
||||
}
|
||||
|
||||
public static ArraySegment<byte> GetArray(this ReadOnlyMemory<byte> memory)
|
||||
{
|
||||
if (!MemoryMarshal.TryGetArray(memory, out var result))
|
||||
{
|
||||
throw new InvalidOperationException("Buffer backed by array was expected");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using System.IO.Pipelines;
|
||||
|
||||
namespace MQTTnet.AspNetCore.Client.Tcp
|
||||
{
|
||||
public class DuplexPipe : IDuplexPipe
|
||||
{
|
||||
public DuplexPipe(PipeReader reader, PipeWriter writer)
|
||||
{
|
||||
Input = reader;
|
||||
Output = writer;
|
||||
}
|
||||
|
||||
public PipeReader Input { get; }
|
||||
|
||||
public PipeWriter Output { get; }
|
||||
|
||||
public static DuplexPipePair CreateConnectionPair(PipeOptions inputOptions, PipeOptions outputOptions)
|
||||
{
|
||||
var input = new Pipe(inputOptions);
|
||||
var output = new Pipe(outputOptions);
|
||||
|
||||
var transportToApplication = new DuplexPipe(output.Reader, input.Writer);
|
||||
var applicationToTransport = new DuplexPipe(input.Reader, output.Writer);
|
||||
|
||||
return new DuplexPipePair(applicationToTransport, transportToApplication);
|
||||
}
|
||||
|
||||
// This class exists to work around issues with value tuple on .NET Framework
|
||||
public readonly struct DuplexPipePair
|
||||
{
|
||||
public IDuplexPipe Transport { get; }
|
||||
public IDuplexPipe Application { get; }
|
||||
|
||||
public DuplexPipePair(IDuplexPipe transport, IDuplexPipe application)
|
||||
{
|
||||
Transport = transport;
|
||||
Application = application;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO.Pipelines;
|
||||
using System.Net.Sockets;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MQTTnet.AspNetCore.Client.Tcp
|
||||
{
|
||||
public class SocketAwaitable : ICriticalNotifyCompletion
|
||||
{
|
||||
private static readonly Action _callbackCompleted = () => { };
|
||||
|
||||
private readonly PipeScheduler _ioScheduler;
|
||||
|
||||
private Action _callback;
|
||||
private int _bytesTransferred;
|
||||
private SocketError _error;
|
||||
|
||||
public SocketAwaitable(PipeScheduler ioScheduler)
|
||||
{
|
||||
_ioScheduler = ioScheduler;
|
||||
}
|
||||
|
||||
public bool IsCompleted => ReferenceEquals(_callback, _callbackCompleted);
|
||||
|
||||
public SocketAwaitable GetAwaiter() => this;
|
||||
|
||||
public int GetResult()
|
||||
{
|
||||
Debug.Assert(ReferenceEquals(_callback, _callbackCompleted));
|
||||
|
||||
_callback = null;
|
||||
|
||||
if (_error != SocketError.Success)
|
||||
{
|
||||
throw new SocketException((int)_error);
|
||||
}
|
||||
|
||||
return _bytesTransferred;
|
||||
}
|
||||
|
||||
public void OnCompleted(Action continuation)
|
||||
{
|
||||
if (ReferenceEquals(_callback, _callbackCompleted) ||
|
||||
ReferenceEquals(Interlocked.CompareExchange(ref _callback, continuation, null), _callbackCompleted))
|
||||
{
|
||||
Task.Run(continuation);
|
||||
}
|
||||
}
|
||||
|
||||
public void UnsafeOnCompleted(Action continuation)
|
||||
{
|
||||
OnCompleted(continuation);
|
||||
}
|
||||
|
||||
public void Complete(int bytesTransferred, SocketError socketError)
|
||||
{
|
||||
_error = socketError;
|
||||
_bytesTransferred = bytesTransferred;
|
||||
var continuation = Interlocked.Exchange(ref _callback, _callbackCompleted);
|
||||
|
||||
if (continuation != null)
|
||||
{
|
||||
_ioScheduler.Schedule(state => ((Action)state)(), continuation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user