Compare commits

..
172 Commits
Author SHA1 Message Date
Christian Kratky 3cce33415d Update documents 2017-09-28 18:09:01 +02:00
Christian Kratky d09455f78e Add semaphore for accessing send stream 2017-09-28 09:29:47 +02:00
Christian Kratky e947881aca Remove lock to prevent dead locks 2017-09-27 21:20:42 +02:00
Christian Kratky 077c9ea00d Merge 2017-09-27 21:11:11 +02:00
Christian f8af4597f2 Merge pull request #44 from 1iveowl/master
Bug fixes v3
2017-09-27 21:09:31 +02:00
Christian Kratky 32dc96ae93 Wait until receiving of packets is started before sending packets 2017-09-27 21:08:36 +02:00
Christian Kratky 153e83ee3e Remove not needed async/await 2017-09-26 20:02:12 +02:00
Christian Kratky 6402dfd9ea Update version info to 2.3.1 2017-09-26 20:01:57 +02:00
Christian Kratky 98bafb1f26 Fix null ref exception in UWP TCP channel 2017-09-26 20:00:35 +02:00
1iveowl 6d92937892 Fixed typo 2017-09-24 17:06:41 +02:00
1iveowl 28d31d0cf5 Increased PR quality 2017-09-24 17:03:29 +02:00
1iveowl 07fd509e74 Bug fixes 2017-09-24 16:19:59 +02:00
Christian 37a5dc7abe Update README.md 2017-09-21 11:48:05 +02:00
Christian Kratky 8604097ada Merge branch 'master' of https://github.com/chkr1011/MQTTnet 2017-09-20 23:31:14 +02:00
Christian Kratky ea587d97c8 Organize solution 2017-09-20 23:31:09 +02:00
Christian fb272b07aa Update .bettercodehub.yml 2017-09-20 23:25:35 +02:00
Christian 7067a8e411 Create .bettercodehub.yml 2017-09-20 23:22:12 +02:00
Christian Kratky 50800b1d44 Fix issues with exception handling after disconnect 2017-09-20 21:36:32 +02:00
Christian Kratky dcf6f94d77 Refactoring 2017-09-20 21:28:18 +02:00
Christian Kratky 8131995006 Update core project to netstandard 1.3 2017-09-20 21:05:36 +02:00
Christian Kratky 7de1634a36 Fix issues with disconnected event 2017-09-20 21:05:13 +02:00
Christian b252781f8d Merge pull request #41 from JanEggers/develop
Develop
2017-09-20 21:00:39 +02:00
Eggers Jan 023cb12079 added resharper settings 2017-09-20 09:34:01 +02:00
Eggers Jan b5b87f5272 improved timeout after memory usage 2017-09-20 09:33:41 +02:00
Christian Kratky f287bd4d1d Fix TimeoutAfter again 2017-09-20 08:32:36 +02:00
Christian Kratky 222a154290 Update documentation 2017-09-19 23:15:41 +02:00
Christian Kratky 9b8af2455f Fix issues with "TimeoutAfter" 2017-09-19 23:08:18 +02:00
Christian Kratky 493c934463 Update documents 2017-09-19 23:07:42 +02:00
Christian Kratky 83f1d74a76 Make deserialization cancleable 2017-09-19 23:07:28 +02:00
Christian Kratky e7c8d1c1c1 Add cancellation token to adapter 2017-09-19 23:06:45 +02:00
Christian Kratky 305578e7e6 Refactor code 2017-09-19 23:05:41 +02:00
Christian Kratky 651f0aa15c Add interfaces for factories 2017-09-19 23:04:42 +02:00
Christian Kratky 51e7cd725e Extend UWP test app 2017-09-19 23:03:30 +02:00
Christian Kratky 32473af7c2 Refactor web socket usage 2017-09-18 18:20:36 +02:00
Christian Kratky b5fc6e3d12 Refactorings 2017-09-16 17:06:47 +02:00
Christian dc65bdb913 Merge pull request #40 from JanEggers/develop
Develop
2017-09-16 16:08:40 +02:00
JanEggers d4ef1a7302 fixed code issue 2017-09-16 08:29:34 +02:00
JanEggers f8c01b2168 fixed invalid operation exception 2017-09-16 08:13:46 +02:00
JanEggers d7e2b18247 packages that are events form the server may not cause invalid operation exceptions 2017-09-16 08:10:49 +02:00
JanEggers 12a275918e fixed high load on server 2017-09-16 08:02:48 +02:00
Christian Kratky eda8bff10b Optimized exception handling and awaits. 2017-09-15 15:58:48 +02:00
Christian Kratky 76105de4c7 Refactor latest changes 2017-09-15 15:26:26 +02:00
Christian Kratky eed56cdf55 Update documentation 2017-09-15 15:26:08 +02:00
Christian da7adc1068 Merge pull request #39 from JanEggers/develop
fixed unittest server and client both generated id 2 for differnt pac…
2017-09-15 08:22:58 +02:00
JanEggers f1ff947697 higher numbers 2017-09-14 22:26:28 +02:00
JanEggers 9250853854 Merge branch 'EvenMorePerf' into develop 2017-09-14 22:22:43 +02:00
JanEggers d1b634511d reduced footprint of client msg queue and numbers can go even higher 2017-09-14 22:18:54 +02:00
JanEggers cf7b7191f8 if we wait before sending the packets we dont need to store the received packets at all because the awaiter will always be there or is already canceled 2017-09-14 21:48:38 +02:00
Christian 0dcd8227da Add new badge 2017-09-14 18:33:29 +02:00
Eggers Jan 089eb9b604 fixed unittest server and client both generated id 2 for differnt package types 2017-09-14 13:37:45 +02:00
Christian f04a9c4f3e Fix readme layout 2017-09-14 08:41:23 +02:00
Christian Kratky e2d15c8764 Merge branch 'develop' of https://github.com/chkr1011/MQTTnet into develop
# Conflicts:
#	MQTTnet.Core/Serializer/MqttPacketReader.cs
#	MQTTnet.Core/Serializer/MqttPacketSerializer.cs
#	Tests/MQTTnet.TestApp.NetFramework/PerformanceTest.cs
2017-09-13 21:57:39 +02:00
Christian 9a25fb63ad Merge pull request #38 from JanEggers/Buffering
Buffering
2017-09-13 21:01:59 +02:00
Christian Kratky 0e48076d25 Extended performance test app 2017-09-13 21:01:36 +02:00
Christian Kratky a956de3df7 Refactorings 2017-09-13 21:01:25 +02:00
Eggers Jan 455a047242 fixed async await return bug 2017-09-13 09:35:56 +02:00
Eggers Jan 27bb22de09 use buffered receive stream 2017-09-13 09:35:46 +02:00
Eggers Jan 285c70f24f removed unused code 2017-09-13 09:19:41 +02:00
Eggers Jan c7c1691d75 use raw buffer for frist connect msg then use buffered stream to read more efficient 2017-09-13 09:14:08 +02:00
Eggers Jan 6c7e70f78a use blockingcollection for pipelining 2017-09-13 09:14:08 +02:00
Eggers Jan 97338b016d optimized communication adapter 2017-09-13 09:14:08 +02:00
Eggers Jan 473c8e0a15 send multiple packages at once msg/sec from 9000 to 32000 2017-09-13 09:14:08 +02:00
Eggers Jan 090e59e99a unfifed timeout handling and fixed memory usage due to Task.Delay tasks for each send package are present for the duration of the timeout resulting in memory usage. new approach uses cancellationtoken that will be cleaned up directly if operation completes before timeout 2017-09-13 09:12:34 +02:00
Eggers Jan 5712bc11d2 increased concurrency to force raceconditions 2017-09-13 09:10:41 +02:00
Eggers Jan 323339b0b2 dont use byte reader writer in critical paths 2017-09-13 09:10:41 +02:00
Eggers Jan c001897590 remove async and framing from serializer 2017-09-13 09:10:41 +02:00
Eggers Jan 8fd3e5070d add missing await 2017-09-13 09:10:41 +02:00
Eggers Jan 01e739d524 generate full debug symbols 2017-09-13 09:10:41 +02:00
Eggers Jan 16a184a92f use streams 2017-09-13 09:10:41 +02:00
Christian Kratky 1a1c0beabc Merge branch 'develop' of https://github.com/chkr1011/MQTTnet into develop 2017-09-12 22:07:41 +02:00
Christian Kratky ff0b022357 Partially fix UWP implementations 2017-09-12 22:07:27 +02:00
Christian c0e6e54496 Merge pull request #37 from JanEggers/PerfMsgDispatcher
Perf msg dispatcher
2017-09-12 21:50:30 +02:00
Christian Kratky 16bc9cb203 Fix awaits in try/catch 2017-09-12 21:50:07 +02:00
Eggers Jan 196a325f7f fixed incorrect merge 2017-09-12 08:23:50 +02:00
JanEggers da70dc0565 improved performance of packet dispatcher 2017-09-11 22:05:41 +02:00
Christian f7a8b764d0 Merge pull request #35 from JanEggers/BugFixMsgDispatcher
Bug fixes in msg dispatcher
2017-09-11 21:22:33 +02:00
Christian 16949ffb21 Merge pull request #36 from JanEggers/Buffering
added perf test app
2017-09-11 08:46:48 +02:00
JanEggers de0dfb9265 restore other app modes 2017-09-10 22:40:30 +02:00
JanEggers 9cde49f5f8 propper buffering in place 2017-09-10 18:20:25 +02:00
JanEggers 644bcfba27 - make MqttPacketReader and Writer a specialized version of their Binary counterparts
- improved message framing. as basis to improve buffering
- share more common code for the writer part
2017-09-10 15:41:39 +02:00
JanEggers a4d393ae28 fix code quality issues 2017-09-10 14:02:21 +02:00
JanEggers a7a5eed0f2 added perf test app 2017-09-10 14:02:21 +02:00
JanEggers 80a6e6809c use same lockobject everywhere to prevent raceconditions 2017-09-10 13:56:35 +02:00
JanEggers 861efa5e75 dont add the same package twice to _receivedPackets 2017-09-10 13:56:35 +02:00
JanEggers 6c8f85afe9 fixed invalid operation exception 2017-09-10 13:56:35 +02:00
Christian Kratky 6b1a6c054e Even more performance optimizations 2017-09-10 00:07:41 +02:00
Christian Kratky f2502ac431 Refactoring 2017-09-09 23:57:34 +02:00
Christian Kratky 76151deba0 Additional performance improvements 2017-09-09 23:53:42 +02:00
Christian 4a6790f0aa Merge pull request #34 from JanEggers/Fix_Bug33
fixed bug in read async and unfied stream handling
2017-09-09 23:18:04 +02:00
Christian 95298202a3 Merge branch 'develop' into Fix_Bug33 2017-09-09 23:17:41 +02:00
Christian Kratky 1c30d14910 Refactoring of WebSocket support 2017-09-09 23:13:53 +02:00
Christian Kratky 8aec1583df Performance improvements in area of logging and async/await 2017-09-09 22:40:37 +02:00
Christian Kratky 04036cf63a Prepare version information for the next release 2017-09-09 22:29:14 +02:00
Christian Kratky 68aae70b44 Merge branch 'develop' of https://github.com/chkr1011/MQTTnet into develop 2017-09-09 22:01:35 +02:00
Christian Kratky 0907eadb55 Make string format optional if no trace listener is attached 2017-09-09 21:50:55 +02:00
JanEggers 77818d669f Merge branch 'develop' into Fix_Bug33 2017-09-09 20:10:07 +02:00
Christian 70aee177c3 Merge pull request #32 from 1iveowl/master
ConfigureAwait(false)
2017-09-09 19:06:09 +02:00
JanEggers 5e417affa5 use configure await 2017-09-09 02:06:17 +02:00
JanEggers 783ee4328a fixed nullpointer exception in server 2017-09-09 02:01:45 +02:00
JanEggers caa857f318 fixed bug in read async and unfied stream handling 2017-09-09 01:47:32 +02:00
1iveowl 20a1b77b58 ConfigureAwait(false) 2017-09-09 00:48:50 +02:00
Christian 960900dafa Merge pull request #29 from Inventum24/WebSockets
Support for websockets
2017-09-01 18:46:07 +02:00
Christian Kratky 9bf287a1ac Update travis 2017-09-01 18:42:38 +02:00
Christian Kratky f886b0eed1 Fix travis file... 2017-09-01 18:19:59 +02:00
Christian Kratky 1f1f2d2e90 Yet another travis file 2017-09-01 18:13:16 +02:00
Christian Kratky 8ad619ebc1 Add travis.yml 2017-09-01 18:10:00 +02:00
Christian Kratky 85f1a31be3 Add travis file 2017-09-01 18:06:23 +02:00
IMPAQ\pnow 4a57d050f6 Clean Up 2017-09-01 11:28:31 +02:00
IMPAQ\pnow 3a1e32f5aa Added support for websockets 2017-09-01 09:39:59 +02:00
Christian Kratky b407221ce6 Merge branch 'master' into develop 2017-08-21 21:24:35 +02:00
Christian Kratky 96b92aabae Update version information 2017-08-21 20:27:39 +02:00
Christian Kratky d87dab2e25 Fixed typos 2017-08-21 20:24:16 +02:00
Christian b2f9d937e1 Merge pull request #28 from rydergillen-compacSort/master
Tls EndPoint uses DefaultSocket
2017-08-21 20:22:01 +02:00
Ryder Gillen c9ac954bfd Tls endpoint using wrong socket 2017-08-21 09:00:51 -07:00
rydergillen-compacSort dea2da52f9 Merge pull request #1 from chkr1011/master
v2.2.0 Merge
2017-08-21 08:04:02 -07:00
Christian Kratky 487a97c31e Merge branch 'master' into develop 2017-08-17 22:53:15 +02:00
Christian Kratky c90bdbfb93 Fix issue with connection management. 2017-08-17 22:49:16 +02:00
Christian Kratky 598ed66a3b Add support for V3.1.0; Performance improvements 2017-08-13 19:46:32 +02:00
Christian Kratky 59b0b79a32 Fix wrong QoS level handling for server. 2017-08-07 20:28:49 +02:00
Christian Kratky 0172606747 Merge branch 'develop' 2017-08-05 11:38:45 +02:00
Christian Kratky 65c2a0fd7c Update documentation 2017-08-05 11:38:24 +02:00
Christian Kratky 68926cef20 Add unit tests 2017-08-05 11:38:03 +02:00
Christian Kratky 447ca6cabb Add interfaces, optimize performance, fix QoS level 2 2017-08-05 11:37:52 +02:00
Christian Kratky 0b0239bcf1 Performance refactoring. 2017-07-28 22:21:48 +02:00
Christian Kratky e021d5a56b Handle exceptions whicha re thrown while processing application messages. 2017-07-28 22:18:39 +02:00
Christian Kratky 36abf8450e Merge branch 'develop' 2017-07-17 20:19:22 +02:00
Christian Kratky d67b4a0b35 Update documentation 2017-07-17 20:19:08 +02:00
Christian Kratky e4a376acdd Merge branch 'master' into develop 2017-07-15 19:00:29 +02:00
Christian Kratky 8cfdb2fbf2 Update release notes 2017-07-15 18:37:22 +02:00
Christian Kratky 886b2d6b42 Fix issue when stopping server 2017-07-15 18:36:33 +02:00
Christian Kratky 2193281c6a Add version information for next release 2017-07-15 18:36:09 +02:00
Christian Kratky eb7ee420fc Regorganize files 2017-07-15 10:16:26 +02:00
Christian Kratky 5a1d953278 Move build files to build folder 2017-07-15 10:11:03 +02:00
Christian 67c291e15a Merge pull request #15 from 1iveowl/master
Merge fixes
2017-07-15 09:59:48 +02:00
1iveowl 3722bc9ae6 Folder fix 2017-07-14 08:26:25 +02:00
1iveowl 1fb0944e2b Folder fix 2017-07-14 08:26:03 +02:00
1iveowl 61c7ee0958 Merge branch 'master' of https://github.com/1iveowl/MQTTnet 2017-07-14 08:23:45 +02:00
1iveowl 9c5e5b5e10 build fix 2017-07-14 08:22:41 +02:00
1iveowl b3e11d47b7 Build fix 2017-07-14 08:22:11 +02:00
1iveowl 31419fef03 Update .gitignore 2017-07-14 08:22:02 +02:00
Christian Kratky b2e3686f59 Fix build files 2017-07-13 23:13:56 +02:00
Christian 9b90ae0c81 Merge pull request #14 from 1iveowl/master
Added missing Nuspec file
2017-07-13 23:12:35 +02:00
1iveowl 39beb4912b Added missing Nuspec file
Added missing Nuspec file
2017-07-13 20:32:58 +02:00
Christian Kratky 98d7bc980c Add an event to receive all application messages at server level. 2017-07-13 20:28:49 +02:00
Christian 2ff449b241 Merge pull request #13 from 1iveowl/master
Build
2017-07-13 19:48:44 +02:00
1iveowl 83f6ae4e54 UWP target update 2017-07-13 15:01:35 +02:00
1iveowl d26d29e9a6 PowerShell build 2017-07-13 15:00:35 +02:00
Christian Kratky d0e8a0c4e5 Refactor .NET Standard projects 2017-07-12 19:56:57 +02:00
Christian Kratky 35344b27c5 Fixed build settings 2017-07-10 21:19:06 +02:00
Christian 6b6b7f90c8 Merge pull request #9 from 1iveowl/master
Adding support for .NET Standard
2017-07-10 21:02:31 +02:00
1iveowl eeb3242573 Fixed Assembly name 2017-07-06 00:27:19 +02:00
1iveowl cd5c295484 Adding .NET Standard 1.3 support 2017-07-05 21:57:37 +02:00
Christian Kratky 4ffe6dfc2b Update nuget spec 2017-06-08 14:36:50 +02:00
Christian Kratky 072c1c521a Fix x64 files at nuget package 2017-06-08 14:36:05 +02:00
Christian Kratky 314fca3f89 Optimize task management 2017-06-08 14:25:52 +02:00
Christian Kratky dae5a06734 Remove TLS support for UWP servers because Framework does not support it. 2017-06-06 14:31:59 +02:00
Christian Kratky 4b62236638 Update readme 2017-06-04 18:24:36 +02:00
Christian Kratky 5117f9b30d Update nuspec 2017-06-04 17:53:12 +02:00
Christian Kratky 818ffcab29 Fix project settings. 2017-06-04 17:47:41 +02:00
Christian Kratky efe2ceebe0 Merge branch 'develop' of https://github.com/chkr1011/MQTTnet into develop 2017-06-04 17:35:58 +02:00
Christian Kratky 67f9c07b44 Add references 2017-06-04 17:35:53 +02:00
Christian Kratky c5978f5845 Implement test app for Universal Windows 2017-06-04 17:31:58 +02:00
Christian Kratky 929cdb971a Fix breaking changes. 2017-06-03 22:48:22 +02:00
Christian 1317fa3e2a Merge pull request #3 from Zazzmatazz/master
Added TLS1.2 support to client and server.
2017-06-02 22:54:39 +02:00
Zazzmatazz 8a65850f1a Removed some %^&#ing $@#! comments. Gosh darnit. 2017-06-02 16:22:37 -04:00
Zazzmatazz 5c1eceb8a9 Updated task methods.
Instead of awaiting most tasks, changed to simply returning the tasks.
2017-06-02 16:20:48 -04:00
Zazzmatazz 2db289a658 Added TLS1.2 support to client and server.
Added MqttClientSslChannel and MqttServerSslChannel which both utilize
an SSL stream.  Added options to both MqttCLientOptions and
MqttServerOptions.  I was unable to update the code in
MQTTnet.UniversalWindows due to my lack of Windows 10.
2017-06-02 09:19:47 -04:00
Christian Kratky 201844a45b Update documentation 2017-05-21 10:29:45 +02:00
Christian Kratky 93a3bbd849 Throw MqttCommunicationException if access to underlying source failed. 2017-05-06 12:15:57 +02:00
Christian Kratky 86dddd81c7 Extend invalid protocol exception message 2017-05-06 12:15:14 +02:00
Christian Kratky 105abeba06 * [Server] Added support for complex client IDs.
* [Server] Fixed an issue with not correctly removed old client sessions.
* [Server] Several minor performance improvements.
* [Server] An existing client session is no longer closed if a new client connection is invalid.
* [Client] Added support for sending "CleanSession" flag.
2017-05-05 21:34:37 +02:00
Christian Kratky b3a6919ea0 Add support for complex device IDs. 2017-04-30 19:32:52 +02:00
148 changed files with 4986 additions and 2533 deletions
+8
View File
@@ -0,0 +1,8 @@
exclude:
- /Frameworks/.*
- /MQTTnet.TestApp.NetCore/Program.cs
- /Tests/MQTTnet.TestApp.NetFramework/.*
- /Tests/MQTTnet.TestApp.UniversalWindows/.*
component_depth: 1
languages:
- csharp
+66 -21
View File
@@ -1,5 +1,7 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.suo
@@ -15,12 +17,12 @@
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
[Xx]64/
[Xx]86/
[Bb]uild/
x64/
x86/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015 cache/options directory
.vs/
@@ -40,9 +42,11 @@ TestResult.xml
[Rr]eleasePS/
dlldata.c
# DNX
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
**/Properties/launchSettings.json
*_i.c
*_p.c
@@ -81,6 +85,7 @@ ipch/
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
@@ -108,6 +113,10 @@ _TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
@@ -139,13 +148,16 @@ publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Un-comment the next line if you do not want to checkin
# your web deploy settings because they may include unencrypted
# passwords
#*.pubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
@@ -154,7 +166,7 @@ publish/
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# NuGet v3's project.json files produces more ignoreable files
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
@@ -166,12 +178,11 @@ csx/
ecf/
rcf/
# Microsoft Azure ApplicationInsights config file
ApplicationInsights.config
# Windows Store app package directory
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
# Visual Studio cache files
# files ending in .cache can be ignored
@@ -181,16 +192,19 @@ BundleArtifacts/
# Others
ClientBin/
[Ss]tyle[Cc]op.*
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
node_modules/
orleans.codegen.cs
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
@@ -205,6 +219,7 @@ UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
@@ -219,6 +234,10 @@ FakesAssemblies/
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Typescript v1 declaration files
typings/
# Visual Studio 6 build log
*.plg
@@ -226,6 +245,9 @@ FakesAssemblies/
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
@@ -234,13 +256,36 @@ FakesAssemblies/
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# LightSwitch generated files
GeneratedArtifacts/
ModelManifest.xml
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
nuget.exe
# JetBrains Rider
.idea/
*.sln.iml
# CodeRush
.cr/
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
Build/nuget.exe
+10
View File
@@ -0,0 +1,10 @@
language: csharp
solution: MQTTnet.sln
matrix:
include:
- dotnet: 2.0.0
mono: none
dist: trusty
env: DOTNETCORE=1 # optional, can be used to take different code paths in your script
- mono: latest
+48
View File
@@ -0,0 +1,48 @@
<?xml version="1.0"?>
<package >
<metadata>
<id>MQTTnet</id>
<version>2.3.1</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
</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>
<dependencies>
<group targetFramework="netstandard1.3">
<dependency id="NETStandard.Library" version="1.6.1" />
<dependency id="System.Net.Security" version="4.3.2" />
</group>
<group targetFramework="uap10.0">
<dependency id="Microsoft.NETCore.UniversalWindowsPlatform" version="5.4.0" />
</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\"/>
<!-- 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\"/>
<!-- .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\"/>
</files>
</package>
+12
View File
@@ -0,0 +1,12 @@
param([string]$version)
if ([string]::IsNullOrEmpty($version)) {$version = "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"
Remove-Item .\NuGet -Force -Recurse
New-Item -ItemType Directory -Force -Path .\NuGet
.\NuGet.exe pack MQTTnet.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $version
@@ -1,34 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp1.1</TargetFramework>
<Authors>Christian Kratky</Authors>
<Company>Christian Kratky</Company>
<Product>MQTTnet</Product>
<Description>MQTTnet is a .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker).</Description>
<Copyright>Copyright © Christian Kratky 2016-2017</Copyright>
<Version>2.1.1.0</Version>
<AssemblyVersion>2.1.1.0</AssemblyVersion>
<FileVersion>2.1.1.0</FileVersion>
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
<AssemblyName>MQTTnet</AssemblyName>
<RootNamespace>MQTTnet</RootNamespace>
<PackageId>MQTTnet.NetCore</PackageId>
<PackageProjectUrl>https://github.com/chkr1011/MQTTnet</PackageProjectUrl>
<RepositoryUrl>https://github.com/chkr1011/MQTTnet</RepositoryUrl>
<PackageTags>MQTT MQTTClient MQTTServer MQTTBroker Broker</PackageTags>
<PackageIconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</PackageIconUrl>
<PackageLicenseUrl>https://github.com/chkr1011/MQTTnet/blob/master/LICENSE</PackageLicenseUrl>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Any CPU|AnyCPU'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Any CPU|x64'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Any CPU|x86'" />
<ItemGroup>
<ProjectReference Include="..\..\MQTTnet.Core\MQTTnet.Core.csproj" />
</ItemGroup>
</Project>
@@ -1,17 +0,0 @@
using System;
using MQTTnet.Core.Adapter;
using MQTTnet.Core.Client;
using MQTTnet.Core.Serializer;
namespace MQTTnet
{
public class MqttClientFactory
{
public MqttClient CreateMqttClient(MqttClientOptions options)
{
if (options == null) throw new ArgumentNullException(nameof(options));
return new MqttClient(options, new MqttChannelCommunicationAdapter(new MqttTcpChannel(), new DefaultMqttV311PacketSerializer()));
}
}
}
@@ -1,56 +0,0 @@
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Core.Adapter;
using MQTTnet.Core.Serializer;
using MQTTnet.Core.Server;
namespace MQTTnet
{
public sealed class MqttServerAdapter : IMqttServerAdapter, IDisposable
{
private CancellationTokenSource _cancellationTokenSource;
private Socket _socket;
public event EventHandler<MqttClientConnectedEventArgs> ClientConnected;
public void Start(MqttServerOptions options)
{
if (_socket != null) throw new InvalidOperationException("Server is already started.");
_cancellationTokenSource = new CancellationTokenSource();
_socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
_socket.Bind(new IPEndPoint(IPAddress.Any, options.Port));
_socket.Listen(options.ConnectionBacklog);
Task.Run(async () => await AcceptConnectionsAsync(_cancellationTokenSource.Token), _cancellationTokenSource.Token);
}
public void Stop()
{
_cancellationTokenSource?.Dispose();
_cancellationTokenSource = null;
_socket?.Dispose();
_socket = null;
}
public void Dispose()
{
Stop();
}
private async Task AcceptConnectionsAsync(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
var clientSocket = await _socket.AcceptAsync();
var clientAdapter = new MqttChannelCommunicationAdapter(new MqttTcpChannel(clientSocket), new DefaultMqttV311PacketSerializer());
ClientConnected?.Invoke(this, new MqttClientConnectedEventArgs(clientSocket.RemoteEndPoint.ToString(), clientAdapter));
}
}
}
}
@@ -1,15 +0,0 @@
using System;
using MQTTnet.Core.Server;
namespace MQTTnet
{
public class MqttServerFactory
{
public MqttServer CreateMqttServer(MqttServerOptions options)
{
if (options == null) throw new ArgumentNullException(nameof(options));
return new MqttServer(options, new MqttServerAdapter());
}
}
}
@@ -1,81 +0,0 @@
using System;
using System.Net.Sockets;
using System.Threading.Tasks;
using MQTTnet.Core.Channel;
using MQTTnet.Core.Client;
using MQTTnet.Core.Exceptions;
namespace MQTTnet
{
public class MqttTcpChannel : IMqttCommunicationChannel, IDisposable
{
private readonly Socket _socket;
public MqttTcpChannel()
{
_socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
}
public MqttTcpChannel(Socket socket)
{
_socket = socket ?? throw new ArgumentNullException(nameof(socket));
}
public async Task ConnectAsync(MqttClientOptions options)
{
try
{
await _socket.ConnectAsync(options.Server, options.Port);
}
catch (SocketException exception)
{
throw new MqttCommunicationException(exception);
}
}
public async Task DisconnectAsync()
{
try
{
_socket.Dispose();
await Task.FromResult(0);
}
catch (SocketException exception)
{
throw new MqttCommunicationException(exception);
}
}
public async Task WriteAsync(byte[] buffer)
{
if (buffer == null) throw new ArgumentNullException(nameof(buffer));
try
{
await _socket.SendAsync(new ArraySegment<byte>(buffer), SocketFlags.None);
}
catch (SocketException exception)
{
throw new MqttCommunicationException(exception);
}
}
public async Task ReadAsync(byte[] buffer)
{
try
{
var buffer2 = new ArraySegment<byte>(buffer);
await _socket.ReceiveAsync(buffer2, SocketFlags.None);
}
catch (SocketException exception)
{
throw new MqttCommunicationException(exception);
}
}
public void Dispose()
{
_socket?.Dispose();
}
}
}
@@ -0,0 +1,129 @@
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);
}
}
}
}
}
@@ -0,0 +1,116 @@
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;
}
}
}
@@ -0,0 +1,55 @@
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();
}
}
}
@@ -0,0 +1,77 @@
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();
}
}
}
@@ -100,11 +100,13 @@
<Reference Include="System.Core" />
</ItemGroup>
<ItemGroup>
<Compile Include="Implementations\MqttWebSocketChannel.cs" />
<Compile Include="Implementations\WebSocketStream.cs" />
<Compile Include="MqttClientFactory.cs" />
<Compile Include="MqttServerAdapter.cs" />
<Compile Include="MqttServerFactory.cs" />
<Compile Include="Implementations\MqttServerAdapter.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="MqttTcpChannel.cs" />
<Compile Include="Implementations\MqttTcpChannel.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\MQTTnet.Core\MQTTnet.Core.csproj">
@@ -1,17 +1,33 @@
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
public class MqttClientFactory : IMqttClientFactory
{
public MqttClient CreateMqttClient(MqttClientOptions options)
public IMqttClient CreateMqttClient(MqttClientOptions options)
{
if (options == null) throw new ArgumentNullException(nameof(options));
return new MqttClient(options, new MqttChannelCommunicationAdapter(new MqttTcpChannel(), new DefaultMqttV311PacketSerializer()));
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,56 +0,0 @@
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Core.Adapter;
using MQTTnet.Core.Serializer;
using MQTTnet.Core.Server;
namespace MQTTnet
{
public sealed class MqttServerAdapter : IMqttServerAdapter, IDisposable
{
private CancellationTokenSource _cancellationTokenSource;
private Socket _socket;
public event EventHandler<MqttClientConnectedEventArgs> ClientConnected;
public void Start(MqttServerOptions options)
{
if (_socket != null) throw new InvalidOperationException("Server is already started.");
_cancellationTokenSource = new CancellationTokenSource();
_socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
_socket.Bind(new IPEndPoint(IPAddress.Any, options.Port));
_socket.Listen(options.ConnectionBacklog);
Task.Run(async () => await AcceptConnectionsAsync(_cancellationTokenSource.Token), _cancellationTokenSource.Token);
}
public void Stop()
{
_cancellationTokenSource?.Dispose();
_cancellationTokenSource = null;
_socket?.Dispose();
_socket = null;
}
public void Dispose()
{
Stop();
}
private async Task AcceptConnectionsAsync(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
var clientSocket = await Task.Factory.FromAsync(_socket.BeginAccept, _socket.EndAccept, null);
var clientAdapter = new MqttChannelCommunicationAdapter(new MqttTcpChannel(clientSocket), new DefaultMqttV311PacketSerializer());
ClientConnected?.Invoke(this, new MqttClientConnectedEventArgs(clientSocket.RemoteEndPoint.ToString(), clientAdapter));
}
}
}
}
@@ -1,15 +1,18 @@
using System;
using System.Collections.Generic;
using MQTTnet.Core.Adapter;
using MQTTnet.Core.Server;
using MQTTnet.Implementations;
namespace MQTTnet
{
public class MqttServerFactory
public class MqttServerFactory : IMqttServerFactory
{
public MqttServer CreateMqttServer(MqttServerOptions options)
public IMqttServer CreateMqttServer(MqttServerOptions options)
{
if (options == null) throw new ArgumentNullException(nameof(options));
return new MqttServer(options, new MqttServerAdapter());
return new MqttServer(options, new List<IMqttServerAdapter> { new MqttServerAdapter() });
}
}
}
@@ -1,85 +0,0 @@
using System;
using System.Net.Sockets;
using System.Threading.Tasks;
using MQTTnet.Core.Channel;
using MQTTnet.Core.Client;
using MQTTnet.Core.Exceptions;
namespace MQTTnet
{
public class MqttTcpChannel : IMqttCommunicationChannel, IDisposable
{
private readonly Socket _socket;
public MqttTcpChannel()
{
_socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
}
public MqttTcpChannel(Socket socket)
{
_socket = socket ?? throw new ArgumentNullException(nameof(socket));
}
public async Task ConnectAsync(MqttClientOptions options)
{
try
{
await Task.Factory.FromAsync(_socket.BeginConnect, _socket.EndConnect, options.Server, options.Port, null);
}
catch (SocketException exception)
{
throw new MqttCommunicationException(exception);
}
}
public async Task DisconnectAsync()
{
try
{
await Task.Factory.FromAsync(_socket.BeginDisconnect, _socket.EndDisconnect, true, null);
}
catch (SocketException exception)
{
throw new MqttCommunicationException(exception);
}
}
public async Task WriteAsync(byte[] buffer)
{
if (buffer == null) throw new ArgumentNullException(nameof(buffer));
try
{
await Task.Factory.FromAsync(
// ReSharper disable once AssignNullToNotNullAttribute
_socket.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, null, null),
_socket.EndSend);
}
catch (SocketException exception)
{
throw new MqttCommunicationException(exception);
}
}
public async Task ReadAsync(byte[] buffer)
{
try
{
await Task.Factory.FromAsync(
// ReSharper disable once AssignNullToNotNullAttribute
_socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, null, null),
_socket.EndReceive);
}
catch (SocketException exception)
{
throw new MqttCommunicationException(exception);
}
}
public void Dispose()
{
_socket?.Dispose();
}
}
}
@@ -11,5 +11,5 @@ using System.Runtime.InteropServices;
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
[assembly: Guid("a480ef90-0eaa-4d9a-b271-47a9c47f6f7d")]
[assembly: AssemblyVersion("2.1.1.0")]
[assembly: AssemblyFileVersion("2.1.1.0")]
[assembly: AssemblyVersion("2.3.1.0")]
[assembly: AssemblyFileVersion("2.3.1.0")]
@@ -0,0 +1,125 @@
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);
}
}
}
}
}
@@ -0,0 +1,94 @@
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;
}
}
}
@@ -0,0 +1,55 @@
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();
}
}
}
@@ -0,0 +1,79 @@
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();
}
}
}
@@ -0,0 +1,32 @@
<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>
@@ -0,0 +1,33 @@
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();
}
}
}
}
@@ -0,0 +1,18 @@
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() });
}
}
}
@@ -0,0 +1,65 @@
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.");
}
}
}
}
@@ -0,0 +1,104 @@
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());
}
}
}
@@ -0,0 +1,56 @@
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();
}
}
}
@@ -0,0 +1,77 @@
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();
}
}
}
@@ -11,11 +11,12 @@
<AssemblyName>MQTTnet</AssemblyName>
<DefaultLanguage>en-US</DefaultLanguage>
<TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
<TargetPlatformVersion>10.0.14393.0</TargetPlatformVersion>
<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>
@@ -26,6 +27,7 @@
<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>
@@ -35,13 +37,14 @@
<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</NoWarn>
<NoWarn>;2008,CS1591</NoWarn>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
@@ -52,7 +55,7 @@
<OutputPath>bin\x86\Release\</OutputPath>
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<Optimize>true</Optimize>
<NoWarn>;2008</NoWarn>
<NoWarn>;2008,CS1591</NoWarn>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
@@ -64,7 +67,7 @@
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\ARM\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<NoWarn>;2008</NoWarn>
<NoWarn>;2008,CS1591</NoWarn>
<DebugType>full</DebugType>
<PlatformTarget>ARM</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
@@ -75,7 +78,7 @@
<OutputPath>bin\ARM\Release\</OutputPath>
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<Optimize>true</Optimize>
<NoWarn>;2008</NoWarn>
<NoWarn>;2008,CS1591</NoWarn>
<DebugType>pdbonly</DebugType>
<PlatformTarget>ARM</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
@@ -87,7 +90,7 @@
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<NoWarn>;2008</NoWarn>
<NoWarn>;2008,CS1591</NoWarn>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
@@ -98,7 +101,7 @@
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<Optimize>true</Optimize>
<NoWarn>;2008</NoWarn>
<NoWarn>;2008,CS1591</NoWarn>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
@@ -107,15 +110,13 @@
<DocumentationFile>bin\x64\Release\MQTTnet.XML</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<!-- A reference to the entire .Net Framework and Windows SDK are automatically included -->
<None Include="project.json" />
</ItemGroup>
<ItemGroup>
<Compile Include="Implementations\MqttWebSocketChannel.cs" />
<Compile Include="MqttClientFactory.cs" />
<Compile Include="MqttServerAdapter.cs" />
<Compile Include="Implementations\MqttServerAdapter.cs" />
<Compile Include="Implementations\MqttTcpChannel.cs" />
<Compile Include="MqttServerFactory.cs" />
<Compile Include="MqttTcpChannel.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Implementations\WebSocketStream.cs" />
<EmbeddedResource Include="Properties\MQTTnet.Universal.rd.xml" />
</ItemGroup>
<ItemGroup>
@@ -124,6 +125,11 @@
<Name>MQTTnet.Core</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
<Version>5.4.0</Version>
</PackageReference>
</ItemGroup>
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' &lt; '14.0' ">
<VisualStudioVersion>14.0</VisualStudioVersion>
</PropertyGroup>
@@ -1,17 +1,33 @@
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
public class MqttClientFactory : IMqttClientFactory
{
public MqttClient CreateMqttClient(MqttClientOptions options)
public IMqttClient CreateMqttClient(MqttClientOptions options)
{
if (options == null) throw new ArgumentNullException(nameof(options));
return new MqttClient(options, new MqttChannelCommunicationAdapter(new MqttTcpChannel(), new DefaultMqttV311PacketSerializer()));
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,54 +0,0 @@
using System;
using System.Threading;
using Windows.Networking.Sockets;
using MQTTnet.Core.Adapter;
using MQTTnet.Core.Serializer;
using MQTTnet.Core.Server;
namespace MQTTnet
{
public sealed class MqttServerAdapter : IMqttServerAdapter, IDisposable
{
private CancellationTokenSource _cancellationTokenSource;
private StreamSocketListener _socket;
public event EventHandler<MqttClientConnectedEventArgs> ClientConnected;
public void Start(MqttServerOptions options)
{
if (_socket != null) throw new InvalidOperationException("Server is already started.");
_cancellationTokenSource = new CancellationTokenSource();
_socket = new StreamSocketListener();
_socket.BindServiceNameAsync(options.Port.ToString()).AsTask().Wait();
_socket.ConnectionReceived += ConnectionReceived;
}
private void ConnectionReceived(StreamSocketListener sender, StreamSocketListenerConnectionReceivedEventArgs args)
{
var clientAdapter = new MqttChannelCommunicationAdapter(new MqttTcpChannel(args.Socket), new DefaultMqttV311PacketSerializer());
var identifier = $"{args.Socket.Information.RemoteAddress}:{args.Socket.Information.RemotePort}";
ClientConnected?.Invoke(this, new MqttClientConnectedEventArgs(identifier, clientAdapter));
}
public void Stop()
{
_cancellationTokenSource?.Dispose();
if (_socket != null)
{
_socket.ConnectionReceived -= ConnectionReceived;
}
_socket?.Dispose();
_socket = null;
}
public void Dispose()
{
Stop();
}
}
}
@@ -1,15 +1,18 @@
using System;
using System.Collections.Generic;
using MQTTnet.Core.Adapter;
using MQTTnet.Core.Server;
using MQTTnet.Implementations;
namespace MQTTnet
{
public class MqttServerFactory
public class MqttServerFactory : IMqttServerFactory
{
public MqttServer CreateMqttServer(MqttServerOptions options)
public IMqttServer CreateMqttServer(MqttServerOptions options)
{
if (options == null) throw new ArgumentNullException(nameof(options));
return new MqttServer(options, new MqttServerAdapter());
return new MqttServer(options, new List<IMqttServerAdapter> { new MqttServerAdapter() });
}
}
}
@@ -1,62 +0,0 @@
using System;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading.Tasks;
using Windows.Networking;
using Windows.Networking.Sockets;
using Windows.Storage.Streams;
using MQTTnet.Core.Channel;
using MQTTnet.Core.Client;
using Buffer = Windows.Storage.Streams.Buffer;
namespace MQTTnet
{
public sealed class MqttTcpChannel : IMqttCommunicationChannel, IDisposable
{
private readonly StreamSocket _socket;
public MqttTcpChannel()
{
_socket = new StreamSocket();
}
public MqttTcpChannel(StreamSocket socket)
{
_socket = socket ?? throw new ArgumentNullException(nameof(socket));
}
public async Task ConnectAsync(MqttClientOptions options)
{
if (options == null) throw new ArgumentNullException(nameof(options));
await _socket.ConnectAsync(new HostName(options.Server), options.Port.ToString());
}
public async Task DisconnectAsync()
{
await _socket.CancelIOAsync();
_socket.Dispose();
}
public async Task WriteAsync(byte[] buffer)
{
if (buffer == null) throw new ArgumentNullException(nameof(buffer));
await _socket.OutputStream.WriteAsync(buffer.AsBuffer());
await _socket.OutputStream.FlushAsync();
}
public async Task ReadAsync(byte[] buffer)
{
var buffer2 = new Buffer((uint)buffer.Length);
await _socket.InputStream.ReadAsync(buffer2, (uint)buffer.Length, InputStreamOptions.None);
var array2 = buffer2.ToArray();
Array.Copy(array2, buffer, array2.Length);
}
public void Dispose()
{
_socket?.Dispose();
}
}
}
@@ -10,5 +10,5 @@ using System.Runtime.InteropServices;
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
[assembly: AssemblyVersion("2.1.1.0")]
[assembly: AssemblyFileVersion("2.1.1.0")]
[assembly: AssemblyVersion("2.3.1.0")]
[assembly: AssemblyFileVersion("2.3.1.0")]
@@ -1,17 +0,0 @@
{
"dependencies": {
"Microsoft.NETCore.UniversalWindowsPlatform": "5.1.0"
},
"frameworks": {
"uap10.0": {},
"netstandard1.1": {}
},
"runtimes": {
"win10-arm": {},
"win10-arm-aot": {},
"win10-x86": {},
"win10-x86-aot": {},
"win10-x64": {},
"win10-x64-aot": {}
}
}
@@ -1,18 +1,23 @@
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
{
Task ConnectAsync(MqttClientOptions options, TimeSpan timeout);
IMqttPacketSerializer PacketSerializer { get; }
Task DisconnectAsync();
Task ConnectAsync(TimeSpan timeout, MqttClientOptions options);
Task SendPacketAsync(MqttBasePacket packet, TimeSpan timeout);
Task DisconnectAsync(TimeSpan timeout);
Task<MqttBasePacket> ReceivePacketAsync(TimeSpan timeout);
Task SendPacketsAsync(TimeSpan timeout, CancellationToken cancellationToken, IEnumerable<MqttBasePacket> packets);
Task<MqttBasePacket> ReceivePacketAsync(TimeSpan timeout, CancellationToken cancellationToken);
}
}
@@ -1,9 +1,13 @@
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;
@@ -11,78 +15,180 @@ namespace MQTTnet.Core.Adapter
{
public class MqttChannelCommunicationAdapter : IMqttCommunicationAdapter
{
private readonly IMqttPacketSerializer _serializer;
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));
_serializer = serializer ?? throw new ArgumentNullException(nameof(serializer));
PacketSerializer = serializer ?? throw new ArgumentNullException(nameof(serializer));
}
public async Task ConnectAsync(MqttClientOptions options, TimeSpan timeout)
{
var task = _channel.ConnectAsync(options);
if (await Task.WhenAny(Task.Delay(timeout), task) != task)
{
throw new MqttCommunicationTimedOutException();
}
}
public IMqttPacketSerializer PacketSerializer { get; }
public async Task DisconnectAsync()
public async Task ConnectAsync(TimeSpan timeout, MqttClientOptions options)
{
await _channel.DisconnectAsync();
}
public async Task SendPacketAsync(MqttBasePacket packet, TimeSpan timeout)
{
MqttTrace.Information(nameof(MqttChannelCommunicationAdapter), $"TX >>> {packet} [Timeout={timeout}]");
bool hasTimeout;
try
{
var task = _serializer.SerializeAsync(packet, _channel);
hasTimeout = await Task.WhenAny(Task.Delay(timeout), task) != task;
await _channel.ConnectAsync(options).TimeoutAfter(timeout).ConfigureAwait(false);
}
catch (TaskCanceledException)
{
throw;
}
catch (MqttCommunicationTimedOutException)
{
throw;
}
catch (MqttCommunicationException)
{
throw;
}
catch (Exception exception)
{
throw new MqttCommunicationException(exception);
}
}
if (hasTimeout)
public async Task DisconnectAsync(TimeSpan timeout)
{
try
{
throw new MqttCommunicationTimedOutException();
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<MqttBasePacket> ReceivePacketAsync(TimeSpan timeout)
public async Task SendPacketsAsync(TimeSpan timeout, CancellationToken cancellationToken, IEnumerable<MqttBasePacket> packets)
{
MqttBasePacket packet;
if (timeout > TimeSpan.Zero)
{
var workerTask = _serializer.DeserializeAsync(_channel);
var timeoutTask = Task.Delay(timeout);
var hasTimeout = Task.WhenAny(timeoutTask, workerTask) == timeoutTask;
await _semaphore.WaitAsync(cancellationToken);
if (hasTimeout)
try
{
foreach (var packet in packets)
{
throw new MqttCommunicationTimedOutException();
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);
}
packet = workerTask.Result;
if (timeout > TimeSpan.Zero)
{
await _channel.SendStream.FlushAsync(cancellationToken).TimeoutAfter(timeout).ConfigureAwait(false);
}
else
{
await _channel.SendStream.FlushAsync(cancellationToken).ConfigureAwait(false);
}
}
else
catch (TaskCanceledException)
{
packet = await _serializer.DeserializeAsync(_channel);
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));
}
if (packet == null)
{
throw new MqttProtocolViolationException("Received malformed packet.");
}
var body = new byte[header.BodyLength];
MqttTrace.Information(nameof(MqttChannelCommunicationAdapter), $"RX <<< {packet}");
return packet;
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));
}
}
}
}
@@ -0,0 +1,15 @@
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);
}
}
}
@@ -5,7 +5,7 @@ namespace MQTTnet.Core.Adapter
{
public class MqttConnectingFailedException : MqttCommunicationException
{
public MqttConnectingFailedException(MqttConnectReturnCode returnCode)
public MqttConnectingFailedException(MqttConnectReturnCode returnCode)
: base($"Connecting with MQTT server failed ({returnCode}).")
{
ReturnCode = returnCode;
@@ -0,0 +1,19 @@
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 +1,16 @@
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();
Task WriteAsync(byte[] buffer);
Task ReadAsync(byte[] buffer);
}
}
+24
View File
@@ -0,0 +1,24 @@
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);
}
}
@@ -0,0 +1,7 @@
namespace MQTTnet.Core.Client
{
public interface IMqttClientFactory
{
IMqttClient CreateMqttClient(MqttClientOptions options);
}
}
@@ -1,8 +1,8 @@
using System;
namespace MQTTnet.Core
namespace MQTTnet.Core.Client
{
public class MqttApplicationMessageReceivedEventArgs : EventArgs
public sealed class MqttApplicationMessageReceivedEventArgs : EventArgs
{
public MqttApplicationMessageReceivedEventArgs(MqttApplicationMessage applicationMessage)
{
+239 -155
View File
@@ -1,5 +1,4 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
@@ -13,15 +12,14 @@ using MQTTnet.Core.Protocol;
namespace MQTTnet.Core.Client
{
public class MqttClient
public class MqttClient : IMqttClient
{
private readonly ConcurrentDictionary<ushort, MqttPublishPacket> _pendingExactlyOncePublishPackets = new ConcurrentDictionary<ushort, MqttPublishPacket>();
private readonly HashSet<ushort> _processedPublishPackets = new HashSet<ushort>();
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;
@@ -29,137 +27,183 @@ namespace MQTTnet.Core.Client
{
_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 { get; private set; }
public bool IsConnected => _cancellationTokenSource != null && !_cancellationTokenSource.IsCancellationRequested;
public async Task ConnectAsync(MqttApplicationMessage willApplicationMessage = null)
{
MqttTrace.Verbose(nameof(MqttClient), "Trying to connect.");
ThrowIfConnected("It is not allowed to connect with a server after the connection is established.");
if (IsConnected)
try
{
throw new MqttProtocolViolationException("It is not allowed to connect with a server after the connection is established.");
_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);
}
var connectPacket = new MqttConnectPacket
catch (Exception)
{
ClientId = _options.ClientId,
Username = _options.UserName,
Password = _options.Password,
KeepAlivePeriod = (ushort)_options.KeepAlivePeriod.TotalSeconds,
WillMessage = willApplicationMessage
};
await _adapter.ConnectAsync(_options, _options.DefaultCommunicationTimeout);
MqttTrace.Verbose(nameof(MqttClient), "Connection with server established.");
_cancellationTokenSource = new CancellationTokenSource();
_latestPacketIdentifier = 0;
_processedPublishPackets.Clear();
_packetDispatcher.Reset();
IsConnected = true;
Task.Run(() => ReceivePackets(_cancellationTokenSource.Token), _cancellationTokenSource.Token).Forget();
var response = await SendAndReceiveAsync<MqttConnAckPacket>(connectPacket);
if (response.ConnectReturnCode != MqttConnectReturnCode.ConnectionAccepted)
{
throw new MqttConnectingFailedException(response.ConnectReturnCode);
await DisconnectInternalAsync().ConfigureAwait(false);
throw;
}
if (_options.KeepAlivePeriod != TimeSpan.Zero)
{
Task.Run(() => SendKeepAliveMessagesAsync(_cancellationTokenSource.Token), _cancellationTokenSource.Token).Forget();
}
Connected?.Invoke(this, EventArgs.Empty);
}
public async Task DisconnectAsync()
{
await SendAsync(new MqttDisconnectPacket());
await DisconnectInternalAsync();
if (!IsConnected)
{
return;
}
try
{
await SendAsync(new MqttDisconnectPacket()).ConfigureAwait(false);
}
finally
{
await DisconnectInternalAsync().ConfigureAwait(false);
}
}
public Task<IList<MqttSubscribeResult>> SubscribeAsync(params TopicFilter[] topicFilters)
public async Task<IList<MqttSubscribeResult>> SubscribeAsync(IEnumerable<TopicFilter> topicFilters)
{
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
return SubscribeAsync(topicFilters.ToList());
}
public async Task<IList<MqttSubscribeResult>> SubscribeAsync(IList<TopicFilter> topicFilters)
{
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
if (!topicFilters.Any()) throw new MqttProtocolViolationException("At least one topic filter must be set [MQTT-3.8.3-3].");
ThrowIfNotConnected();
var subscribePacket = new MqttSubscribePacket
{
PacketIdentifier = GetNewPacketIdentifier(),
TopicFilters = topicFilters
TopicFilters = topicFilters.ToList()
};
var response = await SendAndReceiveAsync<MqttSubAckPacket>(subscribePacket);
var response = await SendAndReceiveAsync<MqttSubAckPacket>(subscribePacket).ConfigureAwait(false);
if (response.SubscribeReturnCodes.Count != topicFilters.Count)
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 topicFilters.Select((t, i) => new MqttSubscribeResult(t, response.SubscribeReturnCodes[i])).ToList();
return subscribePacket.TopicFilters.Select((t, i) => new MqttSubscribeResult(t, response.SubscribeReturnCodes[i])).ToList();
}
public Task Unsubscribe(params string[] topicFilters)
public async Task UnsubscribeAsync(IEnumerable<string> topicFilters)
{
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
return Unsubscribe(topicFilters.ToList());
}
public async Task Unsubscribe(IList<string> topicFilters)
{
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
if (!topicFilters.Any()) throw new MqttProtocolViolationException("At least one topic filter must be set [MQTT-3.10.3-2].");
ThrowIfNotConnected();
var unsubscribePacket = new MqttUnsubscribePacket
{
PacketIdentifier = GetNewPacketIdentifier(),
TopicFilters = topicFilters
TopicFilters = topicFilters.ToList()
};
await SendAndReceiveAsync<MqttUnsubAckPacket>(unsubscribePacket);
}
public async Task PublishAsync(MqttApplicationMessage applicationMessage)
public async Task PublishAsync(IEnumerable<MqttApplicationMessage> applicationMessages)
{
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
ThrowIfNotConnected();
var publishPacket = applicationMessage.ToPublishPacket();
var publishPackets = applicationMessages.Select(m => m.ToPublishPacket());
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce)
foreach (var qosGroup in publishPackets.GroupBy(p => p.QualityOfServiceLevel))
{
await SendAsync(publishPacket);
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();
}
}
}
else if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtLeastOnce)
}
private async Task AuthenticateAsync(MqttApplicationMessage willApplicationMessage)
{
var connectPacket = new MqttConnectPacket
{
publishPacket.PacketIdentifier = GetNewPacketIdentifier();
await SendAndReceiveAsync<MqttPubAckPacket>(publishPacket);
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);
}
else if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.ExactlyOnce)
}
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)
{
publishPacket.PacketIdentifier = GetNewPacketIdentifier();
await SendAndReceiveAsync<MqttPubRecPacket>(publishPacket);
await SendAsync(publishPacket.CreateResponse<MqttPubCompPacket>());
await Task.Delay(TimeSpan.FromMilliseconds(100));
}
}
@@ -168,76 +212,87 @@ namespace MQTTnet.Core.Client
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();
await _adapter.DisconnectAsync(_options.DefaultCommunicationTimeout).ConfigureAwait(false);
MqttTrace.Information(nameof(MqttClient), "Disconnected from adapter.");
}
catch
catch (Exception exception)
{
MqttTrace.Warning(nameof(MqttClient), exception, "Error while disconnecting from adapter.");
}
finally
{
_cancellationTokenSource?.Cancel();
IsConnected = false;
Disconnected?.Invoke(this, EventArgs.Empty);
}
}
private async void ProcessReceivedPacket(MqttBasePacket mqttPacket)
private async Task ProcessReceivedPacketAsync(MqttBasePacket packet)
{
try
{
if (mqttPacket is MqttPingReqPacket)
MqttTrace.Information(nameof(MqttClient), "Received <<< {0}", packet);
if (packet is MqttPingReqPacket)
{
await SendAsync(new MqttPingRespPacket());
return;
}
if (mqttPacket is MqttDisconnectPacket)
if (packet is MqttDisconnectPacket)
{
await DisconnectAsync();
return;
}
var publishPacket = mqttPacket as MqttPublishPacket;
if (publishPacket != null)
if (packet is MqttPublishPacket publishPacket)
{
await ProcessReceivedPublishPacket(publishPacket);
return;
}
var pubRelPacket = mqttPacket as MqttPubRelPacket;
if (pubRelPacket != null)
if (packet is MqttPubRelPacket pubRelPacket)
{
await ProcessReceivedPubRelPacket(pubRelPacket);
return;
}
_packetDispatcher.Dispatch(mqttPacket);
_packetDispatcher.Dispatch(packet);
}
catch (Exception exception)
{
MqttTrace.Error(nameof(MqttClient), exception, "Error while processing received packet.");
MqttTrace.Error(nameof(MqttClient), exception, "Unhandled exception while processing received packet.");
}
}
private void FireApplicationMessageReceivedEvent(MqttPublishPacket publishPacket)
{
if (publishPacket.QualityOfServiceLevel != MqttQualityOfServiceLevel.AtMostOnce)
try
{
_processedPublishPackets.Add(publishPacket.PacketIdentifier);
var applicationMessage = publishPacket.ToApplicationMessage();
ApplicationMessageReceived?.Invoke(this, new MqttApplicationMessageReceivedEventArgs(applicationMessage));
}
catch (Exception exception)
{
MqttTrace.Error(nameof(MqttClient), exception, "Unhandled exception while handling application message.");
}
var applicationMessage = new MqttApplicationMessage(
publishPacket.Topic,
publishPacket.Payload,
publishPacket.QualityOfServiceLevel,
publishPacket.Retain
);
ApplicationMessageReceived?.Invoke(this, new MqttApplicationMessageReceivedEventArgs(applicationMessage));
}
private async Task ProcessReceivedPublishPacket(MqttPublishPacket publishPacket)
@@ -245,66 +300,52 @@ namespace MQTTnet.Core.Client
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce)
{
FireApplicationMessageReceivedEvent(publishPacket);
return;
}
else
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtLeastOnce)
{
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtLeastOnce)
{
FireApplicationMessageReceivedEvent(publishPacket);
await SendAsync(new MqttPubAckPacket { PacketIdentifier = publishPacket.PacketIdentifier });
}
else if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.ExactlyOnce)
{
_pendingExactlyOncePublishPackets[publishPacket.PacketIdentifier] = publishPacket;
await SendAsync(new MqttPubRecPacket { PacketIdentifier = publishPacket.PacketIdentifier });
}
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 async Task ProcessReceivedPubRelPacket(MqttPubRelPacket pubRelPacket)
private Task ProcessReceivedPubRelPacket(MqttPubRelPacket pubRelPacket)
{
MqttPublishPacket originalPublishPacket;
if (!_pendingExactlyOncePublishPackets.TryRemove(pubRelPacket.PacketIdentifier, out originalPublishPacket))
lock (_unacknowledgedPublishPackets)
{
throw new MqttCommunicationException();
_unacknowledgedPublishPackets.Remove(pubRelPacket.PacketIdentifier);
}
await SendAsync(originalPublishPacket.CreateResponse<MqttPubCompPacket>());
FireApplicationMessageReceivedEvent(originalPublishPacket);
return SendAsync(pubRelPacket.CreateResponse<MqttPubCompPacket>());
}
private Task SendAsync(MqttBasePacket packet)
{
return _adapter.SendPacketAsync(packet, _options.DefaultCommunicationTimeout);
return _adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, _cancellationTokenSource.Token, packet);
}
private async Task<TResponsePacket> SendAndReceiveAsync<TResponsePacket>(MqttBasePacket requestPacket) where TResponsePacket : MqttBasePacket
{
bool ResponsePacketSelector(MqttBasePacket p)
{
var p1 = p as TResponsePacket;
if (p1 == null)
{
return false;
}
var pi1 = requestPacket as IPacketWithIdentifier;
var pi2 = p as IPacketWithIdentifier;
if (pi1 != null && pi2 != null)
{
if (pi1.PacketIdentifier != pi2.PacketIdentifier)
{
return false;
}
}
return true;
}
await _adapter.SendPacketAsync(requestPacket, _options.DefaultCommunicationTimeout);
return (TResponsePacket)await _packetDispatcher.WaitForPacketAsync(ResponsePacketSelector, _options.DefaultCommunicationTimeout);
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()
@@ -320,51 +361,94 @@ namespace MQTTnet.Core.Client
{
while (!cancellationToken.IsCancellationRequested)
{
await Task.Delay(_options.KeepAlivePeriod, cancellationToken);
await SendAndReceiveAsync<MqttPingRespPacket>(new MqttPingReqPacket());
await Task.Delay(_options.KeepAlivePeriod, cancellationToken).ConfigureAwait(false);
if (cancellationToken.IsCancellationRequested)
{
return;
}
await SendAndReceiveAsync<MqttPingRespPacket>(new MqttPingReqPacket()).ConfigureAwait(false);
}
}
catch (TaskCanceledException)
{
}
catch (MqttCommunicationException exception)
{
MqttTrace.Warning(nameof(MqttClient), exception, "MQTT communication error while receiving packets.");
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, "Error while sending/receiving keep alive packets.");
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.");
await DisconnectInternalAsync();
}
}
private async Task ReceivePackets(CancellationToken cancellationToken)
{
MqttTrace.Information(nameof(MqttClient), "Start receiving packets.");
try
{
while (!cancellationToken.IsCancellationRequested)
{
var mqttPacket = await _adapter.ReceivePacketAsync(TimeSpan.Zero);
MqttTrace.Information(nameof(MqttClient), $"Received <<< {mqttPacket}");
_isReceivingPackets = true;
Task.Run(() => ProcessReceivedPacket(mqttPacket), cancellationToken).Forget();
var packet = await _adapter.ReceivePacketAsync(TimeSpan.Zero, cancellationToken).ConfigureAwait(false);
if (cancellationToken.IsCancellationRequested)
{
return;
}
StartProcessReceivedPacket(packet, cancellationToken);
}
}
catch (TaskCanceledException)
{
}
catch (MqttCommunicationException exception)
{
MqttTrace.Warning(nameof(MqttClient), exception, "MQTT communication error while receiving packets.");
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, "Error while receiving packets.");
MqttTrace.Error(nameof(MqttClient), exception, "Unhandled exception while receiving packets.");
await DisconnectInternalAsync().ConfigureAwait(false);
}
finally
{
MqttTrace.Information(nameof(MqttClient), "Stopped receiving packets.");
await DisconnectInternalAsync();
}
}
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
}
}
}
@@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using MQTTnet.Core.Packets;
namespace MQTTnet.Core.Client
{
public static class MqttClientExtensions
{
public static Task PublishAsync(this IMqttClient client, params MqttApplicationMessage[] applicationMessages)
{
if (client == null) throw new ArgumentNullException(nameof(client));
if (applicationMessages == null) throw new ArgumentNullException(nameof(applicationMessages));
return client.PublishAsync(applicationMessages);
}
public static Task<IList<MqttSubscribeResult>> SubscribeAsync(this IMqttClient client, params TopicFilter[] topicFilters)
{
if (client == null) throw new ArgumentNullException(nameof(client));
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
return client.SubscribeAsync(topicFilters.ToList());
}
public static Task UnsubscribeAsync(this IMqttClient client, params string[] topicFilters)
{
if (client == null) throw new ArgumentNullException(nameof(client));
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
return client.UnsubscribeAsync(topicFilters.ToList());
}
}
}
+11 -2
View File
@@ -1,12 +1,15 @@
using System;
using MQTTnet.Core.Serializer;
namespace MQTTnet.Core.Client
{
public class MqttClientOptions
public sealed class MqttClientOptions
{
public string Server { get; set; }
public int Port { get; set; } = 1883;
public int? Port { get; set; }
public MqttClientTlsOptions TlsOptions { get; set; } = new MqttClientTlsOptions();
public string UserName { get; set; }
@@ -14,8 +17,14 @@ namespace MQTTnet.Core.Client
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;
}
}
@@ -0,0 +1,19 @@
using System;
namespace MQTTnet.Core.Client
{
public static class MqttClientOptionsExtensions
{
public static int GetPort(this MqttClientOptions options)
{
if (options == null) throw new ArgumentNullException(nameof(options));
if (options.Port.HasValue)
{
return options.Port.Value;
}
return !options.TlsOptions.UseTls ? 1883 : 8883;
}
}
}
@@ -0,0 +1,13 @@
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; }
}
}
@@ -0,0 +1,8 @@
namespace MQTTnet.Core.Client
{
public enum MqttConnectionType
{
Tcp,
Ws
}
}
-16
View File
@@ -1,16 +0,0 @@
using System;
using System.Threading.Tasks;
using MQTTnet.Core.Packets;
namespace MQTTnet.Core.Client
{
public class MqttPacketAwaiter : TaskCompletionSource<MqttBasePacket>
{
public MqttPacketAwaiter(Func<MqttBasePacket, bool> packetSelector)
{
PacketSelector = packetSelector ?? throw new ArgumentNullException(nameof(packetSelector));
}
public Func<MqttBasePacket, bool> PacketSelector { get; }
}
}
+52 -67
View File
@@ -1,111 +1,96 @@
using System;
using System.Collections.Generic;
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 object _syncRoot = new object();
private readonly List<MqttBasePacket> _receivedPackets = new List<MqttBasePacket>();
private readonly List<MqttPacketAwaiter> _packetAwaiters = new List<MqttPacketAwaiter>();
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(Func<MqttBasePacket, bool> selector, TimeSpan timeout)
public async Task<MqttBasePacket> WaitForPacketAsync(MqttBasePacket request, Type responseType, TimeSpan timeout)
{
if (selector == null) throw new ArgumentNullException(nameof(selector));
if (request == null) throw new ArgumentNullException(nameof(request));
var packetAwaiter = AddPacketAwaiter(selector);
DispatchPendingPackets();
var hasTimeout = await Task.WhenAny(Task.Delay(timeout), packetAwaiter.Task) != packetAwaiter.Task;
RemovePacketAwaiter(packetAwaiter);
if (hasTimeout)
var packetAwaiter = AddPacketAwaiter(request, responseType);
try
{
MqttTrace.Warning(nameof(MqttPacketDispatcher), "Timeout while waiting for packet.");
throw new MqttCommunicationTimedOutException();
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);
}
return packetAwaiter.Task.Result;
}
public void Dispatch(MqttBasePacket packet)
{
if (packet == null) throw new ArgumentNullException(nameof(packet));
var packetDispatched = false;
foreach (var packetAwaiter in GetPacketAwaiters())
var type = packet.GetType();
if (packet is IMqttPacketWithIdentifier withIdentifier)
{
if (packetAwaiter.PacketSelector(packet))
if (_packetByResponseTypeAndIdentifier.TryGetValue(type, out var byid))
{
packetAwaiter.SetResult(packet);
packetDispatched = true;
break;
if (byid.TryRemove(withIdentifier.PacketIdentifier, out var tcs))
{
tcs.TrySetResult(packet);
return;
}
}
}
else if (_packetByResponseType.TryRemove(type, out var tcs))
{
tcs.TrySetResult(packet);
return;
}
lock (_syncRoot)
{
if (!packetDispatched)
{
_receivedPackets.Add(packet);
}
else
{
_receivedPackets.Remove(packet);
}
}
throw new InvalidOperationException($"Packet of type '{type.Name}' not handled or dispatched.");
}
public void Reset()
{
lock (_syncRoot)
{
_packetAwaiters.Clear();
_receivedPackets.Clear();
}
_packetByResponseTypeAndIdentifier.Clear();
_packetByResponseType.Clear();
}
private List<MqttPacketAwaiter> GetPacketAwaiters()
private TaskCompletionSource<MqttBasePacket> AddPacketAwaiter(MqttBasePacket request, Type responseType)
{
lock (_packetAwaiters)
var tcs = new TaskCompletionSource<MqttBasePacket>();
if (request is IMqttPacketWithIdentifier requestWithIdentifier)
{
return new List<MqttPacketAwaiter>(_packetAwaiters);
var byId = _packetByResponseTypeAndIdentifier.GetOrAdd(responseType, key => new ConcurrentDictionary<ushort, TaskCompletionSource<MqttBasePacket>>());
byId[requestWithIdentifier.PacketIdentifier] = tcs;
}
else
{
_packetByResponseType[responseType] = tcs;
}
return tcs;
}
private MqttPacketAwaiter AddPacketAwaiter(Func<MqttBasePacket, bool> selector)
private void RemovePacketAwaiter(MqttBasePacket request, Type responseType)
{
lock (_syncRoot)
if (request is IMqttPacketWithIdentifier requestWithIdentifier)
{
var packetAwaiter = new MqttPacketAwaiter(selector);
_packetAwaiters.Add(packetAwaiter);
return packetAwaiter;
var byId = _packetByResponseTypeAndIdentifier.GetOrAdd(responseType, key => new ConcurrentDictionary<ushort, TaskCompletionSource<MqttBasePacket>>());
byId.TryRemove(requestWithIdentifier.PacketIdentifier, out var _);
}
}
private void RemovePacketAwaiter(MqttPacketAwaiter packetAwaiter)
{
lock (_syncRoot)
else
{
_packetAwaiters.Remove(packetAwaiter);
}
}
private void DispatchPendingPackets()
{
List<MqttBasePacket> receivedPackets;
lock (_syncRoot)
{
receivedPackets = new List<MqttBasePacket>(_receivedPackets);
}
foreach (var pendingPacket in receivedPackets)
{
Dispatch(pendingPacket);
_packetByResponseType.TryRemove(responseType, out var _);
}
}
}
+33 -14
View File
@@ -6,39 +6,58 @@ namespace MQTTnet.Core.Diagnostics
{
public static event EventHandler<MqttTraceMessagePublishedEventArgs> TraceMessagePublished;
public static void Verbose(string source, string message)
public static void Verbose(string source, string message, params object[] parameters)
{
Publish(source, MqttTraceLevel.Verbose, null, message);
Publish(source, MqttTraceLevel.Verbose, null, message, parameters);
}
public static void Information(string source, string message)
public static void Information(string source, string message, params object[] parameters)
{
Publish(source, MqttTraceLevel.Information, null, message);
Publish(source, MqttTraceLevel.Information, null, message, parameters);
}
public static void Warning(string source, string message)
public static void Warning(string source, string message, params object[] parameters)
{
Publish(source, MqttTraceLevel.Warning, null, message);
Publish(source, MqttTraceLevel.Warning, null, message, parameters);
}
public static void Warning(string source, Exception exception, string message)
public static void Warning(string source, Exception exception, string message, params object[] parameters)
{
Publish(source, MqttTraceLevel.Warning, exception, message);
Publish(source, MqttTraceLevel.Warning, exception, message, parameters);
}
public static void Error(string source, string message)
public static void Error(string source, string message, params object[] parameters)
{
Publish(source, MqttTraceLevel.Error, null, message);
Publish(source, MqttTraceLevel.Error, null, message, parameters);
}
public static void Error(string source, Exception exception, string message)
public static void Error(string source, Exception exception, string message, params object[] parameters)
{
Publish(source, MqttTraceLevel.Error, exception, message);
Publish(source, MqttTraceLevel.Error, exception, message, parameters);
}
private static void Publish(string source, MqttTraceLevel traceLevel, Exception exception, string message)
private static void Publish(string source, MqttTraceLevel traceLevel, Exception exception, string message, params object[] parameters)
{
TraceMessagePublished?.Invoke(null, new MqttTraceMessagePublishedEventArgs(Environment.CurrentManagedThreadId, source, traceLevel, message, exception));
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 -1
View File
@@ -1,6 +1,6 @@
namespace MQTTnet.Core.Diagnostics
{
public enum MqttTraceLevel
public enum MqttTraceLevel
{
Verbose,
Information,
@@ -2,7 +2,7 @@
namespace MQTTnet.Core.Diagnostics
{
public class MqttTraceMessagePublishedEventArgs : EventArgs
public sealed class MqttTraceMessagePublishedEventArgs : EventArgs
{
public MqttTraceMessagePublishedEventArgs(int threadId, string source, MqttTraceLevel level, string message, Exception exception)
{
@@ -4,7 +4,7 @@ namespace MQTTnet.Core.Exceptions
{
public class MqttCommunicationException : Exception
{
public MqttCommunicationException()
protected MqttCommunicationException()
{
}
@@ -17,5 +17,10 @@ namespace MQTTnet.Core.Exceptions
: base(message)
{
}
public MqttCommunicationException(string message, Exception innerException)
: base(message, innerException)
{
}
}
}
@@ -1,6 +1,6 @@
namespace MQTTnet.Core.Exceptions
{
public class MqttCommunicationTimedOutException : MqttCommunicationException
public sealed class MqttCommunicationTimedOutException : MqttCommunicationException
{
}
}
@@ -2,9 +2,9 @@
namespace MQTTnet.Core.Exceptions
{
public class MqttProtocolViolationException : Exception
public sealed class MqttProtocolViolationException : Exception
{
public MqttProtocolViolationException(string message)
public MqttProtocolViolationException(string message)
: base(message)
{
}
@@ -1,32 +0,0 @@
using System.Collections.Generic;
using System.Threading.Tasks;
namespace MQTTnet.Core.Internal
{
public class AsyncGate
{
private readonly Queue<TaskCompletionSource<bool>> _waitingTasks = new Queue<TaskCompletionSource<bool>>();
public Task WaitOneAsync()
{
var tcs = new TaskCompletionSource<bool>();
lock (_waitingTasks)
{
_waitingTasks.Enqueue(tcs);
}
return tcs.Task;
}
public void Set()
{
lock (_waitingTasks)
{
if (_waitingTasks.Count > 0)
{
_waitingTasks.Dequeue().SetResult(true);
}
}
}
}
}
@@ -4,6 +4,16 @@ namespace MQTTnet.Core.Internal
{
internal static class MqttApplicationMessageExtensions
{
public static MqttApplicationMessage ToApplicationMessage(this MqttPublishPacket publishPacket)
{
return new MqttApplicationMessage(
publishPacket.Topic,
publishPacket.Payload,
publishPacket.QualityOfServiceLevel,
publishPacket.Retain
);
}
public static MqttPublishPacket ToPublishPacket(this MqttApplicationMessage applicationMessage)
{
if (applicationMessage == null)
+66 -3
View File
@@ -1,11 +1,74 @@
using System.Threading.Tasks;
using System;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Core.Exceptions;
namespace MQTTnet.Core.Internal
{
internal static class TaskExtensions
public static class TaskExtensions
{
public static void Forget(this Task task)
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();
}
}
}
}
}
+15 -14
View File
@@ -5,20 +5,21 @@
<AssemblyName>MQTTnet.Core</AssemblyName>
<RootNamespace>MQTTnet.Core</RootNamespace>
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
<Description>MQTTnet is a .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker).</Description>
<Product>MQTTnet</Product>
<Company>Christian Kratky</Company>
<Authors>Christian Kratky</Authors>
<Version>2.1.1.0</Version>
<PackageId>MQTTnet.Core</PackageId>
<Copyright>Copyright © Christian Kratky 2016-2017</Copyright>
<PackageProjectUrl>https://github.com/chkr1011/MQTTnet</PackageProjectUrl>
<PackageIconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</PackageIconUrl>
<RepositoryUrl>https://github.com/chkr1011/MQTTnet</RepositoryUrl>
<PackageTags>MQTT MQTTClient MQTTServer MQTTBroker Broker</PackageTags>
<FileVersion>2.1.1.0</FileVersion>
<AssemblyVersion>2.1.1.0</AssemblyVersion>
<PackageLicenseUrl>https://github.com/chkr1011/MQTTnet/blob/master/LICENSE</PackageLicenseUrl>
<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'" />
+1 -1
View File
@@ -3,7 +3,7 @@ using MQTTnet.Core.Protocol;
namespace MQTTnet.Core
{
public class MqttApplicationMessage
public sealed class MqttApplicationMessage
{
public MqttApplicationMessage(string topic, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel, bool retain)
{
@@ -0,0 +1,58 @@
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,6 +1,6 @@
namespace MQTTnet.Core.Packets
{
public interface IPacketWithIdentifier
public interface IMqttPacketWithIdentifier
{
ushort PacketIdentifier { get; set; }
}
+1 -20
View File
@@ -1,25 +1,6 @@
using System;
namespace MQTTnet.Core.Packets
namespace MQTTnet.Core.Packets
{
public abstract class MqttBasePacket
{
public TResponsePacket CreateResponse<TResponsePacket>()
{
var responsePacket = Activator.CreateInstance<TResponsePacket>();
var responsePacketWithIdentifier = responsePacket as IPacketWithIdentifier;
if (responsePacketWithIdentifier != null)
{
var requestPacketWithIdentifier = this as IPacketWithIdentifier;
if (requestPacketWithIdentifier == null)
{
throw new InvalidOperationException("Response packet has PacketIdentifier but request packet does not.");
}
responsePacketWithIdentifier.PacketIdentifier = requestPacketWithIdentifier.PacketIdentifier;
}
return responsePacket;
}
}
}
@@ -1,6 +1,6 @@
namespace MQTTnet.Core.Packets
{
public class MqttBasePublishPacket : MqttBasePacket, IPacketWithIdentifier
public class MqttBasePublishPacket : MqttBasePacket, IMqttPacketWithIdentifier
{
public ushort PacketIdentifier { get; set; }
}
+2 -2
View File
@@ -2,7 +2,7 @@
namespace MQTTnet.Core.Packets
{
public class MqttConnAckPacket : MqttBasePacket
public sealed class MqttConnAckPacket : MqttBasePacket
{
public bool IsSessionPresent { get; set; }
@@ -10,7 +10,7 @@ namespace MQTTnet.Core.Packets
public override string ToString()
{
return $"{nameof(MqttConnAckPacket)}: [ConnectReturnCode={ConnectReturnCode}] [IsSessionPresent={IsSessionPresent}]";
return nameof(MqttConnAckPacket) + ": [ConnectReturnCode=" + ConnectReturnCode + "] [IsSessionPresent=" + IsSessionPresent + "]";
}
}
}
+7 -3
View File
@@ -1,7 +1,11 @@
namespace MQTTnet.Core.Packets
using MQTTnet.Core.Serializer;
namespace MQTTnet.Core.Packets
{
public class MqttConnectPacket: MqttBasePacket
public sealed class MqttConnectPacket : MqttBasePacket
{
public MqttProtocolVersion ProtocolVersion { get; set; }
public string ClientId { get; set; }
public string Username { get; set; }
@@ -16,7 +20,7 @@
public override string ToString()
{
return $"{nameof(MqttConnectPacket)} [ClientId={ClientId}] [Username={Username}] [Password={Password}] [KeepAlivePeriod={KeepAlivePeriod}] [CleanSession={CleanSession}]";
return nameof(MqttConnectPacket) + ": [ClientId=" + ClientId + "] [Username=" + Username + "] [Password=" + Password + "] [KeepAlivePeriod=" + KeepAlivePeriod + "] [CleanSession=" + CleanSession + "]";
}
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
namespace MQTTnet.Core.Packets
{
public class MqttDisconnectPacket : MqttBasePacket
public sealed class MqttDisconnectPacket : MqttBasePacket
{
}
}
@@ -0,0 +1,27 @@
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;
}
}
}
+13
View File
@@ -0,0 +1,13 @@
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 -1
View File
@@ -1,6 +1,6 @@
namespace MQTTnet.Core.Packets
{
public class MqttPingReqPacket : MqttBasePacket
public sealed class MqttPingReqPacket : MqttBasePacket
{
public override string ToString()
{
+1 -1
View File
@@ -1,6 +1,6 @@
namespace MQTTnet.Core.Packets
{
public class MqttPingRespPacket : MqttBasePacket
public sealed class MqttPingRespPacket : MqttBasePacket
{
public override string ToString()
{
+1 -1
View File
@@ -1,6 +1,6 @@
namespace MQTTnet.Core.Packets
{
public class MqttPubAckPacket : MqttBasePublishPacket
public sealed class MqttPubAckPacket : MqttBasePublishPacket
{
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
namespace MQTTnet.Core.Packets
{
public class MqttPubCompPacket : MqttBasePublishPacket
public sealed class MqttPubCompPacket : MqttBasePublishPacket
{
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
namespace MQTTnet.Core.Packets
{
public class MqttPubRecPacket : MqttBasePublishPacket
public sealed class MqttPubRecPacket : MqttBasePublishPacket
{
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
namespace MQTTnet.Core.Packets
{
public class MqttPubRelPacket : MqttBasePublishPacket
public sealed class MqttPubRelPacket : MqttBasePublishPacket
{
}
}
+9 -4
View File
@@ -1,9 +1,9 @@
using System.Text;
using System;
using MQTTnet.Core.Protocol;
namespace MQTTnet.Core.Packets
{
public class MqttPublishPacket : MqttBasePublishPacket
public sealed class MqttPublishPacket : MqttBasePublishPacket
{
public bool Retain { get; set; }
@@ -17,8 +17,13 @@ namespace MQTTnet.Core.Packets
public override string ToString()
{
return
$"{nameof(MqttPublishPacket)} [Topic={Topic}] [Payload={Encoding.UTF8.GetString(Payload, 0, Payload.Length)}] [QoSLevel={QualityOfServiceLevel}] [Dup={Dup}] [Retain={Retain}] [PacketIdentifier={PacketIdentifier}]";
return nameof(MqttPublishPacket) +
": [Topic=" + Topic + "]" +
" [Payload=" + Convert.ToBase64String(Payload) + "]" +
" [QoSLevel=" + QualityOfServiceLevel + "]" +
" [Dup=" + Dup + "]" +
" [Retain=" + Retain + "]" +
" [PacketIdentifier=" + PacketIdentifier + "]";
}
}
}
+2 -3
View File
@@ -4,7 +4,7 @@ using MQTTnet.Core.Protocol;
namespace MQTTnet.Core.Packets
{
public class MqttSubAckPacket : MqttBasePacket, IPacketWithIdentifier
public sealed class MqttSubAckPacket : MqttBasePacket, IMqttPacketWithIdentifier
{
public ushort PacketIdentifier { get; set; }
@@ -13,8 +13,7 @@ namespace MQTTnet.Core.Packets
public override string ToString()
{
var subscribeReturnCodesText = string.Join(",", SubscribeReturnCodes.Select(f => f.ToString()));
return
$"{nameof(MqttSubAckPacket)} [PacketIdentifier={PacketIdentifier}] [SubscribeReturnCodes={subscribeReturnCodesText}]";
return nameof(MqttSubAckPacket) + ": [PacketIdentifier=" + PacketIdentifier + "] [SubscribeReturnCodes=" + subscribeReturnCodesText + "]";
}
}
}
+3 -4
View File
@@ -3,17 +3,16 @@ using System.Linq;
namespace MQTTnet.Core.Packets
{
public class MqttSubscribePacket : MqttBasePacket, IPacketWithIdentifier
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}]";
return nameof(MqttSubscribePacket) + ": [PacketIdentifier=" + PacketIdentifier + "] [TopicFilters=" + topicFiltersText + "]";
}
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
namespace MQTTnet.Core.Packets
{
public class MqttUnsubAckPacket : MqttBasePacket, IPacketWithIdentifier
public sealed class MqttUnsubAckPacket : MqttBasePacket, IMqttPacketWithIdentifier
{
public ushort PacketIdentifier { get; set; }
}
+2 -2
View File
@@ -2,10 +2,10 @@
namespace MQTTnet.Core.Packets
{
public class MqttUnsubscribePacket : MqttBasePacket, IPacketWithIdentifier
public sealed class MqttUnsubscribePacket : MqttBasePacket, IMqttPacketWithIdentifier
{
public ushort PacketIdentifier { get; set; }
public IList<string> TopicFilters { get; set; } = new List<string>();
}
}
+1 -1
View File
@@ -2,7 +2,7 @@
namespace MQTTnet.Core.Packets
{
public class TopicFilter
public sealed class TopicFilter
{
public TopicFilter(string topic, MqttQualityOfServiceLevel qualityOfServiceLevel)
{
+3 -3
View File
@@ -2,7 +2,7 @@
namespace MQTTnet.Core.Serializer
{
public class ByteReader
public sealed class ByteReader
{
private readonly int _source;
private int _index;
@@ -24,7 +24,7 @@ namespace MQTTnet.Core.Serializer
return result;
}
public byte Read(int count)
public int Read(int count)
{
if (_index + count > 8)
{
@@ -42,7 +42,7 @@ namespace MQTTnet.Core.Serializer
_index++;
}
return (byte)result;
return result;
}
}
}
+1 -1
View File
@@ -2,7 +2,7 @@
namespace MQTTnet.Core.Serializer
{
public class ByteWriter
public sealed class ByteWriter
{
private int _index;
private int _byte;
@@ -1,616 +0,0 @@
using System;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using MQTTnet.Core.Channel;
using MQTTnet.Core.Exceptions;
using MQTTnet.Core.Packets;
using MQTTnet.Core.Protocol;
namespace MQTTnet.Core.Serializer
{
public class DefaultMqttV311PacketSerializer : IMqttPacketSerializer
{
public Task SerializeAsync(MqttBasePacket packet, IMqttCommunicationChannel destination)
{
if (packet == null) throw new ArgumentNullException(nameof(packet));
if (destination == null) throw new ArgumentNullException(nameof(destination));
var connectPacket = packet as MqttConnectPacket;
if (connectPacket != null)
{
return SerializeAsync(connectPacket, destination);
}
var connAckPacket = packet as MqttConnAckPacket;
if (connAckPacket != null)
{
return SerializeAsync(connAckPacket, destination);
}
var disconnectPacket = packet as MqttDisconnectPacket;
if (disconnectPacket != null)
{
return SerializeAsync(disconnectPacket, destination);
}
var pingReqPacket = packet as MqttPingReqPacket;
if (pingReqPacket != null)
{
return SerializeAsync(pingReqPacket, destination);
}
var pingRespPacket = packet as MqttPingRespPacket;
if (pingRespPacket != null)
{
return SerializeAsync(pingRespPacket, destination);
}
var publishPacket = packet as MqttPublishPacket;
if (publishPacket != null)
{
return SerializeAsync(publishPacket, destination);
}
var pubAckPacket = packet as MqttPubAckPacket;
if (pubAckPacket != null)
{
return SerializeAsync(pubAckPacket, destination);
}
var pubRecPacket = packet as MqttPubRecPacket;
if (pubRecPacket != null)
{
return SerializeAsync(pubRecPacket, destination);
}
var pubRelPacket = packet as MqttPubRelPacket;
if (pubRelPacket != null)
{
return SerializeAsync(pubRelPacket, destination);
}
var pubCompPacket = packet as MqttPubCompPacket;
if (pubCompPacket != null)
{
return SerializeAsync(pubCompPacket, destination);
}
var subscribePacket = packet as MqttSubscribePacket;
if (subscribePacket != null)
{
return SerializeAsync(subscribePacket, destination);
}
var subAckPacket = packet as MqttSubAckPacket;
if (subAckPacket != null)
{
return SerializeAsync(subAckPacket, destination);
}
var unsubscribePacket = packet as MqttUnsubscribePacket;
if (unsubscribePacket != null)
{
return SerializeAsync(unsubscribePacket, destination);
}
var unsubAckPacket = packet as MqttUnsubAckPacket;
if (unsubAckPacket != null)
{
return SerializeAsync(unsubAckPacket, destination);
}
throw new MqttProtocolViolationException("Packet type invalid.");
}
public async Task<MqttBasePacket> DeserializeAsync(IMqttCommunicationChannel source)
{
if (source == null) throw new ArgumentNullException(nameof(source));
using (var mqttPacketReader = new MqttPacketReader(source))
{
await mqttPacketReader.ReadToEndAsync();
switch (mqttPacketReader.ControlPacketType)
{
case MqttControlPacketType.Connect:
{
return await DeserializeConnectAsync(mqttPacketReader);
}
case MqttControlPacketType.ConnAck:
{
return await DeserializeConnAck(mqttPacketReader);
}
case MqttControlPacketType.Disconnect:
{
return new MqttDisconnectPacket();
}
case MqttControlPacketType.Publish:
{
return await DeserializePublishAsync(mqttPacketReader);
}
case MqttControlPacketType.PubAck:
{
return new MqttPubAckPacket
{
PacketIdentifier = await mqttPacketReader.ReadRemainingDataUShortAsync()
};
}
case MqttControlPacketType.PubRec:
{
return new MqttPubRecPacket
{
PacketIdentifier = await mqttPacketReader.ReadRemainingDataUShortAsync()
};
}
case MqttControlPacketType.PubRel:
{
return new MqttPubRelPacket
{
PacketIdentifier = await mqttPacketReader.ReadRemainingDataUShortAsync()
};
}
case MqttControlPacketType.PubComp:
{
return new MqttPubCompPacket
{
PacketIdentifier = await mqttPacketReader.ReadRemainingDataUShortAsync()
};
}
case MqttControlPacketType.PingReq:
{
return new MqttPingReqPacket();
}
case MqttControlPacketType.PingResp:
{
return new MqttPingRespPacket();
}
case MqttControlPacketType.Subscribe:
{
return await DeserializeSubscribeAsync(mqttPacketReader);
}
case MqttControlPacketType.SubAck:
{
return await DeserializeSubAck(mqttPacketReader);
}
case MqttControlPacketType.Unsubscibe:
{
return await DeserializeUnsubscribeAsync(mqttPacketReader);
}
case MqttControlPacketType.UnsubAck:
{
return new MqttUnsubAckPacket
{
PacketIdentifier = await mqttPacketReader.ReadRemainingDataUShortAsync()
};
}
default:
{
throw new MqttProtocolViolationException("Packet type not supported.");
}
}
}
}
private async Task<MqttBasePacket> DeserializeUnsubscribeAsync(MqttPacketReader reader)
{
var packet = new MqttUnsubscribePacket
{
PacketIdentifier = await reader.ReadRemainingDataUShortAsync(),
};
while (!reader.EndOfRemainingData)
{
packet.TopicFilters.Add(await reader.ReadRemainingDataStringWithLengthPrefixAsync());
}
return packet;
}
private async Task<MqttBasePacket> DeserializeSubscribeAsync(MqttPacketReader reader)
{
var packet = new MqttSubscribePacket
{
PacketIdentifier = await reader.ReadRemainingDataUShortAsync(),
};
while (!reader.EndOfRemainingData)
{
packet.TopicFilters.Add(new TopicFilter(
await reader.ReadRemainingDataStringWithLengthPrefixAsync(),
(MqttQualityOfServiceLevel)await reader.ReadRemainingDataByteAsync()));
}
return packet;
}
private async Task<MqttBasePacket> DeserializePublishAsync(MqttPacketReader reader)
{
var fixedHeader = new ByteReader(reader.FixedHeader);
var retain = fixedHeader.Read();
var qualityOfServiceLevel = (MqttQualityOfServiceLevel)fixedHeader.Read(2);
var dup = fixedHeader.Read();
var topic = await reader.ReadRemainingDataStringWithLengthPrefixAsync();
ushort packetIdentifier = 0;
if (qualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce)
{
packetIdentifier = await reader.ReadRemainingDataUShortAsync();
}
var packet = new MqttPublishPacket
{
Retain = retain,
QualityOfServiceLevel = qualityOfServiceLevel,
Dup = dup,
Topic = topic,
Payload = await reader.ReadRemainingDataAsync(),
PacketIdentifier = packetIdentifier
};
return packet;
}
private async Task<MqttBasePacket> DeserializeConnectAsync(MqttPacketReader reader)
{
var packet = new MqttConnectPacket();
await reader.ReadRemainingDataByteAsync();
await reader.ReadRemainingDataByteAsync();
var protocolName = await reader.ReadRemainingDataAsync(4);
if (Encoding.UTF8.GetString(protocolName, 0, protocolName.Length) != "MQTT")
{
throw new MqttProtocolViolationException("Protocol name is not 'MQTT'.");
}
var protocolLevel = await reader.ReadRemainingDataByteAsync();
var connectFlags = await reader.ReadRemainingDataByteAsync();
var connectFlagsReader = new ByteReader(connectFlags);
connectFlagsReader.Read(); // Reserved.
packet.CleanSession = connectFlagsReader.Read();
var willFlag = connectFlagsReader.Read();
var willQoS = connectFlagsReader.Read(2);
var willRetain = connectFlagsReader.Read();
var passwordFlag = connectFlagsReader.Read();
var usernameFlag = connectFlagsReader.Read();
packet.KeepAlivePeriod = await reader.ReadRemainingDataUShortAsync();
packet.ClientId = await reader.ReadRemainingDataStringWithLengthPrefixAsync();
if (willFlag)
{
packet.WillMessage = new MqttApplicationMessage(
await reader.ReadRemainingDataStringWithLengthPrefixAsync(),
await reader.ReadRemainingDataWithLengthPrefixAsync(),
(MqttQualityOfServiceLevel)willQoS,
willRetain);
}
if (usernameFlag)
{
packet.Username = await reader.ReadRemainingDataStringWithLengthPrefixAsync();
}
if (passwordFlag)
{
packet.Password = await reader.ReadRemainingDataStringWithLengthPrefixAsync();
}
ValidateConnectPacket(packet);
return packet;
}
private async Task<MqttBasePacket> DeserializeSubAck(MqttPacketReader reader)
{
var packet = new MqttSubAckPacket
{
PacketIdentifier = await reader.ReadRemainingDataUShortAsync()
};
while (!reader.EndOfRemainingData)
{
packet.SubscribeReturnCodes.Add((MqttSubscribeReturnCode)await reader.ReadRemainingDataByteAsync());
}
return packet;
}
private async Task<MqttBasePacket> DeserializeConnAck(MqttPacketReader reader)
{
var variableHeader1 = await reader.ReadRemainingDataByteAsync();
var variableHeader2 = await reader.ReadRemainingDataByteAsync();
var packet = new MqttConnAckPacket
{
IsSessionPresent = new ByteReader(variableHeader1).Read(),
ConnectReturnCode = (MqttConnectReturnCode)variableHeader2
};
return packet;
}
private void ValidateConnectPacket(MqttConnectPacket packet)
{
if (string.IsNullOrEmpty(packet.ClientId) && !packet.CleanSession)
{
throw new MqttProtocolViolationException("CleanSession must be set if ClientId is empty [MQTT-3.1.3-7].");
}
if (!string.IsNullOrEmpty(packet.ClientId) && !Regex.IsMatch(packet.ClientId, "^[a-zA-Z0-9]*$"))
{
throw new MqttProtocolViolationException("ClientId contains invalid characters [MQTT-3.1.3-5].");
}
}
private 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 static readonly byte[] MqttPrefix = Encoding.UTF8.GetBytes("MQTT");
private Task SerializeAsync(MqttConnectPacket packet, IMqttCommunicationChannel destination)
{
ValidateConnectPacket(packet);
using (var output = new MqttPacketWriter())
{
// Write variable header
output.Write(0x00, 0x04); // 3.1.2.1 Protocol Name
output.Write(MqttPrefix);
output.Write(0x04); // 3.1.2.2 Protocol Level
var connectFlags = new ByteWriter(); // 3.1.2.3 Connect Flags
connectFlags.Write(false); // Reserved
connectFlags.Write(packet.CleanSession);
connectFlags.Write(packet.WillMessage != null);
if (packet.WillMessage != null)
{
connectFlags.Write((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);
output.Write(connectFlags);
output.Write(packet.KeepAlivePeriod);
output.WriteWithLengthPrefix(packet.ClientId);
if (packet.WillMessage != null)
{
output.WriteWithLengthPrefix(packet.WillMessage.Topic);
output.WriteWithLengthPrefix(packet.WillMessage.Payload);
}
if (packet.Username != null)
{
output.WriteWithLengthPrefix(packet.Username);
}
if (packet.Password != null)
{
output.WriteWithLengthPrefix(packet.Password);
}
output.InjectFixedHeader(MqttControlPacketType.Connect);
return output.WriteToAsync(destination);
}
}
private Task SerializeAsync(MqttConnAckPacket packet, IMqttCommunicationChannel destination)
{
using (var output = new MqttPacketWriter())
{
var connectAcknowledgeFlags = new ByteWriter();
connectAcknowledgeFlags.Write(packet.IsSessionPresent);
output.Write(connectAcknowledgeFlags);
output.Write((byte)packet.ConnectReturnCode);
output.InjectFixedHeader(MqttControlPacketType.ConnAck);
return output.WriteToAsync(destination);
}
}
private Task SerializeAsync(MqttDisconnectPacket packet, IMqttCommunicationChannel destination)
{
return SerializeEmptyPacketAsync(MqttControlPacketType.Disconnect, destination);
}
private Task SerializeAsync(MqttPingReqPacket packet, IMqttCommunicationChannel destination)
{
return SerializeEmptyPacketAsync(MqttControlPacketType.PingReq, destination);
}
private Task SerializeAsync(MqttPingRespPacket packet, IMqttCommunicationChannel destination)
{
return SerializeEmptyPacketAsync(MqttControlPacketType.PingResp, destination);
}
private Task SerializeAsync(MqttPublishPacket packet, IMqttCommunicationChannel destination)
{
ValidatePublishPacket(packet);
using (var output = new MqttPacketWriter())
{
output.WriteWithLengthPrefix(packet.Topic);
if (packet.QualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce)
{
output.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)
{
output.Write(packet.Payload);
}
var fixedHeader = new ByteWriter();
fixedHeader.Write(packet.Retain);
fixedHeader.Write((byte)packet.QualityOfServiceLevel, 2);
fixedHeader.Write(packet.Dup);
output.InjectFixedHeader(MqttControlPacketType.Publish, fixedHeader.Value);
return output.WriteToAsync(destination);
}
}
private Task SerializeAsync(MqttPubAckPacket packet, IMqttCommunicationChannel destination)
{
using (var output = new MqttPacketWriter())
{
output.Write(packet.PacketIdentifier);
output.InjectFixedHeader(MqttControlPacketType.PubAck);
return output.WriteToAsync(destination);
}
}
private Task SerializeAsync(MqttPubRecPacket packet, IMqttCommunicationChannel destination)
{
using (var output = new MqttPacketWriter())
{
output.Write(packet.PacketIdentifier);
output.InjectFixedHeader(MqttControlPacketType.PubRec);
return output.WriteToAsync(destination);
}
}
private async Task SerializeAsync(MqttPubRelPacket packet, IMqttCommunicationChannel destination)
{
using (var output = new MqttPacketWriter())
{
output.Write(packet.PacketIdentifier);
output.InjectFixedHeader(MqttControlPacketType.PubRel, 0x02);
await output.WriteToAsync(destination);
}
}
private Task SerializeAsync(MqttPubCompPacket packet, IMqttCommunicationChannel destination)
{
using (var output = new MqttPacketWriter())
{
output.Write(packet.PacketIdentifier);
output.InjectFixedHeader(MqttControlPacketType.PubComp);
return output.WriteToAsync(destination);
}
}
private Task SerializeAsync(MqttSubscribePacket packet, IMqttCommunicationChannel destination)
{
using (var output = new MqttPacketWriter())
{
output.Write(packet.PacketIdentifier);
if (packet.TopicFilters?.Count > 0)
{
foreach (var topicFilter in packet.TopicFilters)
{
output.WriteWithLengthPrefix(topicFilter.Topic);
output.Write((byte)topicFilter.QualityOfServiceLevel);
}
}
output.InjectFixedHeader(MqttControlPacketType.Subscribe, 0x02);
return output.WriteToAsync(destination);
}
}
private Task SerializeAsync(MqttSubAckPacket packet, IMqttCommunicationChannel destination)
{
using (var output = new MqttPacketWriter())
{
output.Write(packet.PacketIdentifier);
if (packet.SubscribeReturnCodes?.Any() == true)
{
foreach (var packetSubscribeReturnCode in packet.SubscribeReturnCodes)
{
output.Write((byte)packetSubscribeReturnCode);
}
}
output.InjectFixedHeader(MqttControlPacketType.SubAck);
return output.WriteToAsync(destination);
}
}
private Task SerializeAsync(MqttUnsubscribePacket packet, IMqttCommunicationChannel destination)
{
using (var output = new MqttPacketWriter())
{
output.Write(packet.PacketIdentifier);
if (packet.TopicFilters?.Any() == true)
{
foreach (var topicFilter in packet.TopicFilters)
{
output.WriteWithLengthPrefix(topicFilter);
}
}
output.InjectFixedHeader(MqttControlPacketType.Unsubscibe, 0x02);
return output.WriteToAsync(destination);
}
}
private Task SerializeAsync(MqttUnsubAckPacket packet, IMqttCommunicationChannel destination)
{
using (var output = new MqttPacketWriter())
{
output.Write(packet.PacketIdentifier);
output.InjectFixedHeader(MqttControlPacketType.UnsubAck);
return output.WriteToAsync(destination);
}
}
private Task SerializeEmptyPacketAsync(MqttControlPacketType type, IMqttCommunicationChannel destination)
{
using (var output = new MqttPacketWriter())
{
output.InjectFixedHeader(type);
return output.WriteToAsync(destination);
}
}
}
}
@@ -1,13 +1,14 @@
using System.Threading.Tasks;
using MQTTnet.Core.Channel;
using MQTTnet.Core.Adapter;
using MQTTnet.Core.Packets;
namespace MQTTnet.Core.Serializer
{
public interface IMqttPacketSerializer
{
Task SerializeAsync(MqttBasePacket mqttPacket, IMqttCommunicationChannel destination);
MqttProtocolVersion ProtocolVersion { get; set; }
Task<MqttBasePacket> DeserializeAsync(IMqttCommunicationChannel source);
byte[] Serialize(MqttBasePacket mqttPacket);
MqttBasePacket Deserialize(ReceivedMqttPacket receivedMqttPacket);
}
}
+48 -85
View File
@@ -1,92 +1,44 @@
using System;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Core.Channel;
using MQTTnet.Core.Adapter;
using MQTTnet.Core.Exceptions;
using MQTTnet.Core.Protocol;
using MQTTnet.Core.Packets;
namespace MQTTnet.Core.Serializer
{
public sealed class MqttPacketReader : IDisposable
public sealed class MqttPacketReader : BinaryReader
{
private readonly MemoryStream _remainingData = new MemoryStream();
private readonly IMqttCommunicationChannel _source;
public MqttPacketReader(IMqttCommunicationChannel source)
private readonly ReceivedMqttPacket _receivedMqttPacket;
public MqttPacketReader(ReceivedMqttPacket receivedMqttPacket)
: base(receivedMqttPacket.Body, Encoding.UTF8, true)
{
_source = source ?? throw new ArgumentNullException(nameof(source));
_receivedMqttPacket = receivedMqttPacket;
}
public MqttControlPacketType ControlPacketType { get; private set; }
public bool EndOfRemainingData => BaseStream.Position == _receivedMqttPacket.Header.BodyLength;
public byte FixedHeader { get; private set; }
public int RemainingLength { get; private set; }
public bool EndOfRemainingData => _remainingData.Position == _remainingData.Length;
public async Task ReadToEndAsync()
public static MqttPacketHeader ReadHeaderFromSource(Stream stream, CancellationToken cancellationToken)
{
await ReadFixedHeaderAsync();
await ReadRemainingLengthAsync();
var fixedHeader = (byte)stream.ReadByte();
var controlPacketType = (MqttControlPacketType)(fixedHeader >> 4);
var bodyLength = ReadBodyLengthFromSource(stream, cancellationToken);
if (RemainingLength == 0)
return new MqttPacketHeader
{
return;
}
var buffer = new byte[RemainingLength];
await _source.ReadAsync(buffer);
_remainingData.Write(buffer, 0, buffer.Length);
_remainingData.Position = 0;
FixedHeader = fixedHeader,
ControlPacketType = controlPacketType,
BodyLength = bodyLength
};
}
private async Task ReadFixedHeaderAsync()
public override ushort ReadUInt16()
{
FixedHeader = await ReadStreamByteAsync();
var byteReader = new ByteReader(FixedHeader);
byteReader.Read(4);
ControlPacketType = (MqttControlPacketType)byteReader.Read(4);
}
private async Task<byte> ReadStreamByteAsync()
{
var buffer = new byte[1];
await _source.ReadAsync(buffer);
return buffer[0];
}
private async Task ReadRemainingLengthAsync()
{
// Alorithm taken from http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html.
var multiplier = 1;
var value = 0;
byte encodedByte;
do
{
encodedByte = await ReadStreamByteAsync();
value += (encodedByte & 127) * multiplier;
multiplier *= 128;
if (multiplier > 128 * 128 * 128)
{
throw new MqttProtocolViolationException("Remaining length is ivalid.");
}
} while ((encodedByte & 128) != 0);
RemainingLength = value;
}
public async Task<byte> ReadRemainingDataByteAsync()
{
return (await ReadRemainingDataAsync(1))[0];
}
public async Task<ushort> ReadRemainingDataUShortAsync()
{
var buffer = await ReadRemainingDataAsync(2);
var buffer = ReadBytes(2);
var temp = buffer[0];
buffer[0] = buffer[1];
@@ -95,34 +47,45 @@ namespace MQTTnet.Core.Serializer
return BitConverter.ToUInt16(buffer, 0);
}
public async Task<string> ReadRemainingDataStringWithLengthPrefixAsync()
public string ReadStringWithLengthPrefix()
{
var buffer = await ReadRemainingDataWithLengthPrefixAsync();
var buffer = ReadWithLengthPrefix();
return Encoding.UTF8.GetString(buffer, 0, buffer.Length);
}
public async Task<byte[]> ReadRemainingDataWithLengthPrefixAsync()
public byte[] ReadWithLengthPrefix()
{
var length = await ReadRemainingDataUShortAsync();
return await ReadRemainingDataAsync(length);
var length = ReadUInt16();
return ReadBytes(length);
}
public Task<byte[]> ReadRemainingDataAsync()
public byte[] ReadRemainingData()
{
return ReadRemainingDataAsync(RemainingLength - (int)_remainingData.Position);
return ReadBytes(_receivedMqttPacket.Header.BodyLength - (int)BaseStream.Position);
}
public async Task<byte[]> ReadRemainingDataAsync(int length)
private static int ReadBodyLengthFromSource(Stream stream, CancellationToken cancellationToken)
{
var buffer = new byte[length];
await _remainingData.ReadAsync(buffer, 0, buffer.Length);
// 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();
}
return buffer;
}
public void Dispose()
{
_remainingData?.Dispose();
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;
}
}
}
@@ -0,0 +1,609 @@
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);
}
}
}
+38 -61
View File
@@ -1,80 +1,46 @@
using System;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using MQTTnet.Core.Channel;
using MQTTnet.Core.Protocol;
using System.Collections.Generic;
namespace MQTTnet.Core.Serializer
{
public sealed class MqttPacketWriter : IDisposable
public sealed class MqttPacketWriter : BinaryWriter
{
private readonly MemoryStream _buffer = new MemoryStream(512);
public void InjectFixedHeader(byte fixedHeader)
public MqttPacketWriter(Stream stream)
: base(stream)
{
if (_buffer.Length == 0)
{
Write(fixedHeader);
Write(0);
return;
}
var backupBuffer = _buffer.ToArray();
var remainingLength = (int)_buffer.Length;
_buffer.SetLength(0);
_buffer.WriteByte(fixedHeader);
// Alorithm taken from http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html.
var x = remainingLength;
do
{
var encodedByte = x % 128;
x = x / 128;
if (x > 0)
{
encodedByte = encodedByte | 128;
}
_buffer.WriteByte((byte)encodedByte);
} while (x > 0);
_buffer.Write(backupBuffer, 0, backupBuffer.Length);
}
public void InjectFixedHeader(MqttControlPacketType packetType, byte flags = 0)
public static byte BuildFixedHeader(MqttControlPacketType packetType, byte flags = 0)
{
var fixedHeader = (byte)((byte)packetType << 4);
var fixedHeader = (int)packetType << 4;
fixedHeader |= flags;
InjectFixedHeader(fixedHeader);
return (byte)fixedHeader;
}
public void Write(byte value)
{
_buffer.WriteByte(value);
}
public void Write(ushort value)
public override void Write(ushort value)
{
var buffer = BitConverter.GetBytes(value);
_buffer.WriteByte(buffer[1]);
_buffer.WriteByte(buffer[0]);
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));
_buffer.WriteByte(value.Value);
}
public void Write(params byte[] value)
{
if (value == null) throw new ArgumentNullException(nameof(value));
_buffer.Write(value, 0, value.Length);
Write(value.Value);
}
public void WriteWithLengthPrefix(string value)
@@ -90,16 +56,27 @@ namespace MQTTnet.Core.Serializer
Write(value);
}
public Task WriteToAsync(IMqttCommunicationChannel destination)
public static void BuildLengthHeader(int length, List<byte> header)
{
if (destination == null) throw new ArgumentNullException(nameof(destination));
if (length == 0)
{
header.Add(0);
return;
}
return destination.WriteAsync(_buffer.ToArray());
}
// 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;
}
public void Dispose()
{
_buffer?.Dispose();
header.Add((byte)encodedByte);
} while (x > 0);
}
}
}
@@ -0,0 +1,8 @@
namespace MQTTnet.Core.Serializer
{
public enum MqttProtocolVersion
{
V311,
V310
}
}
@@ -0,0 +1,11 @@
using MQTTnet.Core.Serializer;
namespace MQTTnet.Core.Server
{
public class ConnectedMqttClient
{
public string ClientId { get; set; }
public MqttProtocolVersion ProtocolVersion { get; set; }
}
}
@@ -0,0 +1,9 @@
namespace MQTTnet.Core.Server
{
public sealed class GetOrCreateClientSessionResult
{
public bool IsExistingSession { get; set; }
public MqttClientSession Session { get; set; }
}
}
+18
View File
@@ -0,0 +1,18 @@
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();
}
}
@@ -0,0 +1,7 @@
namespace MQTTnet.Core.Server
{
public interface IMqttServerFactory
{
IMqttServer CreateMqttServer(MqttServerOptions options);
}
}
@@ -0,0 +1,17 @@
using System;
namespace MQTTnet.Core.Server
{
public sealed class MqttApplicationMessageReceivedEventArgs : EventArgs
{
public MqttApplicationMessageReceivedEventArgs(string clientId, MqttApplicationMessage applicationMessage)
{
ClientId = clientId;
ApplicationMessage = applicationMessage ?? throw new ArgumentNullException(nameof(applicationMessage));
}
public string ClientId { get; }
public MqttApplicationMessage ApplicationMessage { get; }
}
}

Some files were not shown because too many files have changed in this diff Show More