Compare commits

...
348 Commits
Author SHA1 Message Date
Christian Kratky 0bdb7e9e44 Merge branch 'develop' 2018-07-08 14:02:35 +02:00
Christian Kratky 54e3cd7cb7 Update nuspecs. 2018-07-08 14:01:47 +02:00
Christian Kratky 16ad07cfc9 Add wrapper for WebSocket4Net. 2018-07-08 10:34:46 +02:00
Christian Kratky dee0adcd8c Add new overloads. 2018-07-07 22:46:07 +02:00
Christian Kratky acbf1226ab Fix a bug in the keep alive monitor. 2018-07-07 21:22:08 +02:00
Christian Kratky f002744d87 Merge branch 'develop' of https://github.com/chkr1011/MQTTnet into develop 2018-07-07 21:03:36 +02:00
Christian Kratky 9efee7af21 Refactoring. 2018-07-07 21:03:31 +02:00
Christian 9814b10496 Merge pull request #355 from JanEggers/ConnectionHandlerWithWebsockets
Connection handler with websockets
2018-07-07 21:00:32 +02:00
JanEggers 978694b3c0 set binary mode 2018-07-07 10:52:24 +02:00
JanEggers bc6f155f12 using connectionhandlers with websockets 2018-07-07 10:42:33 +02:00
Christian Kratky d494c2104b Merge branch 'develop' of https://github.com/chkr1011/MQTTnet into develop 2018-07-05 21:20:14 +02:00
Christian Kratky 8035c6859f Update docs. 2018-07-05 21:20:08 +02:00
Christian Kratky 2f10295022 Fix message handling for UWP. 2018-07-05 21:20:01 +02:00
Christian e5f32730a7 Merge pull request #353 from JanEggers/hideoverloadthatdoesnotprovideoptions
hide overload that does not provide options
2018-07-05 21:13:35 +02:00
JanEggers cd257b4d25 hide overload that does not provide options 2018-07-05 20:31:01 +02:00
Christian Kratky ce534baa75 Move the connection check interval of the ManagedClient to the options. 2018-07-04 22:20:24 +02:00
Christian Kratky 2b420b8e66 Add support for disabling server endpoints (ipv4 or ipv6) 2018-07-04 22:20:02 +02:00
Christian Kratky 35300abfb5 Fix typo. 2018-07-04 22:03:36 +02:00
Christian Kratky 3d0ecc05ff Fix bug which prevents receiving large packets for UWP. 2018-07-04 22:03:19 +02:00
Christian Kratky bfb6ec8856 Update UWP test app. 2018-07-04 22:02:42 +02:00
Christian Kratky bf1d682a85 Update libs 2018-07-04 22:00:48 +02:00
Christian Kratky 7d9326a147 Extend MqttPacketReader constructor to allow more unit tests. 2018-07-04 22:00:34 +02:00
Christian Kratky 7fd09009e7 Update docs. 2018-07-04 21:58:00 +02:00
Christian Kratky 9fa5e2dc1f Add the adapter Endpoint to the connection validator. 2018-07-04 21:57:51 +02:00
Christian Kratky 15d4ee4d68 Merge branch 'develop' 2018-06-30 00:29:26 +02:00
Christian Kratky 6ca6a52ae8 Update docs. 2018-06-30 00:29:04 +02:00
Christian Kratky 3d643aa4cb Update versions. 2018-06-29 23:54:34 +02:00
Christian 9739d23b6f Merge pull request #340 from JanEggers/moreExtensionsForAspnetCoreConfiguration
more extensions for aspnetcore configuration
2018-06-29 21:03:10 +02:00
JanEggers c43acf7dac more extensions for aspnetcore configuration 2018-06-29 20:05:48 +02:00
Christian 995c226893 Merge pull request #339 from JanEggers/develop
fixed parsing issue and added test
2018-06-29 14:17:49 +02:00
Jan Eggers 8574bbb64f fixed parsing issue and added test 2018-06-29 13:37:26 +02:00
JanEggers b2bb545947 Update MQTTnet.AspNetCore.nuspec
added dependencies to nuspec
2018-06-29 13:06:46 +02:00
Christian Kratky bf8dae67de Merge branch 'develop' of https://github.com/chkr1011/MQTTnet into develop 2018-06-25 20:10:10 +02:00
Christian c6309da015 Merge pull request #334 from JanEggers/improveErrorMessages
Improve error messages
2018-06-25 20:09:57 +02:00
Christian 2e83a1010e Merge pull request #330 from JanEggers/develop
fixed another build issue
2018-06-25 20:03:49 +02:00
Christian Kratky d5efb31e3a Fix build script. 2018-06-25 20:03:02 +02:00
Jan Eggers b77798f885 dont use thread static buffers they are overlapping on server due to async io 2018-06-25 15:31:49 +02:00
Jan Eggers 5923c7c408 fixed negative length for empty packages 2018-06-25 14:52:31 +02:00
Jan Eggers 7dfb6fdc52 fixed dup flag serialization and added roundtrip tests 2018-06-25 14:51:46 +02:00
Jan Eggers 4e40b2ddf3 added test to prove something is broken 2018-06-25 12:31:47 +02:00
Jan Eggers 4e9abd41c0 improve error messages 2018-06-25 12:31:19 +02:00
Jan Eggers 7240b86d98 fixed another build issue 2018-06-25 09:25:29 +02:00
Christian Kratky 211853d519 Fix wrong packet writer buffer length calculation. 2018-06-24 19:51:00 +02:00
Christian Kratky f39589a8c1 Add new publish overloads. 2018-06-22 20:45:30 +02:00
Christian e387971292 Merge pull request #326 from JanEggers/develop
Fixed build versions
2018-06-22 20:42:39 +02:00
Jan Eggers 4fc1912ee8 fixed assembly versions 2018-06-22 09:21:33 +02:00
Christian Kratky 31a704b675 Merge branch 'master' into develop 2018-06-21 23:21:00 +02:00
Christian Kratky c5c088300f Merge branch 'master' of https://github.com/chkr1011/MQTTnet 2018-06-21 23:20:26 +02:00
Christian Kratky ccea8e6eb5 Update readme 2018-06-21 23:03:33 +02:00
Christian Kratky ac60ed4559 Merge branch 'develop' of https://github.com/chkr1011/MQTTnet into develop 2018-06-21 23:01:09 +02:00
Christian Kratky 8026263a27 Update nuspec 2018-06-21 23:00:52 +02:00
Christian Kratky 6fa6abe6f0 Update readme. 2018-06-21 20:42:49 +02:00
Christian Kratky e8d5bd7bfb Add new properties and events for the managed client. 2018-06-21 20:42:44 +02:00
Christian 2a03c1f1f4 Merge pull request #321 from asthomas/develop
Fix for incoming SSL connections
2018-06-21 19:26:13 +02:00
asthomas 0c6b92536e Merge pull request #1 from chkr1011/develop
Develop
2018-06-21 08:31:05 -04:00
Andrew dddc1ec112 Fix a bug where an incoming SSL client connection was being treated as a plain-text connection. 2018-06-21 08:12:55 -04:00
Christian Kratky 8b376874c0 Merge develop 2018-06-20 20:10:12 +02:00
Christian Kratky 0322660561 Refactor async/await and ConcurrentDictionary usage. 2018-06-20 20:07:25 +02:00
Christian a7205ddf02 Merge pull request #318 from JanEggers/perf
improve session manager
2018-06-20 20:05:25 +02:00
Jan Eggers 64b38487bc improve session manager 2018-06-20 08:20:54 +02:00
Christian Kratky bc20850fba Refactor serializer locking and thread instances. 2018-06-19 20:52:31 +02:00
Christian Kratky 66048931a2 Refactor ASP net Core integration and fix breaking change. 2018-06-17 17:19:08 +02:00
Christian 8444b6b3be Merge pull request #298 from JanEggers/transports
new Transports for AspnetCore 2.1
2018-06-17 15:38:23 +02:00
Christian Kratky baac26c5f6 Refactor package deserialization. 2018-06-17 14:36:32 +02:00
Christian Kratky 56e9e15ac0 Refactor Serializer memory usage. 2018-06-17 13:47:58 +02:00
Christian Kratky beb54acc2f Refactor serializer benchmark to allow fair comparisons. 2018-06-17 13:19:43 +02:00
JanEggers d573423372 downgrade dependencies to 2.0 2018-06-16 17:39:37 +02:00
JanEggers 410bc211a5 fixed packet reader 2018-06-16 17:39:04 +02:00
JanEggers e65c3f2f2b moved files into the correct place 2018-06-16 17:30:08 +02:00
JanEggers cf947415e8 enable abort exceptions 2018-06-16 17:21:58 +02:00
JanEggers c32a606a70 add memory diagnoser 2018-06-16 17:21:58 +02:00
JanEggers 78c6059668 enabled clients based on tcp to benefit from new api and added Benchmark for EndtoEnd with new API 2018-06-16 17:21:58 +02:00
JanEggers 7c2adf636f removed dead code and make new connection context an opt in so kestrel 2.0 still works 2018-06-16 17:20:01 +02:00
JanEggers d5ac8c7183 use final packages 2018-06-16 17:20:00 +02:00
JanEggers 7a7fab0907 fixed rebase 2018-06-16 17:20:00 +02:00
JanEggers c758ae89f4 first iteration 2018-06-16 17:20:00 +02:00
Christian Kratky caea7910b4 Refactor message processing and async/await usage. 2018-06-16 14:30:02 +02:00
Christian Kratky 6192685671 Remove batch sending of messages to prevent message reordering and several required awaits. 2018-06-15 00:19:51 +02:00
Christian Kratky 26eed30dbf Added custom exception for gracefully closed exceptions. 2018-06-11 00:26:38 +02:00
Christian Kratky 20779dfece Reorganize solution structure. 2018-06-11 00:00:30 +02:00
Christian Kratky 6bcfa436ea Fix TLS certificate loading. 2018-06-09 21:57:04 +02:00
Christian Kratky e62316105f Refactor server socket management for Linux support. 2018-06-09 15:31:47 +02:00
Christian Kratky 9e318fc40d Update BetterCodeHub config. 2018-06-06 22:10:34 +02:00
Christian Kratky 4b407d5213 Merge branch 'develop' of https://github.com/chkr1011/MQTTnet into develop 2018-06-06 22:08:55 +02:00
Christian Kratky 841a22b2b2 Refactor channel adapter. 2018-06-06 22:08:46 +02:00
Christian Kratky 840d8ab4fd Refactor TCP channel. 2018-06-06 22:04:37 +02:00
Christian 2e3e58ed73 Update .bettercodehub.yml 2018-06-06 21:56:47 +02:00
Christian Kratky eb48f87667 Update packages 2018-06-06 21:50:17 +02:00
Christian Kratky a164c120d5 Update solution file. 2018-06-06 21:47:59 +02:00
Christian 9dddd40dbe Update .bettercodehub.yml 2018-06-06 21:47:37 +02:00
Christian ad778636cf Delete MQTTnet.NetStandard.csproj 2018-06-06 21:45:58 +02:00
Christian Kratky 78360bc24a Add ID property for the ManagedMqttApplicationMessage. 2018-06-06 21:44:25 +02:00
Christian Kratky 3480d9ac35 Refactoring 2018-06-06 21:29:34 +02:00
Christian Kratky ae72bc3f99 Fix wrong exceptions during a clean disconnect. 2018-06-06 21:29:19 +02:00
Christian Kratky c8bf36044b Rerfactor package parsing. 2018-06-06 21:28:39 +02:00
Christian Kratky b05cb0d2ea Move managed client to separate nuget. 2018-06-06 21:27:39 +02:00
Christian Kratky fd1880f1ff Update better code hub config. 2018-06-06 21:26:56 +02:00
Christian Kratky 70c5bd1875 Remove obsolete files. 2018-06-05 23:41:02 +02:00
Christian Kratky 00ac9ce961 Fix wrong remaining length calculation. 2018-06-05 23:39:59 +02:00
Christian Kratky 1d455221e4 Clear awaiters after an exception has occured. 2018-06-05 21:28:11 +02:00
Christian Kratky 31b4eb7937 Optimize package serializing and dispatching. 2018-06-05 21:13:58 +02:00
Christian Kratky c88b46783b [Server] Add new interceptor before messages are added to a queue. 2018-06-04 22:33:13 +02:00
Christian Kratky 39edd22a9d Refactor thread management for incoming packets. 2018-06-04 22:19:07 +02:00
Christian Kratky afe25db0d3 Update docs. 2018-06-04 21:26:33 +02:00
Christian Kratky f86a5af794 Merge branch 'pr/301' into develop 2018-06-04 21:23:27 +02:00
Christian Kratky e6de4d014e Update client test app 2018-06-04 21:19:15 +02:00
Christoph Stichlberger b38df9eecd Respect keep alive interval after sending 2018-06-04 16:00:03 +02:00
Christian Kratky 54abf84e96 Merge branch 'develop' of https://github.com/chkr1011/MQTTnet into develop 2018-06-03 22:04:48 +02:00
Christian Kratky 66ebdfd61a Add test for simple server and client execution. 2018-06-03 22:04:43 +02:00
Christian 4fe7c13d8e Merge pull request #295 from JanEggers/moreBenchmarks
more benchmarks
2018-06-01 12:40:54 +02:00
Jan Eggers 2578b380f9 fixed sln change and moved options to seperate file 2018-06-01 12:27:06 +02:00
Jan Eggers cadf9f797f added pipe based benchmak 2018-06-01 08:47:00 +02:00
JanEggers fd50e4e260 more benchmarks 2018-06-01 07:14:03 +02:00
Christian Kratky 1cbec27b7f Fix projects 2018-05-31 21:48:09 +02:00
Christian Kratky 8d110c44f7 Merge master 2018-05-31 21:39:27 +02:00
Christian Kratky 66a77db851 Merge branch 'master' of https://github.com/chkr1011/MQTTnet 2018-05-31 21:35:51 +02:00
Christian Kratky 0505efbac1 Refactor graceful socket close. 2018-05-31 21:34:51 +02:00
Christian Kratky 99614f2e15 Merge branch 'develop' of https://github.com/chkr1011/MQTTnet into develop 2018-05-31 21:28:10 +02:00
Christian 08e41244fc Merge pull request #294 from JanEggers/NewExtensionToReduceCodeForBuilderPattern
added extension method
2018-05-31 21:27:50 +02:00
JanEggers 4e74b59796 added extension method 2018-05-31 13:45:57 +02:00
Christian 2b6b268b4e Merge pull request #291 from JanEggers/BenchmarksSdkProject
use new project system for benchmarks
2018-05-31 10:58:55 +02:00
Jan Eggers 09aacf011e use new project system for benchmarks 2018-05-31 07:59:25 +02:00
Christian Kratky 9bea46adcb Remove unneeded _sealed_ keywords. Update docs. 2018-05-30 20:56:20 +02:00
Christian Kratky a10183628b Suspend keep alive checking while a packet is being received. 2018-05-29 23:13:53 +02:00
Christian Kratky 7e0017d14a Update docs. 2018-05-29 22:29:28 +02:00
Christian Kratky 62e9a333c7 Add several packet validations. 2018-05-29 22:21:16 +02:00
Christian Kratky 4838da48ef Replace a manual lock with the lock class. 2018-05-29 21:45:25 +02:00
Christian Kratky b5a43c416a Add validation of packet control type. 2018-05-29 21:39:40 +02:00
Christian Kratky c1297eadd4 Update docs 2018-05-29 21:22:46 +02:00
Christian Kratky 06886fe6c2 Update UWP version for RPC extension 2018-05-29 21:21:41 +02:00
Christian Kratky 137d1d181b Refactor ManagedMqttClientStorage 2018-05-29 21:21:30 +02:00
Christian 97d0286a31 Merge pull request #286 from JTrotta/master
ManagedClientStorage now works
2018-05-29 21:15:05 +02:00
Christian 782dcffc62 Update MQTTnet.sln 2018-05-29 21:08:32 +02:00
Christian 40f7a7c593 Delete duplicate file 2018-05-29 21:07:20 +02:00
Christian 44e5b2c2fd Merge pull request #274 from sebastienwarin/develop
Fix MqttProtocolVersion 3.1 client connection
2018-05-29 20:47:23 +02:00
JTrotta f844a8be5e ManagedMqttClietStorage now works 2018-05-29 11:32:16 +02:00
Gerardo 449d8aca65 Merge pull request #4 from chkr1011/master
merge last
2018-05-29 09:40:45 +02:00
Sébastien Warin 9b29f8c23a Fix MqttProtocolVersion 3.1 connection 2018-05-18 17:10:28 +02:00
Christian 5e79581662 Merge pull request #273 from sebastienwarin/develop
Adding delay between Pub&Sub in the test unit
2018-05-18 00:02:14 +02:00
Sébastien Warin 79a2952f94 Adding delay between Pub&Sub in the test unit 2018-05-17 12:29:50 +02:00
Christian 8077d8dfeb Refactor TopicFilterComparer 2018-05-16 23:07:39 +02:00
Christian d72ac9694e Merge pull request #270 from sebastienwarin/develop
Replace the Async coordination primitives & replace the MqttTopicFilterComparer to improve the performance
2018-05-16 21:45:59 +02:00
Sébastien Warin 5e0bc17341 Removing the debug log messages 2018-05-16 20:47:27 +02:00
Sébastien Warin 932aa1fd39 Fixes chkr1011's refactoing and pass all tests 2018-05-16 11:46:09 +02:00
Sébastien Warin 2c45e9fedd Merge branch 'develop' of https://github.com/chkr1011/MQTTnet into develop
# Conflicts:
#	Frameworks/MQTTnet.NetStandard/Server/MqttClientPendingMessagesQueue.cs
#	Frameworks/MQTTnet.NetStandard/Server/MqttClientSessionsManager.cs
#	Frameworks/MQTTnet.NetStandard/Server/MqttRetainedMessagesManager.cs
2018-05-16 10:09:10 +02:00
Sébastien Warin c1352d50c1 Review code style to match with the existing code 2018-05-16 10:00:29 +02:00
Christian 2499d29196 Refactor events. 2018-05-16 00:12:09 +02:00
Christian fbf1cbc76e Refactor ConnectedClient statistics API 2018-05-16 00:08:42 +02:00
Christian 719838de3b Add new server option to disable/enable persistent sessions. 2018-05-15 22:04:05 +02:00
Sébastien Warin ba66c108fa Fixes and clean the new topic matcher from israellot 2018-05-15 17:39:45 +02:00
Sébastien Warin 5287bf5cac Codacy/PR Quality Review 2018-05-15 16:54:37 +02:00
Sébastien Warin 10740f12c4 Replace the Async coordination primitives 2018-05-15 15:28:14 +02:00
Christian 3e6edb92c5 Refactor ASP.NET integration. 2018-05-13 20:59:00 +02:00
Christian dc7b9414d9 Refactor logging. Remove TCP adapter from HTTP host if disabled. 2018-05-13 20:18:08 +02:00
Christian 8df8cb1eb6 Refactor logging and add benchmark. 2018-05-11 22:00:54 +02:00
Christian e14ea9a240 Add overflow strategy for pending messages queue. 2018-05-11 20:49:31 +02:00
Christian 57287a5303 Merge branch 'develop' of https://github.com/chkr1011/MQTTnet into develop 2018-05-11 20:06:16 +02:00
Christian febc8f774d Start a dedicated thread per client. 2018-05-11 20:06:10 +02:00
Christian e51042ed04 Update UWP version 2018-05-11 20:05:59 +02:00
Christian 910c123887 Merge pull request #266 from VladimirAkopyan/master
Limiting the growth of PendingMessagesQueue to a configurable Value
2018-05-11 20:05:36 +02:00
Christian e0899abaac Merge pull request #257 from JanEggers/AllocationBenchmark
[wip] improved serializer performance
2018-05-11 20:04:07 +02:00
Vladimir Akopyan 9df1bb2ff2 Merge branch 'develop' into master 2018-05-11 17:52:20 +02:00
VladimirAkopyan 7b987635f7 ref #230 Limiting the growth of PendingMessagesQueue to a configurable value. it will only store X lated messages, 250 by default.
https://github.com/chkr1011/MQTTnet/issues/230
2018-05-11 16:39:59 +01:00
JanEggers 8c37cf16c0 fixed tests 2018-05-10 22:28:01 +02:00
JanEggers c569c10740 dont use writer instance 2018-05-10 20:50:50 +02:00
Christian 325541642e Merge pull request #256 from JanEggers/AllocationBenchmark
added benchmark for serializers
2018-05-10 19:58:30 +02:00
JanEggers 97d91ec0f8 fixed code quality issues 2018-05-10 19:30:29 +02:00
JanEggers 17db90d5ab fixed build 2018-05-10 19:25:33 +02:00
JanEggers e7a7bb1a7b added benchmark for serializers 2018-05-10 19:18:31 +02:00
Christian b621806a7c Update build config 2018-05-10 16:27:27 +02:00
Christian 7720a3f091 Refactor thread management. 2018-05-10 16:27:12 +02:00
Christian b22b02a0b6 Refactor subscriptions manager 2018-05-03 22:22:07 +02:00
Christian d14088512b Refactoring 2018-05-03 20:47:15 +02:00
Christian 62527a91ac Complete merge 2018-05-03 20:44:41 +02:00
Christian f9e4a97eb8 Merge 2018-05-03 20:44:21 +02:00
Christian ff45be1f71 Update docs 2018-05-03 20:30:44 +02:00
Christian aaed2ed790 Process client messages in worker thread (extend options) 2018-05-03 20:30:40 +02:00
Christian d9501de252 Treat a connection handover as a clean disconnect. 2018-05-03 20:29:53 +02:00
Christian 9fdcb08364 Add benchmark app. 2018-05-03 20:29:02 +02:00
Christian 9f4f37bb51 Update nugets 2018-04-28 20:24:07 +02:00
Christian 49cf364352 Update docs 2018-04-28 13:05:45 +02:00
Christian b9a73cc23d Changed socket features. 2018-04-28 13:05:35 +02:00
Christian 357fd844d7 Fix TopicFilterComparer 2018-04-28 13:05:22 +02:00
Christian 74a5522965 Add topic filter tests from MQTT spec 2018-04-25 20:22:25 +02:00
Christian 84dab18c2a Fix broken UWP support. 2018-04-25 19:55:30 +02:00
Christian 9e90111b37 Fix several deadlocks 2018-04-24 20:54:18 +02:00
Christian ffb1645f14 Refactoring 2018-04-21 13:07:11 +02:00
Christian 6448b217d5 Merge branch 'develop' of https://github.com/chkr1011/MQTTnet into develop 2018-04-21 12:57:57 +02:00
Christian 1b84ab40ce Merge pull request #226 from israellot/develop
Remove useless sempahore object
2018-04-21 12:57:38 +02:00
Christian af8d1ec6be Refactoring 2018-04-21 12:55:09 +02:00
Israel Lot 04a4080768 removed performance section from sln file 2018-04-19 19:45:01 -03:00
Israel Lot 942bf70582 one less copy and allocation 2018-04-19 19:38:03 -03:00
Israel Lot b9f0ee2317 merge upstream 2018-04-19 19:07:03 -03:00
Christian cb09bac246 Refactor the WebSocket channel and remove the internal queue. 2018-04-18 23:10:00 +02:00
Christian 109e4b2cf1 Refactor the task timeout code to avoid still running tasks if the timeout is reached. 2018-04-18 21:14:31 +02:00
Israel Lot e4cf2978e6 Remove useless sempahore object 2018-04-17 21:43:10 -03:00
Christian 17b74f20c9 Migrate to Stream-less approach. 2018-04-17 22:29:23 +02:00
Christian ae11e31048 Refactoring 2018-04-17 20:37:56 +02:00
Christian e455ce9c78 Merge branch 'develop' of https://github.com/chkr1011/MQTTnet into develop 2018-04-17 20:16:08 +02:00
Christian 41df5cdd8b Update docs 2018-04-17 20:16:00 +02:00
Christian 383225d7f4 Merge pull request #225 from israellot/optmizations
Optmizations
2018-04-17 20:15:24 +02:00
Israel Lot a62c15ad1a Missing interface implementation 2018-04-16 14:46:46 -03:00
Israel Lot 5300742543 Avoid unnecessary enumeration for every packet 2018-04-16 14:30:52 -03:00
Israel Lot cd09a2f176 Fix code style 2018-04-16 14:08:04 -03:00
Israel Lot 11ba46d140 Remove useless stream flush 2018-04-16 14:01:07 -03:00
Israel Lot e006377e64 Fix zero lenght packets bug 2018-04-16 13:25:56 -03:00
Israel Lot 786fa53334 Fix unit tests for serializer 2018-04-16 13:07:35 -03:00
Israel Lot 9bc34e4406 Translate timeout exception 2018-04-16 13:07:19 -03:00
Israel Lot c915af8dad Disable Nagle on sockets, send packets in one shot. 2018-04-16 12:30:56 -03:00
Christian 98fbbc2864 Merge branch 'develop' of https://github.com/chkr1011/MQTTnet into develop 2018-04-15 21:07:48 +02:00
Christian d2049d63d6 Refactoring minor issues 2018-04-15 21:07:41 +02:00
Christian 63a12d4d40 Merge pull request #220 from israellot/optmizations
Replace double dictionary with a tuple key
2018-04-15 21:06:46 +02:00
Christian 9051f8275b Merge pull request #215 from biapar/patch-1
Update Startup.cs
2018-04-15 21:02:29 +02:00
Israel Lot a5f33bc244 Replace double dictionary with a tuple key 2018-04-13 23:00:36 -03:00
Christian b23d25217b Fix unit tests. 2018-04-11 23:12:27 +02:00
Christian 1df6798934 Fix issues in RPC extension. 2018-04-11 22:19:38 +02:00
Christian 9afff6d8a0 Merge branch 'develop' 2018-04-11 21:22:54 +02:00
Christian ec41efd860 Fix client dead lock and protocol version issues. 2018-04-11 21:22:08 +02:00
BP 580a431fbf Update Startup.cs 2018-04-09 10:59:34 +02:00
Christian c06d84e834 Renamed MqttServerAdapter to MqttTcpServerAdapter 2018-04-06 22:49:41 +02:00
Christian 06a299d212 Update README.md
Remove windows app
2018-04-05 23:40:05 +02:00
Christian f90985a1f9 Merge pull request #214 from JanEggers/Fix175
added tcp adapter
2018-04-05 23:39:10 +02:00
JanEggers 677beddf07 fixed code quality compliance 2018-04-05 16:03:28 +02:00
JanEggers 7154a7b3dc fixed tests 2018-04-05 15:55:08 +02:00
JanEggers 7de3d79d09 added tcp adapter 2018-04-05 14:58:13 +02:00
Christian 54a0265ef9 Fix UnitTests 2018-04-01 20:53:32 +02:00
Christian 8f96acaec4 Fixed a race condition in client and server. 2018-03-31 23:10:14 +02:00
Christian cea984cc2f Fix stream and socket handling. 2018-03-29 22:12:23 +02:00
Christian fcf748e980 Merge branch 'develop' 2018-03-25 14:45:35 +02:00
Christian e58cb3c9cd Update docs. 2018-03-25 14:42:43 +02:00
Christian 3587a8ebd1 Add new option which allows configuring the send interval for keep alive packets. 2018-03-25 14:42:34 +02:00
Christian 3193381b69 Refactor web socket stream. Add mqtt as default sub protocol. 2018-03-25 14:42:12 +02:00
Christian 7a9ddfc2b5 Fix packet identifier which is sent from the server. 2018-03-25 14:41:12 +02:00
Christian 447b2aa2c6 Add new extension methods. 2018-03-25 14:39:44 +02:00
Christian b3eaeea8a4 Fix log issue in UWP test app. 2018-03-25 14:38:36 +02:00
Christian 45b4c05867 Update version 2018-03-25 14:38:16 +02:00
Christian 49da66dcff Fix issue when disconnecting on UWP 2018-03-13 23:28:50 +01:00
Christian bb9d5586f0 Update nuspec 2018-03-13 20:31:01 +01:00
Christian c5d22f4b27 Update docs 2018-03-13 20:23:36 +01:00
Christian e57ec11ed8 Update libs 2018-03-13 20:23:31 +01:00
Christian 56acc82445 Fix null ref in server when using interceptor 2018-03-13 20:23:22 +01:00
Christian 6e828d091f Override ToString() for LogMessage 2018-03-13 20:23:07 +01:00
Christian e1d8697331 Fix wrong parsing and generating of MqttConnAck for version 3.1.0 2018-03-13 20:22:48 +01:00
Christian 90098c2006 Optimize dispatcher 2018-03-13 20:22:13 +01:00
Christian b3a25fb9e8 Fix subscription handling of _ManagedClient_. 2018-03-05 20:43:37 +01:00
Christian f85ad4965b Refactor keep alive monitoring and add unit tests. 2018-02-28 21:43:19 +01:00
Christian f5696b6fc0 Merge branch 'develop' 2018-02-21 21:33:01 +01:00
Christian c26555aca5 Refactor ManagedClient 2018-02-21 21:32:21 +01:00
Christian 0accb467b5 Merge branch 'develop' of https://github.com/chkr1011/MQTTnet into develop 2018-02-21 20:44:10 +01:00
Christian 3b34fd4750 Fix build 2018-02-21 20:44:05 +01:00
Christian abb216c055 Fix retained message handling 2018-02-21 20:43:22 +01:00
Christian 1760dc7d35 Fix async issues 2018-02-21 20:43:09 +01:00
Christian 034e7fd750 Add event for processed application message (managed client only) 2018-02-21 20:42:39 +01:00
Christian 76c7b319b8 Fix build errors 2018-02-21 20:42:05 +01:00
Christian 09010e95a2 Merge pull request #180 from lerppana/managed_unsubscribe_fix
Fix ManagedMqttClient missing call to unsubscribe topics
2018-02-21 20:09:10 +01:00
Lassi Laati 2341d66192 Fix ManagedMqttClient missing call to unsubscribe topics 2018-02-20 09:23:47 +02:00
Christian 21586b7710 Merge branch 'master' of https://github.com/chkr1011/MQTTnet 2018-02-04 20:10:11 +01:00
Christian b426ae1198 Update docs 2018-02-04 20:06:01 +01:00
Christian e9db9d65ac Update README.md 2018-02-04 20:05:34 +01:00
Christian 59dcbf4c0c Add server events for subscribed and unsubscribed topics. Add pending messages count for connected clients. 2018-02-02 22:44:21 +01:00
Christian a204d233f6 Update configs 2018-02-02 00:47:00 +01:00
Christian 26cd84c88c Fix generation of packet identifier. 2018-02-02 00:46:47 +01:00
Christian 5f77255893 Update docs 2018-01-27 14:12:54 +01:00
Christian 8856a7aa34 Fix broken package reader 2018-01-26 23:56:22 +01:00
Christian 30754433eb Add server methods for controlling the client subscriptions. 2018-01-26 22:06:21 +01:00
Christian 751ac19c4b Fix reading more data than required for the body. 2018-01-26 21:25:56 +01:00
Christian 1415e4b878 Covnert the body buffer to a memory stream which will grow automatically. 2018-01-24 20:49:47 +01:00
Christian e56203faac Update docs 2018-01-24 20:16:47 +01:00
Christian 64c997a4a2 Merge pull request #163 from kpreisser/fixInfiniteLoop
Fix infinite loop when the client closes the connection
2018-01-24 20:12:15 +01:00
Christian 391ee34a0f Merge pull request #166 from kpreisser/fixDecodeRemainingLength
Fix the algorithm for decoding the body length
2018-01-24 20:09:14 +01:00
kpreisser 88de5047bc Fix the algorithm for decoding the remaining length to allow a length greater than 2,097,151 bytes.
See: https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/errata01/os/mqtt-v3.1.1-errata01-os.html#_Toc442180732
2018-01-24 15:32:36 +01:00
kpreisser d020e809a1 Fix infinite loop that can occur when the client closes the connection before sending the complete packet body. 2018-01-23 10:45:58 +01:00
Christian 8b2b8d41df Update docs 2018-01-22 21:11:44 +01:00
Christian 40c33ac639 Refactoring 2018-01-22 21:11:35 +01:00
Christian 002e4b8d69 Fix broken unit tests 2018-01-22 20:55:50 +01:00
Christian c060c379d3 Add thrown exception for disconnected event 2018-01-22 20:55:06 +01:00
Christian 4d13407a83 Merge branch 'develop' of https://github.com/chkr1011/MQTTnet into develop 2018-01-21 14:33:33 +01:00
Christian d737f550c7 Add encrypted code signing key 2018-01-21 14:33:29 +01:00
Christian 5e8d5b9219 Add documents 2018-01-21 14:29:43 +01:00
Christian f98f21ffdf Update libs 2018-01-21 14:29:36 +01:00
Christian ec9e64ebfb Update docs 2018-01-21 14:14:04 +01:00
Christian 39672970e2 Update 3rd-Partly libs 2018-01-21 14:13:50 +01:00
Christian 5becf7980a Merge pull request #157 from kpreisser/trueAsync
Use asynchronous methods for blocking operations
2018-01-21 14:12:29 +01:00
Christian c7c84ad39b Make clients interfaces disposable. 2018-01-21 14:10:23 +01:00
Christian 8773117493 Fix an issue which adds a delay after every accepted connection. 2018-01-21 14:09:34 +01:00
kpreisser bb697e1bd9 Follow-Up: Add missing ConfigureAwait() call. 2018-01-17 11:29:48 +01:00
kpreisser e1a9aa7d03 Adjust the MqttClientPendingMessagesQueue and the MqttPacketReader to use asynchronous methods for long-running/blocking operations.
Otherwise, Worker Threads from the .NET Threadpool are blocked while waiting for the next client message, and while waiting for the next message to send.
2018-01-17 11:10:39 +01:00
Christian Kratky 312773d054 Add password protected code signing certificate to enable build process. 2017-12-22 12:02:25 +01:00
Christian Kratky 1ee48a978f Merge develop 2017-12-18 14:17:31 +01:00
Christian Kratky 5ddb6b77e2 Update documentation 2017-12-14 13:24:50 +01:00
Christian Kratky e0284886b5 Added the bound IP address to the server options. 2017-12-14 13:23:12 +01:00
Christian Kratky 44c07c2034 Fix broken ASP.NET Core integration. 2017-12-14 13:12:18 +01:00
Christian Kratky f6b38081fc Update documents. 2017-12-12 14:30:28 +01:00
Christian c570509628 Merge pull request #139 from FourOnes/develop
fixed: Compile Source Code Occur Errors On Mac Machine
2017-12-12 14:27:04 +01:00
limingru e98ff1d6d6 uncomment code 2017-12-12 17:26:16 +08:00
limingru 0f54663789 fixed: Compile Source Code Occur Errors On Mac Machine 2017-12-12 17:21:22 +08:00
Christian Kratky f91d935708 Add the ClientId to the application message interceptor. 2017-12-11 17:10:27 +01:00
Christian Kratky fb8b60fccc Add overload for subscribing. 2017-12-08 21:12:05 +01:00
Christian Kratky ad8506cc30 Update documents 2017-12-07 20:41:44 +01:00
Christian Kratky d6f88acb8d Merge branch 'develop' of https://github.com/chkr1011/MQTTnet into develop 2017-12-07 20:37:49 +01:00
Christian Kratky 333fc4c387 Fixed an TLS issue in the MqttClientOptionsBuilder; Fix a subscription issue in the managed client. 2017-12-07 20:35:29 +01:00
Christian 5f9214e9a2 Merge pull request #134 from cvellan/patch-3
Serialized certificates loaded as X509Certificate2
2017-12-05 13:18:16 +01:00
cvellan f974dcb647 Serialized certificates loaded as X509Certificate2
Instances of X509Certificate2 passed into an MqttClientOptionsBuilder lose data and functionality by being loaded as an X509Certificate.
2017-12-05 10:13:07 +11:00
Christian Kratky 2cb1c544f5 Add extension method for subscriptions 2017-12-04 22:40:44 +01:00
Christian Kratky 644e948489 Fix keep alive checks in server 2017-12-04 13:52:14 +01:00
Christian Kratky d12d2648b7 Add extension for RPC calls; replace locks; Refactored storage in Managed Client 2017-12-04 10:21:37 +01:00
Christian Kratky 0ee3c8e135 Add statistics of last sent packets to the _ConnectedClient_. 2017-11-28 21:19:30 +01:00
Christian Kratky bc959a8c3d Performance optimizations 2017-11-27 00:25:45 +01:00
Christian Kratky 69781d822a Update docs, fix managed client connection issue 2017-11-26 22:57:56 +01:00
Christian Kratky 611fabcd01 Performance optimizations 2017-11-25 23:10:48 +01:00
Christian Kratky 24ab285066 Update project settings 2017-11-25 20:33:04 +01:00
Christian Kratky ee6ec14bf8 Add interface for server options. Add context class for connection validator (hide internal packets). 2017-11-25 20:12:50 +01:00
Christian Kratky f9d67e2cd2 Merge proces, enable signing of the assembly. 2017-11-25 19:55:26 +01:00
Christian a72516fdcc Update README.md 2017-11-25 19:50:31 +01:00
Christian Kratky ebfc6aab6b Fix nuget spec 2017-11-25 16:20:07 +01:00
Christian Kratky 9c20edd3d0 Update documents 2017-11-25 16:17:44 +01:00
Christian Kratky b02b0ccd7c Refactor some names. 2017-11-23 22:50:26 +01:00
Christian Kratky 1bcf7ec29c Fix ASP.NET Core breaking changes 2017-11-23 22:34:16 +01:00
Christian Kratky e54b4f58df Update version info. Fix typo. Add MqttServerOptionsBuilder. 2017-11-23 22:26:22 +01:00
Christian Kratky 30540729df Merge branch 'master' into develop 2017-11-22 22:31:02 +01:00
Christian Kratky 77b80dc53e Removed 3rd-Party libraries. 2017-11-22 22:30:55 +01:00
Christian Kratky 861d3dfa0f Fix version info 2017-11-21 21:00:48 +01:00
Christian Kratky e0235aedff Refactor trace messages 2017-11-21 20:55:57 +01:00
Christian Kratky 8411382c42 Update build script 2017-11-17 21:29:37 +01:00
Christian Kratky f8078a6894 Update version info 2017-11-17 21:26:43 +01:00
Christian Kratky f66e8c8b3a Merge branch 'master' of https://github.com/chkr1011/MQTTnet 2017-11-17 21:16:19 +01:00
Christian Kratky acc269e702 Fixed WebSocket client stream 2017-11-17 21:13:01 +01:00
Christian Kratky 5bcda12ca9 Fix QoS handling in server 2017-11-16 14:17:58 +01:00
Christian Kratky 8de87c8772 Wrap COM exceptions, close client connection when server stops, process retained messages from server 2017-11-16 00:28:56 +01:00
Christian Kratky b956623234 Merge branch 'develop' of https://github.com/chkr1011/MQTTnet into develop 2017-11-15 20:59:10 +01:00
Christian Kratky 6e615f85eb Update docs 2017-11-15 20:59:03 +01:00
Christian b70b469201 Merge pull request #107 from 1iveowl/master
Reference to MQTT Tester - updated
2017-11-15 12:37:57 +01:00
1iveowl c4b706f8ad Reference to MQTT Tester 2017-11-12 23:19:37 +01:00
1iveowl 9d971003d5 Reference to MQTT Tester 2017-11-12 23:17:26 +01:00
1iveowl c322817cb1 Reference to MQTT Tester 2017-11-12 23:16:54 +01:00
1iveowl 0c4c93784d Reference to MQTT Tester 2017-11-12 22:59:06 +01:00
Christian 4cae09a4e9 Merge pull request #104 from JanEggers/fixedWebsocketSubprotocolNegotiation
fixed websocket subprotocol negotiation
2017-11-12 19:00:38 +01:00
JanEggers c775867db4 merged if statements 2017-11-12 16:58:13 +01:00
JanEggers 8672fb2792 fixed websocket subprotocol negotiation 2017-11-12 16:46:32 +01:00
Christian 48c47c6fcf Merge pull request #102 from JanEggers/targetframeworkfix
added net461 build
2017-11-12 14:58:25 +01:00
JanEggers 38904a6213 removed testapp again 2017-11-12 14:13:39 +01:00
JanEggers bdadfa1c26 added net461 build 2017-11-12 14:07:33 +01:00
Christian e38142399e Merge pull request #101 from JanEggers/targetframeworkfix
Targetframeworkfix
2017-11-12 12:55:14 +01:00
JanEggers 5cbbfedcce added testproject for importing mqtt nugets and verify bin folder 2017-11-12 12:33:56 +01:00
JanEggers bc97575760 downgrade dependencies to be netstandard 1.3 compliant and remove net461 shim requirement 2017-11-12 12:28:55 +01:00
JanEggers afd56a7f6f added indication of what Mqtt TargetFramework is used 2017-11-12 09:22:17 +01:00
Gerardo d076aac421 Merge pull request #3 from chkr1011/develop
Original Develop
2017-10-30 11:08:41 +01:00
Gerardo e6d811ea7c Merge pull request #2 from chkr1011/develop
From develop
2017-10-26 12:09:02 +02:00
310 changed files with 11201 additions and 5833 deletions
+1 -6
View File
@@ -1,8 +1,3 @@
exclude:
- /Frameworks/.*
- /MQTTnet.TestApp.NetCore/Program.cs
- /Tests/MQTTnet.TestApp.NetFramework/.*
- /Tests/MQTTnet.TestApp.UniversalWindows/.*
component_depth: 1
component_depth: 2
languages:
- csharp
+2
View File
@@ -291,3 +291,5 @@ __pycache__/
Build/nuget.exe
*.js
*.map
/Tests/MQTTnet.TestApp.NetCore/RetainedMessages.json
+11 -9
View File
@@ -2,7 +2,7 @@
<package >
<metadata>
<id>MQTTnet.AspNetCore</id>
<version>2.5.1</version>
<version>0.0.0</version>
<authors>Christian Kratky</authors>
<owners>Christian Kratky</owners>
<licenseUrl>https://github.com/chkr1011/MQTTnet/blob/master/LICENSE</licenseUrl>
@@ -10,19 +10,21 @@
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>This is a support library to integrate MQTTnet into AspNetCore.</description>
<releaseNotes>* Updated to MQTTnet 2.5.1.
<releaseNotes>* Updated to MQTTnet 2.8.1.
* For more release notes please check the MQTTnet release notes.
</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 ESP Smart Home Cities Automation Xamarin</tags>
<copyright>Copyright Christian Kratky 2016-2018</copyright>
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin</tags>
<dependencies>
<group targetFramework="netstandard2.0">
<dependency id="MQTTnet" version="2.5.1" />
</group>
<dependency id="MQTTnet" version="2.8.1" />
<dependency id="Microsoft.AspNetCore.Connections.Abstractions" version="2.1.0" />
<dependency id="Microsoft.AspNetCore.WebSockets" version="2.0.1" />
<dependency id="Microsoft.Extensions.Hosting.Abstractions" version="2.0.1" />
</dependencies>
</metadata>
<files>
<!-- .NET Standard 2.0 -->
<file src="..\Frameworks\MQTTnet.AspNetCore\bin\Release\netstandard2.0\MQTTnet.AspNetCore.*" target="lib\netstandard2.0\"/>
<file src="..\Source\MQTTnet.AspNetCore\bin\Release\netstandard2.0\MQTTnet.AspNetCore.*" target="lib\netstandard2.0\"/>
</files>
</package>
</package>
@@ -0,0 +1,39 @@
<?xml version="1.0"?>
<package >
<metadata>
<id>MQTTnet.Extensions.ManagedClient</id>
<version>0.0.0</version>
<authors>Christian Kratky</authors>
<owners>Christian Kratky</owners>
<licenseUrl>https://github.com/chkr1011/MQTTnet/blob/master/LICENSE</licenseUrl>
<projectUrl>https://github.com/chkr1011/MQTTnet</projectUrl>
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>This is an extension library which provides a managed MQTT client with additional features using MQTTnet.</description>
<releaseNotes>* Updated to MQTTnet 2.8.1.
* For more release notes please check the MQTTnet release notes.
</releaseNotes>
<copyright>Copyright Christian Kratky 2016-2018</copyright>
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin</tags>
<dependencies>
<dependency id="MQTTnet" version="2.8.1" />
</dependencies>
</metadata>
<files>
<!-- .NET Standard 1.3 -->
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\netstandard1.3\MQTTnet.Extensions.ManagedClient.*" target="lib\netstandard1.3\"/>
<!-- .NET Standard 2.0 -->
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\netstandard2.0\MQTTnet.Extensions.ManagedClient.*" target="lib\netstandard2.0\"/>
<!-- Universal Windows -->
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\uap10.0\MQTTnet.Extensions.ManagedClient.*" target="lib\uap10.0\"/>
<!-- .NET Framework -->
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\net452\MQTTnet.Extensions.ManagedClient.*" target="lib\net452\"/>
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\netstandard2.0\MQTTnet.Extensions.ManagedClient.*" target="lib\net461\"/>
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\netstandard2.0\MQTTnet.Extensions.ManagedClient.*" target="lib\net472\"/>
</files>
</package>
+39
View File
@@ -0,0 +1,39 @@
<?xml version="1.0"?>
<package >
<metadata>
<id>MQTTnet.Extensions.Rpc</id>
<version>0.0.0</version>
<authors>Christian Kratky</authors>
<owners>Christian Kratky</owners>
<licenseUrl>https://github.com/chkr1011/MQTTnet/blob/master/LICENSE</licenseUrl>
<projectUrl>https://github.com/chkr1011/MQTTnet</projectUrl>
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>This is an extension library which allows executing synchronous device calls including a response using MQTTnet.</description>
<releaseNotes>* Updated to MQTTnet 2.8.1.
* For more release notes please check the MQTTnet release notes.
</releaseNotes>
<copyright>Copyright Christian Kratky 2016-2018</copyright>
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin</tags>
<dependencies>
<dependency id="MQTTnet" version="2.8.1" />
</dependencies>
</metadata>
<files>
<!-- .NET Standard 1.3 -->
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\netstandard1.3\MQTTnet.Extensions.Rpc.*" target="lib\netstandard1.3\"/>
<!-- .NET Standard 2.0 -->
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\netstandard2.0\MQTTnet.Extensions.Rpc.*" target="lib\netstandard2.0\"/>
<!-- Universal Windows -->
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\uap10.0\MQTTnet.Extensions.Rpc.*" target="lib\uap10.0\"/>
<!-- .NET Framework -->
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\net452\MQTTnet.Extensions.Rpc.*" target="lib\net452\"/>
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\netstandard2.0\MQTTnet.Extensions.Rpc.*" target="lib\net461\"/>
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\netstandard2.0\MQTTnet.Extensions.Rpc.*" target="lib\net462\"/>
</files>
</package>
+21 -36
View File
@@ -2,7 +2,7 @@
<package >
<metadata>
<id>MQTTnet</id>
<version>2.5.1</version>
<version>0.0.0</version>
<authors>Christian Kratky</authors>
<owners>Christian Kratky</owners>
<licenseUrl>https://github.com/chkr1011/MQTTnet/blob/master/LICENSE</licenseUrl>
@@ -10,67 +10,52 @@
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker).</description>
<releaseNotes>* Fixed several library reference issues.
* [Client] Fixed issue in _MqttClientOptionsBuilder_ when using no certificates (Thanks to 1iveowl).
<releaseNotes>* [Core] Performance optimizations.
* [Core] Fixed a bug which prevents receiving large packets (UWP only)
* [Client] The ManagedClient options now allow configuring the interval for connection checks.
* [Server] Added the Endpoint of the Adapter (remote IP and port) to the connection validation callback.
* [Server] The ipv4 and ipv6 endpoint can be disabled now by setting the bound IP address to _None_.
* [Server] Fix a bug in the keep alive monitor which caused high CPU load (thanks to @GarageGadget).
</releaseNotes>
<copyright>Copyright Christian Kratky 2016-2017</copyright>
<copyright>Copyright Christian Kratky 2016-2018</copyright>
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin</tags>
<dependencies>
<group targetFramework="netstandard1.3">
<dependency id="NETStandard.Library" version="1.6.1" />
<dependency id="Microsoft.Extensions.DependencyInjection" version="1.1.1" />
<dependency id="Microsoft.Extensions.Logging" version="1.1.2" />
<dependency id="Microsoft.Extensions.Options" version="1.1.2" />
<dependency id="NETStandard.Library" version="1.3.0" />
<dependency id="System.Net.Security" version="4.3.2" />
<dependency id="System.Net.WebSockets" version="4.3.0" />
<dependency id="System.Net.WebSockets.Client" version="4.3.1" />
<dependency id="System.Threading.Thread" version="4.3.0" />
<dependency id="System.Net.WebSockets.Client" version="4.3.1" />
</group>
<group targetFramework="netstandard2.0">
<dependency id="NETStandard.Library" version="2.0.0" />
<dependency id="Microsoft.Extensions.DependencyInjection" version="2.0.0" />
<dependency id="Microsoft.Extensions.Logging" version="2.0.0" />
<dependency id="Microsoft.Extensions.Options" version="2.0.0" />
<dependency id="System.Net.Security" version="4.3.2" />
<dependency id="System.Net.WebSockets" version="4.3.0" />
<dependency id="System.Net.WebSockets.Client" version="4.3.1" />
<dependency id="System.Threading.Thread" version="4.3.0" />
<dependency id="System.Net.WebSockets.Client" version="4.3.2" />
</group>
<group targetFramework="uap10.0">
<dependency id="Microsoft.NETCore.UniversalWindowsPlatform" version="5.4.0" />
<dependency id="Microsoft.Extensions.DependencyInjection" version="1.1.1" />
<dependency id="Microsoft.Extensions.Logging" version="1.1.2" />
<dependency id="Microsoft.Extensions.Options" version="1.1.2" />
<dependency id="Microsoft.NETCore.UniversalWindowsPlatform" version="6.1.4" />
</group>
<group targetFramework="net452">
<dependency id="Microsoft.Extensions.DependencyInjection" version="1.1.1" />
<dependency id="Microsoft.Extensions.Logging" version="1.1.2" />
<dependency id="Microsoft.Extensions.Options" version="1.1.2" />
</group>
<group targetFramework="net461">
</group>
</dependencies>
</metadata>
<files>
<!-- .NET Standard 1.3 -->
<file src="..\Frameworks\MQTTnet.Netstandard\bin\Release\netstandard1.3\MQTTnet.Core.*" target="lib\netstandard1.3\"/>
<file src="..\Frameworks\MQTTnet.Netstandard\bin\Release\netstandard1.3\MQTTnet.*" target="lib\netstandard1.3\"/>
<file src="..\Source\MQTTnet\bin\Release\netstandard1.3\MQTTnet.*" target="lib\netstandard1.3\"/>
<!-- .NET Standard 2.0 -->
<file src="..\Frameworks\MQTTnet.Netstandard\bin\Release\netstandard2.0\MQTTnet.Core.*" target="lib\netstandard2.0\"/>
<file src="..\Frameworks\MQTTnet.Netstandard\bin\Release\netstandard2.0\MQTTnet.*" target="lib\netstandard2.0\"/>
<file src="..\Source\MQTTnet\bin\Release\netstandard2.0\MQTTnet.*" target="lib\netstandard2.0\"/>
<!-- Universal Windows -->
<file src="..\Frameworks\MQTTnet.Netstandard\bin\Release\uap10.0\MQTTnet.Core.*" target="lib\uap10.0\"/>
<file src="..\Frameworks\MQTTnet.Netstandard\bin\Release\uap10.0\MQTTnet.*" target="lib\uap10.0\"/>
<file src="..\Source\MQTTnet\bin\Release\uap10.0\MQTTnet.*" target="lib\uap10.0\"/>
<!-- .NET Framework -->
<file src="..\Frameworks\MQTTnet.Netstandard\bin\Release\net452\MQTTnet.Core.*" target="lib\net452\"/>
<file src="..\Frameworks\MQTTnet.Netstandard\bin\Release\net452\MQTTnet.*" target="lib\net452\"/>
<file src="..\Source\MQTTnet\bin\Release\net452\MQTTnet.*" target="lib\net452\"/>
<file src="..\Source\MQTTnet\bin\Release\netstandard2.0\MQTTnet.*" target="lib\net461\"/>
<file src="..\Source\MQTTnet\bin\Release\netstandard2.0\MQTTnet.*" target="lib\net472\"/>
</files>
</package>
+36 -12
View File
@@ -1,6 +1,11 @@
param([string]$version)
param([string]$assemblyVersion, [string]$nugetVersion)
if ([string]::IsNullOrEmpty($version)) {$version = "0.0.1"}
if ([string]::IsNullOrEmpty($assemblyVersion)) {$assemblyVersion = "0.0.1"}
if ([string]::IsNullOrEmpty($nugetVersion)) {$nugetVersion = "0.0.1"}
Write-Host "Assembly version = $assemblyVersion"
Write-Host "Nuget version = $nugetVersion"
Write-Host
$vswhere = ${Env:\ProgramFiles(x86)} + '\Microsoft Visual Studio\Installer\vswhere'
@@ -8,16 +13,35 @@ $path = &$vswhere -latest -products * -requires Microsoft.Component.MSBuild -pro
if ($path) {
$msbuild = join-path $path 'MSBuild\15.0\Bin\MSBuild.exe'
&$msbuild ..\Frameworks\MQTTnet.Netstandard\MQTTnet.Netstandard.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net452" /p:FileVersion=$version /p:AssemblyVersion=$version /verbosity:m
&$msbuild ..\Frameworks\MQTTnet.Netstandard\MQTTnet.Netstandard.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard1.3" /p:FileVersion=$version /p:AssemblyVersion=$version /verbosity:m
&$msbuild ..\Frameworks\MQTTnet.Netstandard\MQTTnet.Netstandard.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.0" /p:FileVersion=$version /p:AssemblyVersion=$version /verbosity:m
&$msbuild ..\Frameworks\MQTTnet.Netstandard\MQTTnet.Netstandard.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="uap10.0" /p:FileVersion=$version /p:AssemblyVersion=$version /verbosity:m
# Build the core library
&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net452" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net461" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard1.3" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="uap10.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
&$msbuild ..\Frameworks\MQTTnet.AspNetCore\MQTTnet.AspNetCore.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.0" /p:FileVersion=$version /p:AssemblyVersion=$version /verbosity:m
# Build the ASP.NET Core 2.0 extension
&$msbuild ..\Source\MQTTnet.AspNetCore\MQTTnet.AspNetCore.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
# Build the RPC extension
&$msbuild ..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net452" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
&$msbuild ..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net461" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
&$msbuild ..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard1.3" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
&$msbuild ..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
&$msbuild ..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="uap10.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
# Build the Managed Client extension
&$msbuild ..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net452" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
&$msbuild ..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net461" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
&$msbuild ..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard1.3" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
&$msbuild ..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
&$msbuild ..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="uap10.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
Remove-Item .\NuGet -Force -Recurse -ErrorAction SilentlyContinue
Remove-Item .\NuGet -Force -Recurse
New-Item -ItemType Directory -Force -Path .\NuGet
.\NuGet.exe pack MQTTnet.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $version
.\NuGet.exe pack MQTTnet.AspNetCore.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $version
}
.\nuget.exe pack MQTTnet.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $nugetVersion
.\nuget.exe pack MQTTnet.AspNetCore.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $nugetVersion
.\nuget.exe pack MQTTnet.Extensions.Rpc.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $nugetVersion
.\nuget.exe pack MQTTnet.Extensions.ManagedClient.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $nugetVersion
}
Binary file not shown.
+9
View File
@@ -0,0 +1,9 @@
param([string]$apiKey)
$files = Get-ChildItem -Path ".\NuGet" -Filter "*.nupkg"
foreach ($file in $files)
{
Write-Host "Uploading: " $file
.\nuget.exe push $file.Fullname $apiKey -NoSymbols -Source https://api.nuget.org/v3/index.json
}
+9
View File
@@ -0,0 +1,9 @@
# Import codeSigningKey.pfx
In order to import the key for code signing on a new developer machine use the following command within the VS developer command line:
> sn –i codeSigningKey.pfx VS_KEY_EFCA4C5B6DFD4B4F
The container name may be different.
# Check if the assembly has a strong name
> sn -vf MQTTnet.dll
@@ -1,41 +0,0 @@
using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using MQTTnet.Core.Server;
namespace MQTTnet.AspNetCore
{
public static class ApplicationBuilderExtensions
{
public static IApplicationBuilder UseMqttEndpoint(this IApplicationBuilder app, string path = "/mqtt")
{
app.UseWebSockets();
app.Use(async (context, next) =>
{
if (context.Request.Path == path && context.WebSockets.IsWebSocketRequest)
{
var adapter = app.ApplicationServices.GetRequiredService<MqttWebSocketServerAdapter>();
using (var webSocket = await context.WebSockets.AcceptWebSocketAsync("mqtt"))
{
await adapter.AcceptWebSocketAsync(webSocket);
}
}
else
{
await next();
}
});
return app;
}
public static IApplicationBuilder UseMqttServer(this IApplicationBuilder app, Action<IMqttServer> configure)
{
var server = app.ApplicationServices.GetRequiredService<IMqttServer>();
configure(server);
return app;
}
}
}
@@ -1,17 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="2.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MQTTnet.Netstandard\MQTTnet.NetStandard.csproj" />
</ItemGroup>
</Project>
@@ -1,35 +0,0 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using MQTTnet.Core.Adapter;
using MQTTnet.Core.Server;
namespace MQTTnet.AspNetCore
{
public class MqttHostedServer : MqttServer, IHostedService
{
public MqttHostedServer(
IOptions<MqttServerOptions> options,
IEnumerable<IMqttServerAdapter> adapters,
ILogger<MqttServer> logger,
MqttClientSessionsManager clientSessionsManager,
IMqttClientRetainedMessageManager clientRetainedMessageManager
)
: base(options, adapters, logger, clientSessionsManager, clientRetainedMessageManager)
{
}
public Task StartAsync(CancellationToken cancellationToken)
{
return StartAsync();
}
public Task StopAsync(CancellationToken cancellationToken)
{
return StopAsync();
}
}
}
@@ -1,93 +0,0 @@
using System;
using System.IO;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Core.Adapter;
using MQTTnet.Core.Channel;
using MQTTnet.Core.Server;
using MQTTnet.Implementations;
namespace MQTTnet.AspNetCore
{
public class MqttWebSocketServerAdapter : IMqttServerAdapter, IDisposable
{
private readonly IMqttCommunicationAdapterFactory _mqttCommunicationAdapterFactory;
public MqttWebSocketServerAdapter(IMqttCommunicationAdapterFactory mqttCommunicationAdapterFactory)
{
_mqttCommunicationAdapterFactory = mqttCommunicationAdapterFactory ?? throw new ArgumentNullException(nameof(mqttCommunicationAdapterFactory));
}
public event EventHandler<MqttServerAdapterClientAcceptedEventArgs> ClientAccepted;
public Task StartAsync(MqttServerOptions options)
{
return Task.CompletedTask;
}
public Task StopAsync()
{
return Task.CompletedTask;
}
public Task AcceptWebSocketAsync(WebSocket webSocket)
{
if (webSocket == null) throw new ArgumentNullException(nameof(webSocket));
var channel = new MqttWebSocketServerChannel(webSocket);
var clientAdapter = _mqttCommunicationAdapterFactory.CreateServerCommunicationAdapter(channel);
var eventArgs = new MqttServerAdapterClientAcceptedEventArgs(clientAdapter);
ClientAccepted?.Invoke(this, eventArgs);
return eventArgs.SessionTask;
}
public void Dispose()
{
StopAsync();
}
private class MqttWebSocketServerChannel : IMqttCommunicationChannel, IDisposable
{
private readonly WebSocket _webSocket;
public MqttWebSocketServerChannel(WebSocket webSocket)
{
_webSocket = webSocket ?? throw new ArgumentNullException(nameof(webSocket));
RawReceiveStream = new WebSocketStream(_webSocket);
}
public Stream SendStream => RawReceiveStream;
public Stream ReceiveStream => RawReceiveStream;
public Stream RawReceiveStream { get; }
public Task ConnectAsync()
{
return Task.CompletedTask;
}
public Task DisconnectAsync()
{
RawReceiveStream?.Dispose();
if (_webSocket == null)
{
return Task.CompletedTask;
}
return _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
}
public void Dispose()
{
RawReceiveStream?.Dispose();
SendStream?.Dispose();
ReceiveStream?.Dispose();
_webSocket?.Dispose();
}
}
}
}
@@ -1,24 +0,0 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using MQTTnet.Core.Adapter;
using MQTTnet.Core.Server;
namespace MQTTnet.AspNetCore
{
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddHostedMqttServer(this IServiceCollection services)
{
services.AddMqttServerServices();
services.AddSingleton<IHostedService>(s => s.GetService<MqttHostedServer>());
services.AddSingleton<IMqttServer>(s => s.GetService<MqttHostedServer>());
services.AddSingleton<MqttHostedServer>();
services.AddSingleton<MqttWebSocketServerAdapter>();
services.AddSingleton<IMqttServerAdapter>(s => s.GetService<MqttWebSocketServerAdapter>());
return services;
}
}
}
@@ -1,76 +0,0 @@
#if WINDOWS_UWP
using System;
using System.Threading.Tasks;
using MQTTnet.Core.Adapter;
using MQTTnet.Core.Server;
using Windows.Networking.Sockets;
using Microsoft.Extensions.Logging;
namespace MQTTnet.Implementations
{
public class MqttServerAdapter : IMqttServerAdapter, IDisposable
{
private readonly ILogger<MqttServerAdapter> _logger;
private readonly IMqttCommunicationAdapterFactory _communicationAdapterFactory;
private StreamSocketListener _defaultEndpointSocket;
public MqttServerAdapter(ILogger<MqttServerAdapter> logger, IMqttCommunicationAdapterFactory communicationAdapterFactory)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_communicationAdapterFactory = communicationAdapterFactory ?? throw new ArgumentNullException(nameof(communicationAdapterFactory));
}
public event EventHandler<MqttServerAdapterClientAcceptedEventArgs> ClientAccepted;
public async Task StartAsync(MqttServerOptions options)
{
if (options == null) throw new ArgumentNullException(nameof(options));
if (_defaultEndpointSocket != null) throw new InvalidOperationException("Server is already started.");
if (options.DefaultEndpointOptions.IsEnabled)
{
_defaultEndpointSocket = new StreamSocketListener();
await _defaultEndpointSocket.BindServiceNameAsync(options.GetDefaultEndpointPort().ToString(), SocketProtectionLevel.PlainSocket);
_defaultEndpointSocket.ConnectionReceived += AcceptDefaultEndpointConnectionsAsync;
}
if (options.TlsEndpointOptions.IsEnabled)
{
throw new NotSupportedException("TLS servers are not supported for UWP apps.");
}
}
public Task StopAsync()
{
if (_defaultEndpointSocket != null)
{
_defaultEndpointSocket.ConnectionReceived -= AcceptDefaultEndpointConnectionsAsync;
}
_defaultEndpointSocket?.Dispose();
_defaultEndpointSocket = null;
return Task.FromResult(0);
}
public void Dispose()
{
StopAsync();
}
private void AcceptDefaultEndpointConnectionsAsync(StreamSocketListener sender, StreamSocketListenerConnectionReceivedEventArgs args)
{
try
{
var clientAdapter = _communicationAdapterFactory.CreateServerCommunicationAdapter(new MqttTcpChannel(args.Socket));
ClientAccepted?.Invoke(this, new MqttServerAdapterClientAcceptedEventArgs(clientAdapter));
}
catch (Exception exception)
{
_logger.LogError(new EventId(), exception, "Error while accepting connection at default endpoint.");
}
}
}
}
#endif
@@ -1,148 +0,0 @@
#if NET452 || NETSTANDARD1_3 || NETSTANDARD2_0
using System;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Core.Adapter;
using MQTTnet.Core.Server;
using Microsoft.Extensions.Logging;
namespace MQTTnet.Implementations
{
public class MqttServerAdapter : IMqttServerAdapter, IDisposable
{
private readonly ILogger<MqttServerAdapter> _logger;
private readonly IMqttCommunicationAdapterFactory _communicationAdapterFactory;
private CancellationTokenSource _cancellationTokenSource;
private Socket _defaultEndpointSocket;
private Socket _tlsEndpointSocket;
private X509Certificate2 _tlsCertificate;
public MqttServerAdapter(ILogger<MqttServerAdapter> logger, IMqttCommunicationAdapterFactory communicationAdapterFactory)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_communicationAdapterFactory = communicationAdapterFactory ?? throw new ArgumentNullException(nameof(communicationAdapterFactory));
}
public event EventHandler<MqttServerAdapterClientAcceptedEventArgs> ClientAccepted;
public Task StartAsync(MqttServerOptions options)
{
if (_cancellationTokenSource != null) throw new InvalidOperationException("Server is already started.");
_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);
if (!_tlsCertificate.HasPrivateKey)
{
throw new InvalidOperationException("The certificate for TLS encryption must contain the private key.");
}
_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);
}
return Task.FromResult(0);
}
public Task StopAsync()
{
_cancellationTokenSource?.Cancel(false);
_cancellationTokenSource?.Dispose();
_cancellationTokenSource = null;
_defaultEndpointSocket?.Dispose();
_defaultEndpointSocket = null;
_tlsCertificate = null;
_tlsEndpointSocket?.Dispose();
_tlsEndpointSocket = null;
return Task.FromResult(0);
}
public void Dispose()
{
StopAsync();
}
private async Task AcceptDefaultEndpointConnectionsAsync(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
try
{
//todo: else branch can be used with min dependency NET46
#if NET452
var clientSocket = await Task.Factory.FromAsync(_defaultEndpointSocket.BeginAccept, _defaultEndpointSocket.EndAccept, null).ConfigureAwait(false);
#else
var clientSocket = await _defaultEndpointSocket.AcceptAsync().ConfigureAwait(false);
#endif
var clientAdapter = _communicationAdapterFactory.CreateServerCommunicationAdapter(new MqttTcpChannel(clientSocket, null));
ClientAccepted?.Invoke(this, new MqttServerAdapterClientAcceptedEventArgs(clientAdapter));
}
catch (Exception exception)
{
_logger.LogError(new EventId(), 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
{
#if NET452
var clientSocket = await Task.Factory.FromAsync(_tlsEndpointSocket.BeginAccept, _tlsEndpointSocket.EndAccept, null).ConfigureAwait(false);
#else
var clientSocket = await _tlsEndpointSocket.AcceptAsync().ConfigureAwait(false);
#endif
var sslStream = new SslStream(new NetworkStream(clientSocket));
await sslStream.AuthenticateAsServerAsync(_tlsCertificate, false, SslProtocols.Tls12, false).ConfigureAwait(false);
var clientAdapter = _communicationAdapterFactory.CreateServerCommunicationAdapter(new MqttTcpChannel(clientSocket, sslStream));
ClientAccepted?.Invoke(this, new MqttServerAdapterClientAcceptedEventArgs(clientAdapter));
}
catch (Exception exception)
{
_logger.LogError(new EventId(), 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);
}
}
}
}
}
#endif
@@ -1,91 +0,0 @@
using MQTTnet.Core.Channel;
using MQTTnet.Core.Client;
using System;
using System.IO;
using System.Net.WebSockets;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks;
namespace MQTTnet.Implementations
{
public sealed class MqttWebSocketChannel : IMqttCommunicationChannel, IDisposable
{
private readonly MqttClientWebSocketOptions _options;
private ClientWebSocket _webSocket;
public MqttWebSocketChannel(MqttClientWebSocketOptions options)
{
_options = options ?? throw new ArgumentNullException(nameof(options));
}
public Stream SendStream => RawReceiveStream;
public Stream ReceiveStream => RawReceiveStream;
public Stream RawReceiveStream { get; private set; }
public async Task ConnectAsync()
{
var uri = _options.Uri;
if (!uri.StartsWith("ws://", StringComparison.OrdinalIgnoreCase))
{
uri = "ws://" + uri;
}
_webSocket = new ClientWebSocket();
if (_options.RequestHeaders != null)
{
foreach (var requestHeader in _options.RequestHeaders)
{
_webSocket.Options.SetRequestHeader(requestHeader.Key, requestHeader.Value);
}
}
if (_options.SubProtocols != null)
{
foreach (var subProtocol in _options.SubProtocols)
{
_webSocket.Options.AddSubProtocol(subProtocol);
}
}
if (_options.CookieContainer != null)
{
_webSocket.Options.Cookies = _options.CookieContainer;
}
if (_options.TlsOptions?.UseTls == true && _options.TlsOptions?.Certificates != null)
{
_webSocket.Options.ClientCertificates = new X509CertificateCollection();
foreach (var certificate in _options.TlsOptions.Certificates)
{
_webSocket.Options.ClientCertificates.Add(new X509Certificate(certificate));
}
}
await _webSocket.ConnectAsync(new Uri(uri), CancellationToken.None).ConfigureAwait(false);
RawReceiveStream = new WebSocketStream(_webSocket);
}
public async Task DisconnectAsync()
{
RawReceiveStream = null;
if (_webSocket == null)
{
return;
}
if (_webSocket.State == WebSocketState.Open || _webSocket.State == WebSocketState.Connecting)
{
await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None).ConfigureAwait(false);
}
}
public void Dispose()
{
_webSocket?.Dispose();
_webSocket = null;
}
}
}
@@ -1,86 +0,0 @@
using System;
using System.IO;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Core.Exceptions;
namespace MQTTnet.Implementations
{
public class WebSocketStream : Stream
{
private readonly WebSocket _webSocket;
public WebSocketStream(WebSocket webSocket)
{
_webSocket = webSocket ?? throw new ArgumentNullException(nameof(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;
count -= response.Count;
if (response.MessageType == WebSocketMessageType.Close)
{
await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, cancellationToken).ConfigureAwait(false);
}
}
if (_webSocket.State == WebSocketState.Closed)
{
throw new MqttCommunicationException( "connection closed" );
}
return currentOffset - offset;
}
public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
return _webSocket.SendAsync(new ArraySegment<byte>(buffer, offset, count), WebSocketMessageType.Binary, true, cancellationToken);
}
public override int Read(byte[] buffer, int offset, int count)
{
return ReadAsync(buffer, offset, count).GetAwaiter().GetResult();
}
public override void Write(byte[] buffer, int offset, int count)
{
WriteAsync(buffer, offset, count).GetAwaiter().GetResult();
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotSupportedException();
}
public override void SetLength(long value)
{
throw new NotSupportedException();
}
}
}
@@ -1,132 +0,0 @@
using System;
using MQTTnet.Core.Adapter;
using MQTTnet.Core.Client;
using MQTTnet.Core.Serializer;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using MQTTnet.Implementations;
using MQTTnet.Core.ManagedClient;
using MQTTnet.Core.Server;
using MQTTnet.Core.Channel;
namespace MQTTnet
{
public class MqttFactory : IMqttCommunicationAdapterFactory, IMqttClientSesssionFactory, IMqttClientFactory, IMqttServerFactory
{
private readonly IServiceProvider _serviceProvider;
public MqttFactory()
: this(BuildServiceProvider())
{
}
public MqttFactory(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public ILoggerFactory GetLoggerFactory()
{
return _serviceProvider.GetRequiredService<ILoggerFactory>();
}
public IMqttCommunicationAdapter CreateClientCommunicationAdapter(IMqttClientOptions options)
{
var logger = _serviceProvider.GetRequiredService<ILogger<MqttChannelCommunicationAdapter>>();
return new MqttChannelCommunicationAdapter(CreateCommunicationChannel(options.ChannelOptions), CreateSerializer(options.ProtocolVersion), logger);
}
public IMqttCommunicationAdapter CreateServerCommunicationAdapter(IMqttCommunicationChannel channel)
{
var serializer = _serviceProvider.GetRequiredService<IMqttPacketSerializer>();
var logger = _serviceProvider.GetRequiredService<ILogger<MqttChannelCommunicationAdapter>>();
return new MqttChannelCommunicationAdapter(channel, serializer, logger);
}
public IMqttCommunicationChannel CreateCommunicationChannel(IMqttClientChannelOptions options)
{
if (options == null) throw new ArgumentNullException(nameof(options));
switch (options)
{
case MqttClientTcpOptions tcpOptions:
return CreateTcpChannel(tcpOptions);
case MqttClientWebSocketOptions webSocketOptions:
return CreateWebSocketChannel(webSocketOptions);
default:
throw new NotSupportedException();
}
}
public MqttTcpChannel CreateTcpChannel(MqttClientTcpOptions tcpOptions)
{
return new MqttTcpChannel(tcpOptions);
}
public MqttWebSocketChannel CreateWebSocketChannel(MqttClientWebSocketOptions webSocketOptions)
{
return new MqttWebSocketChannel(webSocketOptions);
}
public MqttPacketSerializer CreateSerializer(MqttProtocolVersion protocolVersion)
{
return new MqttPacketSerializer
{
ProtocolVersion = protocolVersion
};
}
public MqttClientSession CreateClientSession(string clientId, MqttClientSessionsManager clientSessionsManager)
{
return new MqttClientSession(
clientId,
_serviceProvider.GetRequiredService<IOptions<MqttServerOptions>>(),
clientSessionsManager,
_serviceProvider.GetRequiredService<MqttClientSubscriptionsManager>(),
_serviceProvider.GetRequiredService<ILogger<MqttClientSession>>(),
_serviceProvider.GetRequiredService<ILogger<MqttClientPendingMessagesQueue>>(),
_serviceProvider.GetRequiredService<IMqttClientRetainedMessageManager>()
);
}
public IMqttClient CreateMqttClient()
{
return _serviceProvider.GetRequiredService<IMqttClient>();
}
public IManagedMqttClient CreateManagedMqttClient()
{
return _serviceProvider.GetRequiredService<IManagedMqttClient>();
}
public IMqttServer CreateMqttServer()
{
return _serviceProvider.GetRequiredService<IMqttServer>();
}
public IMqttServer CreateMqttServer(Action<MqttServerOptions> configure)
{
if (configure == null) throw new ArgumentNullException(nameof(configure));
var options = _serviceProvider.GetRequiredService<IOptions<MqttServerOptions>>();
configure(options.Value);
return _serviceProvider.GetRequiredService<IMqttServer>();
}
private static IServiceProvider BuildServiceProvider()
{
var serviceProvider = new ServiceCollection()
.AddMqttClient()
.AddMqttServer()
.AddLogging()
.BuildServiceProvider();
serviceProvider.GetRequiredService<ILoggerFactory>()
.AddMqttTrace();
return serviceProvider;
}
}
}
@@ -1,70 +0,0 @@
using Microsoft.Extensions.DependencyInjection;
using MQTTnet.Core.Adapter;
using MQTTnet.Core.Client;
using MQTTnet.Core.ManagedClient;
using MQTTnet.Core.Serializer;
using MQTTnet.Core.Server;
using MQTTnet.Implementations;
using System;
using Microsoft.Extensions.Logging;
using MQTTnet.Core.Diagnostics;
namespace MQTTnet
{
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddMqttServer(this IServiceCollection services)
{
services.AddMqttServerServices();
services.AddSingleton<IMqttServer>(s => s.GetService<MqttServer>());
services.AddSingleton<MqttServer>();
return services;
}
public static IServiceCollection AddMqttServerServices(this IServiceCollection services)
{
services.AddOptions();
services.AddSingleton<MqttFactory>();
services.AddSingleton<IMqttCommunicationAdapterFactory>(s => s.GetService<MqttFactory>());
services.AddSingleton<IMqttClientSesssionFactory>(s => s.GetService<MqttFactory>());
services.AddTransient<IMqttServerAdapter, MqttServerAdapter>();
services.AddTransient<IMqttPacketSerializer, MqttPacketSerializer>();
services.AddSingleton<MqttClientSessionsManager>();
services.AddTransient<MqttClientSubscriptionsManager>();
services.AddSingleton<IMqttClientRetainedMessageManager, MqttClientRetainedMessagesManager>();
return services;
}
public static IServiceCollection AddMqttServer(this IServiceCollection services, Action<MqttServerOptions> configureOptions)
{
return services
.AddMqttServer()
.Configure(configureOptions);
}
public static IServiceCollection AddMqttClient(this IServiceCollection services)
{
services.AddSingleton<MqttFactory>();
services.AddSingleton<IMqttCommunicationAdapterFactory>(s => s.GetService<MqttFactory>());
services.AddTransient<IMqttClient, MqttClient>();
services.AddTransient<MqttClient>();
services.AddTransient<IManagedMqttClient, ManagedMqttClient>();
services.AddTransient<ManagedMqttClient>();
services.AddTransient<IMqttPacketSerializer, MqttPacketSerializer>();
services.AddTransient<MqttPacketDispatcher>();
return services;
}
public static ILoggerFactory AddMqttTrace(this ILoggerFactory factory)
{
factory.AddProvider(new MqttNetTrace());
return factory;
}
}
}
@@ -1,22 +0,0 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Core.Packets;
using MQTTnet.Core.Serializer;
namespace MQTTnet.Core.Adapter
{
public interface IMqttCommunicationAdapter
{
IMqttPacketSerializer PacketSerializer { get; }
Task ConnectAsync(TimeSpan timeout);
Task DisconnectAsync(TimeSpan timeout);
Task SendPacketsAsync(TimeSpan timeout, CancellationToken cancellationToken, IEnumerable<MqttBasePacket> packets);
Task<MqttBasePacket> ReceivePacketAsync(TimeSpan timeout, CancellationToken cancellationToken);
}
}
@@ -1,12 +0,0 @@
using MQTTnet.Core.Client;
using MQTTnet.Core.Channel;
namespace MQTTnet.Core.Adapter
{
public interface IMqttCommunicationAdapterFactory
{
IMqttCommunicationAdapter CreateClientCommunicationAdapter(IMqttClientOptions options);
IMqttCommunicationAdapter CreateServerCommunicationAdapter(IMqttCommunicationChannel channel);
}
}
@@ -1,219 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Core.Channel;
using MQTTnet.Core.Exceptions;
using MQTTnet.Core.Internal;
using MQTTnet.Core.Packets;
using MQTTnet.Core.Serializer;
using Microsoft.Extensions.Logging;
namespace MQTTnet.Core.Adapter
{
public class MqttChannelCommunicationAdapter : IMqttCommunicationAdapter
{
private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
private readonly ILogger<MqttChannelCommunicationAdapter> _logger;
private readonly IMqttCommunicationChannel _channel;
public MqttChannelCommunicationAdapter(IMqttCommunicationChannel channel, IMqttPacketSerializer serializer, ILogger<MqttChannelCommunicationAdapter> logger)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_channel = channel ?? throw new ArgumentNullException(nameof(channel));
PacketSerializer = serializer ?? throw new ArgumentNullException(nameof(serializer));
}
public IMqttPacketSerializer PacketSerializer { get; }
public async Task ConnectAsync(TimeSpan timeout)
{
try
{
await _channel.ConnectAsync().TimeoutAfter(timeout).ConfigureAwait(false);
}
catch (TaskCanceledException)
{
throw;
}
catch (OperationCanceledException)
{
throw;
}
catch (MqttCommunicationTimedOutException)
{
throw;
}
catch (MqttCommunicationException)
{
throw;
}
catch (Exception exception)
{
throw new MqttCommunicationException(exception);
}
}
public async Task DisconnectAsync(TimeSpan timeout)
{
try
{
await _channel.DisconnectAsync().TimeoutAfter(timeout).ConfigureAwait(false);
}
catch (TaskCanceledException)
{
throw;
}
catch (OperationCanceledException)
{
throw;
}
catch (MqttCommunicationTimedOutException)
{
throw;
}
catch (MqttCommunicationException)
{
throw;
}
catch (Exception exception)
{
throw new MqttCommunicationException(exception);
}
}
public async Task SendPacketsAsync(TimeSpan timeout, CancellationToken cancellationToken, IEnumerable<MqttBasePacket> packets)
{
try
{
await _semaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
foreach (var packet in packets)
{
if (packet == null)
{
continue;
}
_logger.LogInformation("TX >>> {0} [Timeout={1}]", packet, timeout);
var writeBuffer = PacketSerializer.Serialize(packet);
await _channel.SendStream.WriteAsync(writeBuffer, 0, writeBuffer.Length, cancellationToken).ConfigureAwait(false);
}
if (timeout > TimeSpan.Zero)
{
await _channel.SendStream.FlushAsync(cancellationToken).TimeoutAfter(timeout).ConfigureAwait(false);
}
else
{
await _channel.SendStream.FlushAsync(cancellationToken).ConfigureAwait(false);
}
}
catch (TaskCanceledException)
{
throw;
}
catch (OperationCanceledException)
{
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)
{
ReceivedMqttPacket receivedMqttPacket = null;
try
{
if (timeout > TimeSpan.Zero)
{
receivedMqttPacket = await ReceiveAsync(_channel.ReceiveStream, cancellationToken).TimeoutAfter(timeout).ConfigureAwait(false);
}
else
{
receivedMqttPacket = await ReceiveAsync(_channel.ReceiveStream, cancellationToken).ConfigureAwait(false);
}
if (receivedMqttPacket == null || cancellationToken.IsCancellationRequested)
{
throw new TaskCanceledException();
}
var packet = PacketSerializer.Deserialize(receivedMqttPacket);
if (packet == null)
{
throw new MqttProtocolViolationException("Received malformed packet.");
}
_logger.LogInformation("RX <<< {0}", packet);
return packet;
}
catch (TaskCanceledException)
{
throw;
}
catch (OperationCanceledException)
{
throw;
}
catch (MqttCommunicationTimedOutException)
{
throw;
}
catch (MqttCommunicationException)
{
throw;
}
catch (Exception exception)
{
throw new MqttCommunicationException(exception);
}
finally
{
receivedMqttPacket?.Dispose();
}
}
private static async Task<ReceivedMqttPacket> ReceiveAsync(Stream stream, CancellationToken cancellationToken)
{
var header = MqttPacketReader.ReadHeaderFromSource(stream, cancellationToken);
if (header == null)
{
return null;
}
if (header.BodyLength == 0)
{
return new ReceivedMqttPacket(header, new MemoryStream(0));
}
var body = new byte[header.BodyLength];
var offset = 0;
do
{
var readBytesCount = await stream.ReadAsync(body, offset, body.Length - offset, cancellationToken).ConfigureAwait(false);
offset += readBytesCount;
} while (offset < header.BodyLength);
return new ReceivedMqttPacket(header, new MemoryStream(body, 0, body.Length));
}
}
}
@@ -1,17 +0,0 @@
using System;
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)
{
if (adapter == null) throw new ArgumentNullException(nameof(adapter));
return adapter.SendPacketsAsync(timeout, cancellationToken, packets);
}
}
}
@@ -1,24 +0,0 @@
using System;
using System.IO;
using MQTTnet.Core.Packets;
namespace MQTTnet.Core.Adapter
{
public sealed class ReceivedMqttPacket : IDisposable
{
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; }
public void Dispose()
{
Body?.Dispose();
}
}
}
@@ -1,16 +0,0 @@
using System;
using System.Threading.Tasks;
namespace MQTTnet.Core
{
public static class ApplicationMessagePublisherExtensions
{
public static Task PublishAsync(this IApplicationMessagePublisher client, params MqttApplicationMessage[] applicationMessages)
{
if (client == null) throw new ArgumentNullException(nameof(client));
if (applicationMessages == null) throw new ArgumentNullException(nameof(applicationMessages));
return client.PublishAsync(applicationMessages);
}
}
}
@@ -1,14 +0,0 @@
using System.Threading.Tasks;
using System.IO;
namespace MQTTnet.Core.Channel
{
public interface IMqttCommunicationChannel
{
Stream SendStream { get; }
Stream ReceiveStream { get; }
Task ConnectAsync();
Task DisconnectAsync();
}
}
-11
View File
@@ -1,11 +0,0 @@
using MQTTnet.Core.ManagedClient;
namespace MQTTnet.Core.Client
{
public interface IMqttClientFactory
{
IMqttClient CreateMqttClient();
IManagedMqttClient CreateManagedMqttClient();
}
}
-473
View File
@@ -1,473 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Core.Adapter;
using MQTTnet.Core.Exceptions;
using MQTTnet.Core.Internal;
using MQTTnet.Core.Packets;
using MQTTnet.Core.Protocol;
using Microsoft.Extensions.Logging;
namespace MQTTnet.Core.Client
{
public class MqttClient : IMqttClient
{
private readonly HashSet<ushort> _unacknowledgedPublishPackets = new HashSet<ushort>();
private readonly MqttPacketDispatcher _packetDispatcher;
private readonly IMqttCommunicationAdapterFactory _communicationAdapterFactory;
private readonly ILogger<MqttClient> _logger;
private IMqttClientOptions _options;
private bool _isReceivingPackets;
private int _latestPacketIdentifier;
private CancellationTokenSource _cancellationTokenSource;
private IMqttCommunicationAdapter _adapter;
private IDisposable _scopeHandle;
public MqttClient(IMqttCommunicationAdapterFactory communicationAdapterFactory, ILogger<MqttClient> logger, MqttPacketDispatcher packetDispatcher)
{
_communicationAdapterFactory = communicationAdapterFactory ?? throw new ArgumentNullException(nameof(communicationAdapterFactory));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_packetDispatcher = packetDispatcher ?? throw new ArgumentNullException(nameof(packetDispatcher));
}
public event EventHandler<MqttClientConnectedEventArgs> Connected;
public event EventHandler<MqttClientDisconnectedEventArgs> Disconnected;
public event EventHandler<MqttApplicationMessageReceivedEventArgs> ApplicationMessageReceived;
public bool IsConnected { get; private set; }
public async Task<MqttClientConnectResult> ConnectAsync(IMqttClientOptions options)
{
if (options == null) throw new ArgumentNullException(nameof(options));
if (options.ChannelOptions == null) throw new ArgumentException("ChannelOptions are not set.");
ThrowIfConnected("It is not allowed to connect with a server after the connection is established.");
try
{
_options = options;
_cancellationTokenSource = new CancellationTokenSource();
_latestPacketIdentifier = 0;
_packetDispatcher.Reset();
_adapter = _communicationAdapterFactory.CreateClientCommunicationAdapter(options);
_scopeHandle = _logger.BeginScope(options.LogId ?? options.ClientId);
_logger.LogTrace("Trying to connect with server.");
await _adapter.ConnectAsync(_options.CommunicationTimeout).ConfigureAwait(false);
_logger.LogTrace("Connection with server established.");
await SetupIncomingPacketProcessingAsync().ConfigureAwait(false);
var connectResponse = await AuthenticateAsync(options.WillMessage).ConfigureAwait(false);
_logger.LogTrace("MQTT connection with server established.");
if (_options.KeepAlivePeriod != TimeSpan.Zero)
{
StartSendKeepAliveMessages(_cancellationTokenSource.Token);
}
IsConnected = true;
Connected?.Invoke(this, new MqttClientConnectedEventArgs(connectResponse.IsSessionPresent));
return new MqttClientConnectResult(connectResponse.IsSessionPresent);
}
catch (Exception exception)
{
_logger.LogError(new EventId(), exception, "Error while connecting with server.");
await DisconnectInternalAsync().ConfigureAwait(false);
throw;
}
}
public async Task DisconnectAsync()
{
if (!IsConnected)
{
return;
}
try
{
await SendAsync(new MqttDisconnectPacket()).ConfigureAwait(false);
}
finally
{
await DisconnectInternalAsync().ConfigureAwait(false);
_scopeHandle.Dispose();
}
}
public async Task<IList<MqttSubscribeResult>> SubscribeAsync(IEnumerable<TopicFilter> topicFilters)
{
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
ThrowIfNotConnected();
var subscribePacket = new MqttSubscribePacket
{
PacketIdentifier = GetNewPacketIdentifier(),
TopicFilters = topicFilters.ToList()
};
var response = await SendAndReceiveAsync<MqttSubAckPacket>(subscribePacket).ConfigureAwait(false);
if (response.SubscribeReturnCodes.Count != subscribePacket.TopicFilters.Count)
{
throw new MqttProtocolViolationException("The return codes are not matching the topic filters [MQTT-3.9.3-1].");
}
return subscribePacket.TopicFilters.Select((t, i) => new MqttSubscribeResult(t, response.SubscribeReturnCodes[i])).ToList();
}
public async Task UnsubscribeAsync(IEnumerable<string> topicFilters)
{
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
ThrowIfNotConnected();
var unsubscribePacket = new MqttUnsubscribePacket
{
PacketIdentifier = GetNewPacketIdentifier(),
TopicFilters = topicFilters.ToList()
};
await SendAndReceiveAsync<MqttUnsubAckPacket>(unsubscribePacket);
}
public async Task PublishAsync(IEnumerable<MqttApplicationMessage> applicationMessages)
{
ThrowIfNotConnected();
var publishPackets = applicationMessages.Select(m => m.ToPublishPacket());
foreach (var qosGroup in publishPackets.GroupBy(p => p.QualityOfServiceLevel))
{
var qosPackets = qosGroup.ToArray();
switch (qosGroup.Key)
{
case MqttQualityOfServiceLevel.AtMostOnce:
{
// No packet identifier is used for QoS 0 [3.3.2.2 Packet Identifier]
await _adapter.SendPacketsAsync(_options.CommunicationTimeout, _cancellationTokenSource.Token, qosPackets).ConfigureAwait(false);
break;
}
case MqttQualityOfServiceLevel.AtLeastOnce:
{
foreach (var publishPacket in qosPackets)
{
publishPacket.PacketIdentifier = GetNewPacketIdentifier();
await SendAndReceiveAsync<MqttPubAckPacket>(publishPacket).ConfigureAwait(false);
}
break;
}
case MqttQualityOfServiceLevel.ExactlyOnce:
{
foreach (var publishPacket in qosPackets)
{
publishPacket.PacketIdentifier = GetNewPacketIdentifier();
var pubRecPacket = await SendAndReceiveAsync<MqttPubRecPacket>(publishPacket).ConfigureAwait(false);
await SendAndReceiveAsync<MqttPubCompPacket>(pubRecPacket.CreateResponse<MqttPubRelPacket>()).ConfigureAwait(false);
}
break;
}
default:
{
throw new InvalidOperationException();
}
}
}
}
private async Task<MqttConnAckPacket> AuthenticateAsync(MqttApplicationMessage willApplicationMessage)
{
var connectPacket = new MqttConnectPacket
{
ClientId = _options.ClientId,
Username = _options.Credentials?.Username,
Password = _options.Credentials?.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);
}
return response;
}
private async Task SetupIncomingPacketProcessingAsync()
{
_isReceivingPackets = false;
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
Task.Factory.StartNew(
() => ReceivePackets(_cancellationTokenSource.Token),
_cancellationTokenSource.Token,
TaskCreationOptions.LongRunning,
TaskScheduler.Default).ConfigureAwait(false);
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
while (!_isReceivingPackets && _cancellationTokenSource != null && !_cancellationTokenSource.IsCancellationRequested)
{
await Task.Delay(TimeSpan.FromMilliseconds(100)).ConfigureAwait(false);
}
}
private void ThrowIfNotConnected()
{
if (!IsConnected) throw new MqttCommunicationException("The client is not connected.");
}
private void ThrowIfConnected(string message)
{
if (IsConnected) throw new MqttProtocolViolationException(message);
}
private async Task DisconnectInternalAsync()
{
var clientWasConnected = IsConnected;
IsConnected = false;
var cts = _cancellationTokenSource;
if (cts == null || cts.IsCancellationRequested)
{
return;
}
cts.Cancel(false);
cts.Dispose();
_cancellationTokenSource = null;
try
{
await _adapter.DisconnectAsync(_options.CommunicationTimeout).ConfigureAwait(false);
_logger.LogInformation("Disconnected from adapter.");
}
catch (Exception exception)
{
_logger.LogWarning(new EventId(), exception, "Error while disconnecting from adapter.");
}
finally
{
Disconnected?.Invoke(this, new MqttClientDisconnectedEventArgs(clientWasConnected));
}
}
private async Task ProcessReceivedPacketAsync(MqttBasePacket packet)
{
try
{
_logger.LogInformation("Received <<< {0}", packet);
if (packet is MqttPingReqPacket)
{
await SendAsync(new MqttPingRespPacket());
return;
}
if (packet is MqttDisconnectPacket)
{
await DisconnectAsync();
return;
}
if (packet is MqttPublishPacket publishPacket)
{
await ProcessReceivedPublishPacket(publishPacket);
return;
}
if (packet is MqttPubRelPacket pubRelPacket)
{
await ProcessReceivedPubRelPacket(pubRelPacket);
return;
}
_packetDispatcher.Dispatch(packet);
}
catch (Exception exception)
{
_logger.LogError(new EventId(), exception, "Unhandled exception while processing received packet.");
}
}
private void FireApplicationMessageReceivedEvent(MqttPublishPacket publishPacket)
{
try
{
var applicationMessage = publishPacket.ToApplicationMessage();
ApplicationMessageReceived?.Invoke(this, new MqttApplicationMessageReceivedEventArgs(_options.ClientId, applicationMessage));
}
catch (Exception exception)
{
_logger.LogError(new EventId(), exception, "Unhandled exception while handling application message.");
}
}
private async Task ProcessReceivedPublishPacket(MqttPublishPacket publishPacket)
{
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce)
{
FireApplicationMessageReceivedEvent(publishPacket);
return;
}
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtLeastOnce)
{
FireApplicationMessageReceivedEvent(publishPacket);
await SendAsync(new MqttPubAckPacket {PacketIdentifier = publishPacket.PacketIdentifier});
return;
}
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.ExactlyOnce)
{
// QoS 2 is implement as method "B" [4.3.3 QoS 2: Exactly once delivery]
lock (_unacknowledgedPublishPackets)
{
_unacknowledgedPublishPackets.Add(publishPacket.PacketIdentifier);
}
FireApplicationMessageReceivedEvent(publishPacket);
await SendAsync(new MqttPubRecPacket {PacketIdentifier = publishPacket.PacketIdentifier});
return;
}
throw new MqttCommunicationException("Received a not supported QoS level.");
}
private Task ProcessReceivedPubRelPacket(MqttPubRelPacket pubRelPacket)
{
lock (_unacknowledgedPublishPackets)
{
_unacknowledgedPublishPackets.Remove(pubRelPacket.PacketIdentifier);
}
return SendAsync(pubRelPacket.CreateResponse<MqttPubCompPacket>());
}
private Task SendAsync(MqttBasePacket packet)
{
return _adapter.SendPacketsAsync(_options.CommunicationTimeout, _cancellationTokenSource.Token, packet);
}
private async Task<TResponsePacket> SendAndReceiveAsync<TResponsePacket>(MqttBasePacket requestPacket) where TResponsePacket : MqttBasePacket
{
var packetAwaiter = _packetDispatcher.WaitForPacketAsync(requestPacket, typeof(TResponsePacket), _options.CommunicationTimeout);
await _adapter.SendPacketsAsync(_options.CommunicationTimeout, _cancellationTokenSource.Token, requestPacket).ConfigureAwait(false);
return (TResponsePacket) await packetAwaiter.ConfigureAwait(false);
}
private ushort GetNewPacketIdentifier()
{
return (ushort) Interlocked.Increment(ref _latestPacketIdentifier);
}
private async Task SendKeepAliveMessagesAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Start sending keep alive packets.");
try
{
while (!cancellationToken.IsCancellationRequested)
{
await Task.Delay(_options.KeepAlivePeriod, cancellationToken).ConfigureAwait(false);
if (cancellationToken.IsCancellationRequested)
{
return;
}
await SendAndReceiveAsync<MqttPingRespPacket>(new MqttPingReqPacket()).ConfigureAwait(false);
}
}
catch (OperationCanceledException)
{
}
catch (MqttCommunicationException exception)
{
if (cancellationToken.IsCancellationRequested)
{
return;
}
_logger.LogWarning(new EventId(), exception, "MQTT communication exception while sending/receiving keep alive packets.");
await DisconnectInternalAsync().ConfigureAwait(false);
}
catch (Exception exception)
{
_logger.LogWarning(new EventId(), exception, "Unhandled exception while sending/receiving keep alive packets.");
await DisconnectInternalAsync().ConfigureAwait(false);
}
finally
{
_logger.LogInformation("Stopped sending keep alive packets.");
}
}
private async Task ReceivePackets(CancellationToken cancellationToken)
{
_logger.LogInformation("Start receiving packets.");
try
{
while (!cancellationToken.IsCancellationRequested)
{
_isReceivingPackets = true;
var packet = await _adapter.ReceivePacketAsync(TimeSpan.Zero, cancellationToken).ConfigureAwait(false);
if (cancellationToken.IsCancellationRequested)
{
return;
}
StartProcessReceivedPacket(packet, cancellationToken);
}
}
catch (OperationCanceledException)
{
}
catch (MqttCommunicationException exception)
{
if (cancellationToken.IsCancellationRequested)
{
return;
}
_logger.LogWarning(new EventId(), exception, "MQTT communication exception while receiving packets.");
await DisconnectInternalAsync().ConfigureAwait(false);
}
catch (Exception exception)
{
_logger.LogError(new EventId(), exception, "Unhandled exception while receiving packets.");
await DisconnectInternalAsync().ConfigureAwait(false);
}
finally
{
_logger.LogInformation(nameof(MqttClient), "Stopped receiving packets.");
}
}
private void StartProcessReceivedPacket(MqttBasePacket packet, CancellationToken cancellationToken)
{
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
Task.Run(() => ProcessReceivedPacketAsync(packet), cancellationToken).ConfigureAwait(false);
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
}
private void StartSendKeepAliveMessages(CancellationToken cancellationToken)
{
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
Task.Factory.StartNew(() => SendKeepAliveMessagesAsync(cancellationToken), cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Default)
.ConfigureAwait(false);
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
}
}
}
@@ -1,26 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace MQTTnet.Core.Client
{
public static class MqttClientExtensions
{
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());
}
}
}
-103
View File
@@ -1,103 +0,0 @@
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
using MQTTnet.Core.Exceptions;
using MQTTnet.Core.Internal;
using MQTTnet.Core.Packets;
using Microsoft.Extensions.Logging;
namespace MQTTnet.Core.Client
{
public class MqttPacketDispatcher
{
private readonly ConcurrentDictionary<Type, TaskCompletionSource<MqttBasePacket>> _packetByResponseType = new ConcurrentDictionary<Type, TaskCompletionSource<MqttBasePacket>>();
private readonly ConcurrentDictionary<Type, ConcurrentDictionary<ushort, TaskCompletionSource<MqttBasePacket>>> _packetByResponseTypeAndIdentifier = new ConcurrentDictionary<Type, ConcurrentDictionary<ushort, TaskCompletionSource<MqttBasePacket>>>();
private readonly ILogger<MqttPacketDispatcher> _logger;
public MqttPacketDispatcher(ILogger<MqttPacketDispatcher> logger)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public async Task<MqttBasePacket> WaitForPacketAsync(MqttBasePacket request, Type responseType, TimeSpan timeout)
{
if (request == null) throw new ArgumentNullException(nameof(request));
var packetAwaiter = AddPacketAwaiter(request, responseType);
try
{
return await packetAwaiter.Task.TimeoutAfter(timeout).ConfigureAwait(false);
}
catch (MqttCommunicationTimedOutException)
{
_logger.LogWarning("Timeout while waiting for packet of type '{0}'.", responseType.Name);
throw;
}
finally
{
RemovePacketAwaiter(request, responseType);
}
}
public void Dispatch(MqttBasePacket packet)
{
if (packet == null) throw new ArgumentNullException(nameof(packet));
var type = packet.GetType();
if (packet is IMqttPacketWithIdentifier withIdentifier)
{
if (_packetByResponseTypeAndIdentifier.TryGetValue(type, out var byid))
{
if (byid.TryRemove(withIdentifier.PacketIdentifier, out var tcs))
{
tcs.TrySetResult(packet);
return;
}
}
}
else if (_packetByResponseType.TryRemove(type, out var tcs))
{
tcs.TrySetResult(packet);
return;
}
throw new InvalidOperationException($"Packet of type '{type.Name}' not handled or dispatched.");
}
public void Reset()
{
_packetByResponseTypeAndIdentifier.Clear();
_packetByResponseType.Clear();
}
private TaskCompletionSource<MqttBasePacket> AddPacketAwaiter(MqttBasePacket request, Type responseType)
{
var tcs = new TaskCompletionSource<MqttBasePacket>();
if (request is IMqttPacketWithIdentifier requestWithIdentifier)
{
var byId = _packetByResponseTypeAndIdentifier.GetOrAdd(responseType, key => new ConcurrentDictionary<ushort, TaskCompletionSource<MqttBasePacket>>());
byId[requestWithIdentifier.PacketIdentifier] = tcs;
}
else
{
_packetByResponseType[responseType] = tcs;
}
return tcs;
}
private void RemovePacketAwaiter(MqttBasePacket request, Type responseType)
{
if (request is IMqttPacketWithIdentifier requestWithIdentifier)
{
var byId = _packetByResponseTypeAndIdentifier.GetOrAdd(responseType, key => new ConcurrentDictionary<ushort, TaskCompletionSource<MqttBasePacket>>());
byId.TryRemove(requestWithIdentifier.PacketIdentifier, out var _);
}
else
{
_packetByResponseType.TryRemove(responseType, out var _);
}
}
}
}
-53
View File
@@ -1,53 +0,0 @@
using System;
using Microsoft.Extensions.Logging;
namespace MQTTnet.Core.Diagnostics
{
public class MqttNetLogger : ILogger
{
private readonly string _categoryName;
private readonly MqttNetTrace _mqttNetTrace;
public MqttNetLogger(string categoryName, MqttNetTrace mqttNetTrace)
{
_categoryName = categoryName;
_mqttNetTrace = mqttNetTrace;
}
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
if (formatter == null)
{
throw new ArgumentNullException(nameof(formatter));
}
if (!MqttNetTrace.HasListeners)
{
return;
}
var message = formatter(state, exception);
var traceMessage = new MqttNetTraceMessage(DateTime.Now, Environment.CurrentManagedThreadId, _categoryName, logLevel, message, exception);
_mqttNetTrace.Publish(traceMessage);
}
public bool IsEnabled(LogLevel logLevel)
{
return MqttNetTrace.HasListeners;
}
//not supported: async local requires netstandard1.3
//for implementation see https://github.com/aspnet/Logging/blob/dev/src/Microsoft.Extensions.Logging.Console/ConsoleLogScope.cs
public IDisposable BeginScope<TState>(TState state)
{
return new DisposableScope();
}
private class DisposableScope : IDisposable
{
public void Dispose()
{
}
}
}
}
-35
View File
@@ -1,35 +0,0 @@
using System;
using System.Collections.Concurrent;
using Microsoft.Extensions.Logging;
namespace MQTTnet.Core.Diagnostics
{
public class MqttNetTrace : ILoggerProvider
{
private readonly ConcurrentDictionary<string, MqttNetLogger> _loggers = new ConcurrentDictionary<string, MqttNetLogger>();
public static event EventHandler<MqttNetTraceMessagePublishedEventArgs> TraceMessagePublished;
public static bool HasListeners => TraceMessagePublished != null;
public void Publish(MqttNetTraceMessage traceMessage)
{
TraceMessagePublished?.Invoke(this, new MqttNetTraceMessagePublishedEventArgs(traceMessage));
}
public void Dispose()
{
TraceMessagePublished = null;
}
public ILogger CreateLogger(string categoryName)
{
return _loggers.GetOrAdd(categoryName, CreateLoggerImplementation);
}
private MqttNetLogger CreateLoggerImplementation(string categoryName)
{
return new MqttNetLogger(categoryName, this);
}
}
}
@@ -1,14 +0,0 @@
using System;
namespace MQTTnet.Core.Diagnostics
{
public sealed class MqttNetTraceMessagePublishedEventArgs : EventArgs
{
public MqttNetTraceMessagePublishedEventArgs(MqttNetTraceMessage traceMessage)
{
TraceMessage = traceMessage ?? throw new ArgumentNullException(nameof(traceMessage));
}
public MqttNetTraceMessage TraceMessage { get; }
}
}
@@ -1,30 +0,0 @@
using System;
using Microsoft.Extensions.Logging;
namespace MQTTnet.Core.Diagnostics
{
public sealed class MqttNetTraceMessage
{
public MqttNetTraceMessage(DateTime timestamp, int threadId, string source, LogLevel level, string message, Exception exception)
{
Timestamp = timestamp;
ThreadId = threadId;
Source = source;
Level = level;
Message = message;
Exception = exception;
}
public DateTime Timestamp { get; }
public int ThreadId { get; }
public string Source { get; }
public LogLevel Level { get; }
public string Message { get; }
public Exception Exception { get; }
}
}
@@ -1,6 +0,0 @@
namespace MQTTnet.Core.Exceptions
{
public sealed class MqttCommunicationTimedOutException : MqttCommunicationException
{
}
}
@@ -1,10 +0,0 @@
using System.Collections.Generic;
using System.Threading.Tasks;
namespace MQTTnet.Core
{
public interface IApplicationMessagePublisher
{
Task PublishAsync(IEnumerable<MqttApplicationMessage> applicationMessages);
}
}
-74
View File
@@ -1,74 +0,0 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Core.Exceptions;
namespace MQTTnet.Core.Internal
{
public static class TaskExtensions
{
public static async Task TimeoutAfter(this Task task, TimeSpan timeout)
{
using (var cancellationTokenSource = new CancellationTokenSource())
{
try
{
var timeoutTask = Task.Delay(timeout, cancellationTokenSource.Token);
var finishedTask = await Task.WhenAny(timeoutTask, task).ConfigureAwait(false);
if (finishedTask == timeoutTask)
{
throw new MqttCommunicationTimedOutException();
}
if (task.IsCanceled)
{
throw new TaskCanceledException();
}
if (task.IsFaulted)
{
throw new MqttCommunicationException(task.Exception.GetBaseException());
}
}
finally
{
cancellationTokenSource.Cancel();
}
}
}
public static async Task<TResult> TimeoutAfter<TResult>(this Task<TResult> task, TimeSpan timeout)
{
using (var cancellationTokenSource = new CancellationTokenSource())
{
try
{
var timeoutTask = Task.Delay(timeout, cancellationTokenSource.Token);
var finishedTask = await Task.WhenAny(timeoutTask, task).ConfigureAwait(false);
if (finishedTask == timeoutTask)
{
throw new MqttCommunicationTimedOutException();
}
if (task.IsCanceled)
{
throw new TaskCanceledException();
}
if (task.IsFaulted)
{
throw new MqttCommunicationException(task.Exception.GetBaseException());
}
return task.Result;
}
finally
{
cancellationTokenSource.Cancel();
}
}
}
}
}
-31
View File
@@ -1,31 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.1</TargetFramework>
<AssemblyName>MQTTnet.Core</AssemblyName>
<RootNamespace>MQTTnet.Core</RootNamespace>
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
<DebugType>Full</DebugType>
<Description></Description>
<Product></Product>
<Company></Company>
<Authors></Authors>
<Version>0.0.0.0</Version>
<PackageId></PackageId>
<Copyright></Copyright>
<PackageProjectUrl></PackageProjectUrl>
<PackageIconUrl></PackageIconUrl>
<RepositoryUrl></RepositoryUrl>
<PackageTags></PackageTags>
<FileVersion>2.5.0.0</FileVersion>
<AssemblyVersion>2.5.0.0</AssemblyVersion>
<PackageLicenseUrl></PackageLicenseUrl>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="1.1.2" />
<PackageReference Include="Microsoft.Extensions.Options" Version="1.1.2" />
<PackageReference Include="System.Threading" Version="4.3.0" />
</ItemGroup>
</Project>
@@ -1,21 +0,0 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using MQTTnet.Core.Client;
namespace MQTTnet.Core.ManagedClient
{
public interface IManagedMqttClient : IApplicationMessageReceiver, IApplicationMessagePublisher
{
bool IsConnected { get; }
event EventHandler<MqttClientConnectedEventArgs> Connected;
event EventHandler<MqttClientDisconnectedEventArgs> Disconnected;
Task StartAsync(IManagedMqttClientOptions options);
Task StopAsync();
Task SubscribeAsync(IEnumerable<TopicFilter> topicFilters);
Task UnsubscribeAsync(IEnumerable<TopicFilter> topicFilters);
}
}
@@ -1,12 +0,0 @@
using System.Collections.Generic;
using System.Threading.Tasks;
namespace MQTTnet.Core.ManagedClient
{
public interface IManagedMqttClientStorage
{
Task SaveQueuedMessagesAsync(IList<MqttApplicationMessage> messages);
Task<IList<MqttApplicationMessage>> LoadQueuedMessagesAsync();
}
}
@@ -1,303 +0,0 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Core.Client;
using MQTTnet.Core.Exceptions;
using MQTTnet.Core.Protocol;
using Microsoft.Extensions.Logging;
namespace MQTTnet.Core.ManagedClient
{
public class ManagedMqttClient : IManagedMqttClient
{
private readonly ManagedMqttClientStorageManager _storageManager = new ManagedMqttClientStorageManager();
private readonly BlockingCollection<MqttApplicationMessage> _messageQueue = new BlockingCollection<MqttApplicationMessage>();
private readonly HashSet<TopicFilter> _subscriptions = new HashSet<TopicFilter>();
private readonly IMqttClient _mqttClient;
private readonly ILogger<ManagedMqttClient> _logger;
private CancellationTokenSource _connectionCancellationToken;
private CancellationTokenSource _publishingCancellationToken;
private IManagedMqttClientOptions _options;
private bool _subscriptionsNotPushed;
public ManagedMqttClient(ILogger<ManagedMqttClient> logger, IMqttClient mqttClient)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_mqttClient = mqttClient ?? throw new ArgumentNullException(nameof(mqttClient));
_mqttClient.Connected += OnConnected;
_mqttClient.Disconnected += OnDisconnected;
_mqttClient.ApplicationMessageReceived += OnApplicationMessageReceived;
}
public bool IsConnected => _mqttClient.IsConnected;
public event EventHandler<MqttClientConnectedEventArgs> Connected;
public event EventHandler<MqttClientDisconnectedEventArgs> Disconnected;
public event EventHandler<MqttApplicationMessageReceivedEventArgs> ApplicationMessageReceived;
public async Task StartAsync(IManagedMqttClientOptions options)
{
if (options == null) throw new ArgumentNullException(nameof(options));
if (options.ClientOptions == null) throw new ArgumentException("The client options are not set.", nameof(options));
if (!options.ClientOptions.CleanSession)
{
throw new NotSupportedException("The managed client does not support existing sessions.");
}
if (_connectionCancellationToken != null)
{
throw new InvalidOperationException("The managed client is already started.");
}
_options = options;
await _storageManager.SetStorageAsync(_options.Storage).ConfigureAwait(false);
if (_options.Storage != null)
{
var loadedMessages = await _options.Storage.LoadQueuedMessagesAsync().ConfigureAwait(false);
foreach (var loadedMessage in loadedMessages)
{
_messageQueue.Add(loadedMessage);
}
}
_connectionCancellationToken = new CancellationTokenSource();
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
Task.Factory.StartNew(() => MaintainConnectionAsync(_connectionCancellationToken.Token), _connectionCancellationToken.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
_logger.LogInformation("Started");
}
public Task StopAsync()
{
_connectionCancellationToken?.Cancel(false);
_connectionCancellationToken = null;
while (_messageQueue.Any())
{
_messageQueue.Take();
}
return Task.FromResult(0);
}
public async Task PublishAsync(IEnumerable<MqttApplicationMessage> applicationMessages)
{
if (applicationMessages == null) throw new ArgumentNullException(nameof(applicationMessages));
foreach (var applicationMessage in applicationMessages)
{
await _storageManager.AddAsync(applicationMessage).ConfigureAwait(false);
_messageQueue.Add(applicationMessage);
}
}
public Task SubscribeAsync(IEnumerable<TopicFilter> topicFilters)
{
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
lock (_subscriptions)
{
foreach (var topicFilter in topicFilters)
{
if (_subscriptions.Add(topicFilter))
{
_subscriptionsNotPushed = true;
}
}
}
return Task.FromResult(0);
}
public Task UnsubscribeAsync(IEnumerable<TopicFilter> topicFilters)
{
lock (_subscriptions)
{
foreach (var topicFilter in topicFilters)
{
if (_subscriptions.Remove(topicFilter))
{
_subscriptionsNotPushed = true;
}
}
}
return Task.FromResult(0);
}
private async Task MaintainConnectionAsync(CancellationToken cancellationToken)
{
try
{
while (!cancellationToken.IsCancellationRequested)
{
var connectionState = await ReconnectIfRequiredAsync().ConfigureAwait(false);
if (connectionState == ReconnectionResult.NotConnected)
{
_publishingCancellationToken?.Cancel(false);
_publishingCancellationToken = null;
await Task.Delay(_options.AutoReconnectDelay, cancellationToken).ConfigureAwait(false);
continue;
}
if (connectionState == ReconnectionResult.Reconnected || _subscriptionsNotPushed)
{
await PushSubscriptionsAsync();
_publishingCancellationToken = new CancellationTokenSource();
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
Task.Factory.StartNew(() => PublishQueuedMessagesAsync(_publishingCancellationToken.Token), _publishingCancellationToken.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
continue;
}
if (connectionState == ReconnectionResult.StillConnected)
{
await Task.Delay(100, _connectionCancellationToken.Token).ConfigureAwait(false); // Consider using the _Disconnected_ event here. (TaskCompletionSource)
}
}
}
catch (OperationCanceledException)
{
}
catch (MqttCommunicationException exception)
{
_logger.LogWarning(new EventId(), exception, "Communication exception while maintaining connection.");
}
catch (Exception exception)
{
_logger.LogError(new EventId(), exception, "Unhandled exception while maintaining connection.");
}
finally
{
await _mqttClient.DisconnectAsync().ConfigureAwait(false);
_logger.LogInformation("Stopped");
}
}
private async Task PublishQueuedMessagesAsync(CancellationToken cancellationToken)
{
try
{
while (!cancellationToken.IsCancellationRequested)
{
var message = _messageQueue.Take(cancellationToken);
if (message == null)
{
continue;
}
if (cancellationToken.IsCancellationRequested)
{
continue;
}
await TryPublishQueuedMessageAsync(message).ConfigureAwait(false);
await _storageManager.RemoveAsync(message).ConfigureAwait(false);
}
}
catch (OperationCanceledException)
{
}
finally
{
_logger.LogInformation("Stopped publishing messages");
}
}
private async Task TryPublishQueuedMessageAsync(MqttApplicationMessage message)
{
try
{
await _mqttClient.PublishAsync(message).ConfigureAwait(false);
}
catch (MqttCommunicationException exception)
{
_logger.LogWarning(new EventId(), exception, "Publishing application message failed.");
if (message.QualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce)
{
_messageQueue.Add(message);
}
}
catch (Exception exception)
{
_logger.LogError(new EventId(), exception, "Unhandled exception while publishing queued application message.");
}
}
private async Task PushSubscriptionsAsync()
{
_logger.LogInformation(nameof(ManagedMqttClient), "Synchronizing subscriptions");
List<TopicFilter> subscriptions;
lock (_subscriptions)
{
subscriptions = _subscriptions.ToList();
_subscriptionsNotPushed = false;
}
if (!_subscriptions.Any())
{
return;
}
try
{
await _mqttClient.SubscribeAsync(subscriptions).ConfigureAwait(false);
}
catch (Exception exception)
{
_logger.LogWarning(new EventId(), exception, "Synchronizing subscriptions failed");
_subscriptionsNotPushed = true;
}
}
private async Task<ReconnectionResult> ReconnectIfRequiredAsync()
{
if (_mqttClient.IsConnected)
{
return ReconnectionResult.StillConnected;
}
try
{
await _mqttClient.ConnectAsync(_options.ClientOptions).ConfigureAwait(false);
return ReconnectionResult.Reconnected;
}
catch (Exception)
{
return ReconnectionResult.NotConnected;
}
}
private void OnApplicationMessageReceived(object sender, MqttApplicationMessageReceivedEventArgs eventArgs)
{
ApplicationMessageReceived?.Invoke(this, eventArgs);
}
private void OnDisconnected(object sender, MqttClientDisconnectedEventArgs eventArgs)
{
Disconnected?.Invoke(this, eventArgs);
}
private void OnConnected(object sender, MqttClientConnectedEventArgs eventArgs)
{
Connected?.Invoke(this, eventArgs);
}
}
}
@@ -1,22 +0,0 @@
using System;
using System.Threading.Tasks;
namespace MQTTnet.Core.ManagedClient
{
public static class ManagedMqttClientExtensions
{
public static Task SubscribeAsync(this IManagedMqttClient managedClient, params TopicFilter[] topicFilters)
{
if (managedClient == null) throw new ArgumentNullException(nameof(managedClient));
return managedClient.SubscribeAsync(topicFilters);
}
public static Task UnsubscribeAsync(this IManagedMqttClient managedClient, params TopicFilter[] topicFilters)
{
if (managedClient == null) throw new ArgumentNullException(nameof(managedClient));
return managedClient.UnsubscribeAsync(topicFilters);
}
}
}
@@ -1,75 +0,0 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace MQTTnet.Core.ManagedClient
{
public class ManagedMqttClientStorageManager
{
private readonly List<MqttApplicationMessage> _applicationMessages = new List<MqttApplicationMessage>();
private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
private IManagedMqttClientStorage _storage;
public async Task SetStorageAsync(IManagedMqttClientStorage storage)
{
await _semaphore.WaitAsync().ConfigureAwait(false);
try
{
_storage = storage;
}
finally
{
_semaphore.Release();
}
}
public async Task AddAsync(MqttApplicationMessage applicationMessage)
{
await _semaphore.WaitAsync().ConfigureAwait(false);
try
{
if (_storage == null)
{
return;
}
_applicationMessages.Add(applicationMessage);
await SaveAsync().ConfigureAwait(false);
}
finally
{
_semaphore.Release();
}
}
public async Task RemoveAsync(MqttApplicationMessage applicationMessage)
{
await _semaphore.WaitAsync().ConfigureAwait(false);
try
{
if (_storage == null)
{
return;
}
var index = _applicationMessages.IndexOf(applicationMessage);
if (index == -1)
{
return;
}
_applicationMessages.RemoveAt(index);
await SaveAsync().ConfigureAwait(false);
}
finally
{
_semaphore.Release();
}
}
private Task SaveAsync()
{
return _storage.SaveQueuedMessagesAsync(_applicationMessages);
}
}
}
-29
View File
@@ -1,29 +0,0 @@
using System;
using MQTTnet.Core.Protocol;
namespace MQTTnet.Core
{
public sealed class MqttApplicationMessage
{
public MqttApplicationMessage()
{
}
[Obsolete("Use object initializer or _MqttApplicationMessageBuilder_ instead.")]
public MqttApplicationMessage(string topic, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel, bool retain)
{
Topic = topic ?? throw new ArgumentNullException(nameof(topic));
Payload = payload ?? throw new ArgumentNullException(nameof(payload));
QualityOfServiceLevel = qualityOfServiceLevel;
Retain = retain;
}
public string Topic { get; set; }
public byte[] Payload { get; set; }
public MqttQualityOfServiceLevel QualityOfServiceLevel { get; set; }
public bool Retain { get; set; }
}
}
@@ -1,7 +0,0 @@
namespace MQTTnet.Core.Packets
{
public interface IMqttPacketWithIdentifier
{
ushort PacketIdentifier { get; set; }
}
}
-16
View File
@@ -1,16 +0,0 @@
using MQTTnet.Core.Protocol;
namespace MQTTnet.Core.Packets
{
public sealed class MqttConnAckPacket : MqttBasePacket
{
public bool IsSessionPresent { get; set; }
public MqttConnectReturnCode ConnectReturnCode { get; set; }
public override string ToString()
{
return nameof(MqttConnAckPacket) + ": [ConnectReturnCode=" + ConnectReturnCode + "] [IsSessionPresent=" + IsSessionPresent + "]";
}
}
}
@@ -1,6 +0,0 @@
namespace MQTTnet.Core.Packets
{
public sealed class MqttDisconnectPacket : MqttBasePacket
{
}
}
@@ -1,26 +0,0 @@
using System;
namespace MQTTnet.Core.Packets
{
public static class MqttPacketExtensions
{
public static TResponsePacket CreateResponse<TResponsePacket>(this MqttBasePacket packet)
{
if (packet == null) throw new ArgumentNullException(nameof(packet));
var responsePacket = Activator.CreateInstance<TResponsePacket>();
if (responsePacket is IMqttPacketWithIdentifier responsePacketWithIdentifier)
{
if (!(packet is IMqttPacketWithIdentifier requestPacketWithIdentifier))
{
throw new InvalidOperationException("Response packet has PacketIdentifier but request packet does not.");
}
responsePacketWithIdentifier.PacketIdentifier = requestPacketWithIdentifier.PacketIdentifier;
}
return responsePacket;
}
}
}
-13
View File
@@ -1,13 +0,0 @@
using MQTTnet.Core.Protocol;
namespace MQTTnet.Core.Packets
{
public class MqttPacketHeader
{
public MqttControlPacketType ControlPacketType { get; set; }
public byte FixedHeader { get; set; }
public int BodyLength { get; set; }
}
}
-10
View File
@@ -1,10 +0,0 @@
namespace MQTTnet.Core.Packets
{
public sealed class MqttPingReqPacket : MqttBasePacket
{
public override string ToString()
{
return nameof(MqttPingReqPacket);
}
}
}
@@ -1,10 +0,0 @@
namespace MQTTnet.Core.Packets
{
public sealed class MqttPingRespPacket : MqttBasePacket
{
public override string ToString()
{
return nameof(MqttPingRespPacket);
}
}
}
-6
View File
@@ -1,6 +0,0 @@
namespace MQTTnet.Core.Packets
{
public sealed class MqttPubAckPacket : MqttBasePublishPacket
{
}
}
@@ -1,6 +0,0 @@
namespace MQTTnet.Core.Packets
{
public sealed class MqttPubCompPacket : MqttBasePublishPacket
{
}
}
-6
View File
@@ -1,6 +0,0 @@
namespace MQTTnet.Core.Packets
{
public sealed class MqttPubRecPacket : MqttBasePublishPacket
{
}
}
-6
View File
@@ -1,6 +0,0 @@
namespace MQTTnet.Core.Packets
{
public sealed class MqttPubRelPacket : MqttBasePublishPacket
{
}
}
@@ -1,7 +0,0 @@
namespace MQTTnet.Core.Packets
{
public sealed class MqttUnsubAckPacket : MqttBasePacket, IMqttPacketWithIdentifier
{
public ushort PacketIdentifier { get; set; }
}
}
-11
View File
@@ -1,11 +0,0 @@
using System.Collections.Generic;
namespace MQTTnet.Core.Packets
{
public sealed class MqttUnsubscribePacket : MqttBasePacket, IMqttPacketWithIdentifier
{
public ushort PacketIdentifier { get; set; }
public IList<string> TopicFilters { get; set; } = new List<string>();
}
}
-48
View File
@@ -1,48 +0,0 @@
using System;
namespace MQTTnet.Core.Serializer
{
public sealed class ByteReader
{
private readonly int _source;
private int _index;
public ByteReader(int source)
{
_source = source;
}
public bool Read()
{
if (_index >= 8)
{
throw new InvalidOperationException("End of byte reached.");
}
var result = ((1 << _index) & _source) > 0;
_index++;
return result;
}
public int Read(int count)
{
if (_index + count > 8)
{
throw new InvalidOperationException("End of byte will be reached.");
}
var result = 0;
for (var i = 0; i < count; i++)
{
if (((1 << _index) & _source) > 0)
{
result |= 1 << i;
}
_index++;
}
return result;
}
}
}
-36
View File
@@ -1,36 +0,0 @@
using System;
namespace MQTTnet.Core.Serializer
{
public sealed class ByteWriter
{
private int _index;
private int _byte;
public byte Value => (byte)_byte;
public void Write(int @byte, int count)
{
for (var i = 0; i < count; i++)
{
var value = ((1 << i) & @byte) > 0;
Write(value);
}
}
public void Write(bool bit)
{
if (_index >= 8)
{
throw new InvalidOperationException("End of the byte reached.");
}
if (bit)
{
_byte |= 1 << _index;
}
_index++;
}
}
}
-109
View File
@@ -1,109 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Core.Adapter;
using MQTTnet.Core.Exceptions;
using MQTTnet.Core.Protocol;
using MQTTnet.Core.Packets;
namespace MQTTnet.Core.Serializer
{
public sealed class MqttPacketReader : BinaryReader
{
private readonly ReceivedMqttPacket _receivedMqttPacket;
public MqttPacketReader(ReceivedMqttPacket receivedMqttPacket)
: base(receivedMqttPacket.Body, Encoding.UTF8, true)
{
_receivedMqttPacket = receivedMqttPacket;
}
public bool EndOfRemainingData => BaseStream.Position == _receivedMqttPacket.Header.BodyLength;
public static MqttPacketHeader ReadHeaderFromSource(Stream stream, CancellationToken cancellationToken)
{
var buffer = stream.ReadByte();
if (buffer == -1)
{
return null;
}
var fixedHeader = (byte)buffer;
var controlPacketType = (MqttControlPacketType)(fixedHeader >> 4);
var bodyLength = ReadBodyLengthFromSource(stream, cancellationToken);
return new MqttPacketHeader
{
FixedHeader = fixedHeader,
ControlPacketType = controlPacketType,
BodyLength = bodyLength
};
}
public override ushort ReadUInt16()
{
var buffer = ReadBytes(2);
var temp = buffer[0];
buffer[0] = buffer[1];
buffer[1] = temp;
return BitConverter.ToUInt16(buffer, 0);
}
public string ReadStringWithLengthPrefix()
{
var buffer = ReadWithLengthPrefix();
return Encoding.UTF8.GetString(buffer, 0, buffer.Length);
}
public byte[] ReadWithLengthPrefix()
{
var length = ReadUInt16();
return ReadBytes(length);
}
public byte[] ReadRemainingData()
{
return ReadBytes(_receivedMqttPacket.Header.BodyLength - (int)BaseStream.Position);
}
private static int ReadBodyLengthFromSource(Stream stream, CancellationToken cancellationToken)
{
// Alorithm taken from http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html.
var multiplier = 1;
var value = 0;
byte encodedByte;
var readBytes = new List<int>();
do
{
if (cancellationToken.IsCancellationRequested)
{
throw new TaskCanceledException();
}
var buffer = stream.ReadByte();
readBytes.Add(buffer);
if (buffer == -1)
{
break;
}
encodedByte = (byte)buffer;
value += (byte)(encodedByte & 127) * multiplier;
multiplier *= 128;
if (multiplier > 128 * 128 * 128)
{
throw new MqttProtocolViolationException($"Remaining length is invalid (Data={string.Join(",", readBytes)}).");
}
} while ((encodedByte & 128) != 0);
return value;
}
}
}
@@ -1,514 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using MQTTnet.Core.Adapter;
using MQTTnet.Core.Exceptions;
using MQTTnet.Core.Packets;
using MQTTnet.Core.Protocol;
namespace MQTTnet.Core.Serializer
{
public sealed class MqttPacketSerializer : IMqttPacketSerializer
{
private static byte[] ProtocolVersionV311Name { get; } = Encoding.UTF8.GetBytes("MQTT");
private static byte[] ProtocolVersionV310Name { get; } = Encoding.UTF8.GetBytes("MQIs");
public MqttProtocolVersion ProtocolVersion { get; set; } = MqttProtocolVersion.V311;
public byte[] Serialize(MqttBasePacket packet)
{
if (packet == null) throw new ArgumentNullException(nameof(packet));
using (var stream = new MemoryStream())
using (var writer = new MqttPacketWriter(stream))
{
var fixedHeader = SerializePacket(packet, writer);
var headerBuffer = new List<byte> { fixedHeader };
MqttPacketWriter.WriteRemainingLength((int)stream.Length, headerBuffer);
var header = headerBuffer.ToArray();
var body = stream.ToArray();
var buffer = new byte[header.Length + body.Length];
Buffer.BlockCopy(header, 0, buffer, 0, header.Length);
Buffer.BlockCopy(body, 0, buffer, header.Length, body.Length);
return buffer;
}
}
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 byte SerializePacket(MqttBasePacket packet, MqttPacketWriter writer)
{
switch (packet)
{
case MqttConnectPacket connectPacket: return Serialize(connectPacket, writer);
case MqttConnAckPacket connAckPacket: return Serialize(connAckPacket, writer);
case MqttDisconnectPacket _: return SerializeEmptyPacket(MqttControlPacketType.Disconnect);
case MqttPingReqPacket _: return SerializeEmptyPacket(MqttControlPacketType.PingReq);
case MqttPingRespPacket _: return SerializeEmptyPacket(MqttControlPacketType.PingResp);
case MqttPublishPacket publishPacket: return Serialize(publishPacket, writer);
case MqttPubAckPacket pubAckPacket: return Serialize(pubAckPacket, writer);
case MqttPubRecPacket pubRecPacket: return Serialize(pubRecPacket, writer);
case MqttPubRelPacket pubRelPacket: return Serialize(pubRelPacket, writer);
case MqttPubCompPacket pubCompPacket: return Serialize(pubCompPacket, writer);
case MqttSubscribePacket subscribePacket: return Serialize(subscribePacket, writer);
case MqttSubAckPacket subAckPacket: return Serialize(subAckPacket, writer);
case MqttUnsubscribePacket unsubscribePacket: return Serialize(unsubscribePacket, writer);
case MqttUnsubAckPacket unsubAckPacket: return Serialize(unsubAckPacket, writer);
default: throw new MqttProtocolViolationException("Packet type invalid.");
}
}
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 DeserializePubAck(reader);
case MqttControlPacketType.PubRec: return DeserializePubRec(reader);
case MqttControlPacketType.PubRel: return DeserializePubRel(reader);
case MqttControlPacketType.PubComp: return DeserializePubComp(reader);
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 DeserializeUnsubAck(reader);
default: throw new MqttProtocolViolationException($"Packet type ({(int)header.ControlPacketType}) not supported.");
}
}
private static MqttBasePacket DeserializeUnsubAck(MqttPacketReader reader)
{
return new MqttUnsubAckPacket
{
PacketIdentifier = reader.ReadUInt16()
};
}
private static MqttBasePacket DeserializePubComp(MqttPacketReader reader)
{
return new MqttPubCompPacket
{
PacketIdentifier = reader.ReadUInt16()
};
}
private static MqttBasePacket DeserializePubRel(MqttPacketReader reader)
{
return new MqttPubRelPacket
{
PacketIdentifier = reader.ReadUInt16()
};
}
private static MqttBasePacket DeserializePubRec(MqttPacketReader reader)
{
return new MqttPubRecPacket
{
PacketIdentifier = reader.ReadUInt16()
};
}
private static MqttBasePacket DeserializePubAck(MqttPacketReader reader)
{
return new MqttPubAckPacket
{
PacketIdentifier = reader.ReadUInt16()
};
}
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.");
}
reader.ReadByte(); // Skip protocol level
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
{
Topic = reader.ReadStringWithLengthPrefix(),
Payload = reader.ReadWithLengthPrefix(),
QualityOfServiceLevel = (MqttQualityOfServiceLevel)willQoS,
Retain = 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 (packet == null) throw new ArgumentNullException(nameof(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 == null) throw new ArgumentNullException(nameof(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(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(IMqttPacketWithIdentifier packet, BinaryWriter writer)
{
writer.Write(packet.PacketIdentifier);
return MqttPacketWriter.BuildFixedHeader(MqttControlPacketType.UnsubAck);
}
private static byte SerializeEmptyPacket(MqttControlPacketType type)
{
return MqttPacketWriter.BuildFixedHeader(type);
}
}
}
@@ -1,82 +0,0 @@
using System;
using System.IO;
using System.Text;
using MQTTnet.Core.Protocol;
using System.Collections.Generic;
namespace MQTTnet.Core.Serializer
{
public sealed class MqttPacketWriter : BinaryWriter
{
public MqttPacketWriter(Stream stream)
: base(stream, Encoding.UTF8, true)
{
}
public static byte BuildFixedHeader(MqttControlPacketType packetType, byte flags = 0)
{
var fixedHeader = (int)packetType << 4;
fixedHeader |= flags;
return (byte)fixedHeader;
}
public override void Write(ushort value)
{
var buffer = BitConverter.GetBytes(value);
Write(buffer[1], buffer[0]);
}
public new void Write(params byte[] values)
{
base.Write(values);
}
public new void Write(byte value)
{
base.Write(value);
}
public void Write(ByteWriter value)
{
if (value == null) throw new ArgumentNullException(nameof(value));
Write(value.Value);
}
public void WriteWithLengthPrefix(string value)
{
WriteWithLengthPrefix(Encoding.UTF8.GetBytes(value ?? string.Empty));
}
public void WriteWithLengthPrefix(byte[] value)
{
var length = (ushort)value.Length;
Write(length);
Write(value);
}
public static void WriteRemainingLength(int length, List<byte> target)
{
if (length == 0)
{
target.Add(0);
return;
}
// Alorithm taken from http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html.
var x = length;
do
{
var encodedByte = x % 128;
x = x / 128;
if (x > 0)
{
encodedByte = encodedByte | 128;
}
target.Add((byte)encodedByte);
} while (x > 0);
}
}
}
@@ -1,11 +0,0 @@
using MQTTnet.Core.Serializer;
namespace MQTTnet.Core.Server
{
public class ConnectedMqttClient
{
public string ClientId { get; set; }
public MqttProtocolVersion ProtocolVersion { get; set; }
}
}
@@ -1,15 +0,0 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using MQTTnet.Core.Packets;
namespace MQTTnet.Core.Server
{
public interface IMqttClientRetainedMessageManager
{
Task LoadMessagesAsync();
Task HandleMessageAsync(string clientId, MqttApplicationMessage applicationMessage);
Task<List<MqttApplicationMessage>> GetSubscribedMessagesAsync(MqttSubscribePacket subscribePacket);
}
}
@@ -1,7 +0,0 @@
namespace MQTTnet.Core.Server
{
public interface IMqttClientSesssionFactory
{
MqttClientSession CreateClientSession(string sessionId, MqttClientSessionsManager mqttClientSessionsManager);
}
}
-19
View File
@@ -1,19 +0,0 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace MQTTnet.Core.Server
{
public interface IMqttServer : IApplicationMessageReceiver, IApplicationMessagePublisher
{
event EventHandler<MqttClientConnectedEventArgs> ClientConnected;
event EventHandler<MqttClientDisconnectedEventArgs> ClientDisconnected;
event EventHandler<MqttServerStartedEventArgs> Started;
IList<ConnectedMqttClient> GetConnectedClients();
void Publish(IEnumerable<MqttApplicationMessage> applicationMessages);
Task StartAsync();
Task StopAsync();
}
}
-11
View File
@@ -1,11 +0,0 @@
using System;
namespace MQTTnet.Core.Server
{
public interface IMqttServerFactory
{
IMqttServer CreateMqttServer();
IMqttServer CreateMqttServer(Action<MqttServerOptions> configure);
}
}
@@ -1,7 +0,0 @@
namespace MQTTnet.Core.Server
{
public class MqttApplicationMessageInterceptorContext
{
public MqttApplicationMessage ApplicationMessage { get; set; }
}
}
@@ -1,14 +0,0 @@
using System;
namespace MQTTnet.Core.Server
{
public class MqttClientConnectedEventArgs : EventArgs
{
public MqttClientConnectedEventArgs(ConnectedMqttClient client)
{
Client = client ?? throw new ArgumentNullException(nameof(client));
}
public ConnectedMqttClient Client { get; }
}
}
@@ -1,14 +0,0 @@
using System;
namespace MQTTnet.Core.Server
{
public class MqttClientDisconnectedEventArgs : EventArgs
{
public MqttClientDisconnectedEventArgs(ConnectedMqttClient client)
{
Client = client ?? throw new ArgumentNullException(nameof(client));
}
public ConnectedMqttClient Client { get; }
}
}
@@ -1,103 +0,0 @@
using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Core.Adapter;
using MQTTnet.Core.Exceptions;
using MQTTnet.Core.Packets;
using MQTTnet.Core.Protocol;
using Microsoft.Extensions.Logging;
namespace MQTTnet.Core.Server
{
public sealed class MqttClientPendingMessagesQueue
{
private readonly BlockingCollection<MqttPublishPacket> _pendingPublishPackets = new BlockingCollection<MqttPublishPacket>();
private readonly MqttServerOptions _options;
private readonly MqttClientSession _session;
private readonly ILogger<MqttClientPendingMessagesQueue> _logger;
public MqttClientPendingMessagesQueue(MqttServerOptions options, MqttClientSession session, ILogger<MqttClientPendingMessagesQueue> logger)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_session = session ?? throw new ArgumentNullException(nameof(session));
_options = options ?? throw new ArgumentNullException(nameof(options));
}
public void Start(IMqttCommunicationAdapter adapter, CancellationToken cancellationToken)
{
if (adapter == null) throw new ArgumentNullException(nameof(adapter));
if (cancellationToken.IsCancellationRequested)
{
return;
}
Task.Run(async () => await SendPendingPublishPacketsAsync(adapter, cancellationToken), cancellationToken).ConfigureAwait(false);
}
public void Enqueue(MqttPublishPacket publishPacket)
{
if (publishPacket == null) throw new ArgumentNullException(nameof(publishPacket));
_pendingPublishPackets.Add(publishPacket);
_logger.LogTrace("Enqueued packet (ClientId: {0}).", _session.ClientId);
}
private async Task SendPendingPublishPacketsAsync(IMqttCommunicationAdapter adapter, CancellationToken cancellationToken)
{
try
{
while (!cancellationToken.IsCancellationRequested)
{
await SendPendingPublishPacketAsync(adapter, cancellationToken);
}
}
catch (OperationCanceledException)
{
}
catch (Exception exception)
{
_logger.LogError(new EventId(), exception, "Unhandled exception while sending enqueued packet (ClientId: {0}).", _session.ClientId);
}
}
private async Task SendPendingPublishPacketAsync(IMqttCommunicationAdapter adapter, CancellationToken cancellationToken)
{
MqttPublishPacket packet = null;
try
{
packet = _pendingPublishPackets.Take(cancellationToken);
await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, packet).ConfigureAwait(false);
_logger.LogTrace("Enqueued packet sent (ClientId: {0}).", _session.ClientId);
}
catch (Exception exception)
{
if (exception is MqttCommunicationTimedOutException)
{
_logger.LogWarning(new EventId(), exception, "Sending publish packet failed due to timeout (ClientId: {0}).", _session.ClientId);
}
else if (exception is MqttCommunicationException)
{
_logger.LogWarning(new EventId(), exception, "Sending publish packet failed due to communication exception (ClientId: {0}).", _session.ClientId);
}
else if (exception is OperationCanceledException)
{
}
else
{
_logger.LogError(new EventId(), exception, "Sending publish packet failed (ClientId: {0}).", _session.ClientId);
}
if (packet != null && packet.QualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce)
{
packet.Dup = true;
_pendingPublishPackets.Add(packet, CancellationToken.None);
}
_session.Stop();
}
}
}
}
@@ -1,141 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Core.Packets;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace MQTTnet.Core.Server
{
public sealed class MqttClientRetainedMessagesManager : IMqttClientRetainedMessageManager
{
private readonly Dictionary<string, MqttApplicationMessage> _retainedMessages = new Dictionary<string, MqttApplicationMessage>();
private readonly SemaphoreSlim _gate = new SemaphoreSlim(1, 1);
private readonly ILogger<MqttClientRetainedMessagesManager> _logger;
private readonly MqttServerOptions _options;
public MqttClientRetainedMessagesManager(IOptions<MqttServerOptions> options, ILogger<MqttClientRetainedMessagesManager> logger)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_options = options.Value ?? throw new ArgumentNullException(nameof(options));
}
public async Task LoadMessagesAsync()
{
if (_options.Storage == null)
{
return;
}
await _gate.WaitAsync();
try
{
var retainedMessages = await _options.Storage.LoadRetainedMessagesAsync();
_retainedMessages.Clear();
foreach (var retainedMessage in retainedMessages)
{
_retainedMessages[retainedMessage.Topic] = retainedMessage;
}
}
catch (Exception exception)
{
_logger.LogError(new EventId(), exception, "Unhandled exception while loading retained messages.");
}
finally
{
_gate.Release();
}
}
public async Task HandleMessageAsync(string clientId, MqttApplicationMessage applicationMessage)
{
if (applicationMessage == null) throw new ArgumentNullException(nameof(applicationMessage));
await _gate.WaitAsync().ConfigureAwait(false);
try
{
var saveIsRequired = false;
if (applicationMessage.Payload?.Any() == false)
{
saveIsRequired = _retainedMessages.Remove(applicationMessage.Topic);
_logger.LogInformation("Client '{0}' cleared retained message for topic '{1}'.", clientId, applicationMessage.Topic);
}
else
{
if (!_retainedMessages.ContainsKey(applicationMessage.Topic))
{
_retainedMessages[applicationMessage.Topic] = applicationMessage;
saveIsRequired = true;
}
else
{
var existingMessage = _retainedMessages[applicationMessage.Topic];
if (existingMessage.QualityOfServiceLevel != applicationMessage.QualityOfServiceLevel || !existingMessage.Payload.SequenceEqual(applicationMessage.Payload ?? new byte[0]))
{
_retainedMessages[applicationMessage.Topic] = applicationMessage;
saveIsRequired = true;
}
}
_logger.LogInformation("Client '{0}' set retained message for topic '{1}'.", clientId, applicationMessage.Topic);
}
if (!saveIsRequired)
{
_logger.LogTrace("Skipped saving retained messages because no changes were detected.");
}
if (saveIsRequired && _options.Storage != null)
{
await _options.Storage.SaveRetainedMessagesAsync(_retainedMessages.Values.ToList());
}
}
catch (Exception exception)
{
_logger.LogError(new EventId(), exception, "Unhandled exception while handling retained messages.");
}
finally
{
_gate.Release();
}
}
public async Task<List<MqttApplicationMessage>> GetSubscribedMessagesAsync(MqttSubscribePacket subscribePacket)
{
var retainedMessages = new List<MqttApplicationMessage>();
await _gate.WaitAsync().ConfigureAwait(false);
try
{
foreach (var retainedMessage in _retainedMessages.Values)
{
foreach (var topicFilter in subscribePacket.TopicFilters)
{
if (retainedMessage.QualityOfServiceLevel < topicFilter.QualityOfServiceLevel)
{
continue;
}
if (!MqttTopicFilterComparer.IsMatch(retainedMessage.Topic, topicFilter.Topic))
{
continue;
}
retainedMessages.Add(retainedMessage);
break;
}
}
}
finally
{
_gate.Release();
}
return retainedMessages;
}
}
}
-281
View File
@@ -1,281 +0,0 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Core.Adapter;
using MQTTnet.Core.Exceptions;
using MQTTnet.Core.Internal;
using MQTTnet.Core.Packets;
using MQTTnet.Core.Protocol;
using MQTTnet.Core.Serializer;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace MQTTnet.Core.Server
{
public sealed class MqttClientSession
{
private readonly HashSet<ushort> _unacknowledgedPublishPackets = new HashSet<ushort>();
private readonly IMqttClientRetainedMessageManager _clientRetainedMessageManager;
private readonly MqttClientSubscriptionsManager _subscriptionsManager;
private readonly MqttClientSessionsManager _sessionsManager;
private readonly MqttClientPendingMessagesQueue _pendingMessagesQueue;
private readonly MqttServerOptions _options;
private readonly ILogger<MqttClientSession> _logger;
private IMqttCommunicationAdapter _adapter;
private CancellationTokenSource _cancellationTokenSource;
private MqttApplicationMessage _willMessage;
public MqttClientSession(
string clientId,
IOptions<MqttServerOptions> options,
MqttClientSessionsManager sessionsManager,
MqttClientSubscriptionsManager subscriptionsManager,
ILogger<MqttClientSession> logger,
ILogger<MqttClientPendingMessagesQueue> messageQueueLogger,
IMqttClientRetainedMessageManager clientRetainedMessageManager)
{
_clientRetainedMessageManager = clientRetainedMessageManager ?? throw new ArgumentNullException(nameof(clientRetainedMessageManager));
_sessionsManager = sessionsManager ?? throw new ArgumentNullException(nameof(sessionsManager));
_subscriptionsManager = subscriptionsManager ?? throw new ArgumentNullException(nameof(subscriptionsManager));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
ClientId = clientId;
_options = options.Value;
_pendingMessagesQueue = new MqttClientPendingMessagesQueue(_options, this, messageQueueLogger);
}
public string ClientId { get; }
public MqttProtocolVersion? ProtocolVersion => _adapter?.PacketSerializer.ProtocolVersion;
public bool IsConnected => _adapter != null;
public async Task RunAsync(MqttApplicationMessage willMessage, IMqttCommunicationAdapter adapter)
{
if (adapter == null) throw new ArgumentNullException(nameof(adapter));
try
{
var cancellationTokenSource = new CancellationTokenSource();
_willMessage = willMessage;
_adapter = adapter;
_cancellationTokenSource = cancellationTokenSource;
_pendingMessagesQueue.Start(adapter, cancellationTokenSource.Token);
await ReceivePacketsAsync(adapter, cancellationTokenSource.Token).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
}
catch (MqttCommunicationException exception)
{
_logger.LogWarning(new EventId(), exception, "Client '{0}': Communication exception while processing client packets.", ClientId);
}
catch (Exception exception)
{
_logger.LogError(new EventId(), exception, "Client '{0}': Unhandled exception while processing client packets.", ClientId);
}
}
public void Stop()
{
try
{
_cancellationTokenSource?.Cancel(false);
_cancellationTokenSource?.Dispose();
_cancellationTokenSource = null;
_adapter = null;
_logger.LogInformation("Client '{0}': Disconnected.", ClientId);
}
finally
{
var willMessage = _willMessage;
if (willMessage != null)
{
_willMessage = null; //clear willmessage so it is send just once
_sessionsManager.DispatchApplicationMessage(this, willMessage);
}
}
}
public void EnqueuePublishPacket(MqttPublishPacket publishPacket)
{
if (publishPacket == null) throw new ArgumentNullException(nameof(publishPacket));
if (!_subscriptionsManager.IsSubscribed(publishPacket))
{
return;
}
_pendingMessagesQueue.Enqueue(publishPacket);
}
private async Task ReceivePacketsAsync(IMqttCommunicationAdapter adapter, CancellationToken cancellationToken)
{
try
{
while (!cancellationToken.IsCancellationRequested)
{
var packet = await adapter.ReceivePacketAsync(TimeSpan.Zero, cancellationToken).ConfigureAwait(false);
await ProcessReceivedPacketAsync(adapter, packet, cancellationToken).ConfigureAwait(false);
}
}
catch (OperationCanceledException)
{
}
catch (MqttCommunicationException exception)
{
_logger.LogWarning(new EventId(), exception, "Client '{0}': Communication exception while processing client packets.", ClientId);
Stop();
}
catch (Exception exception)
{
_logger.LogError(new EventId(), exception, "Client '{0}': Unhandled exception while processing client packets.", ClientId);
Stop();
}
}
private Task ProcessReceivedPacketAsync(IMqttCommunicationAdapter adapter, MqttBasePacket packet, CancellationToken cancellationToken)
{
if (packet is MqttPingReqPacket)
{
return adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new MqttPingRespPacket());
}
if (packet is MqttPublishPacket publishPacket)
{
return HandleIncomingPublishPacketAsync(adapter, publishPacket, cancellationToken);
}
if (packet is MqttPubRelPacket pubRelPacket)
{
return HandleIncomingPubRelPacketAsync(adapter, pubRelPacket, cancellationToken);
}
if (packet is MqttPubRecPacket pubRecPacket)
{
return adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, pubRecPacket.CreateResponse<MqttPubRelPacket>());
}
if (packet is MqttPubAckPacket || packet is MqttPubCompPacket)
{
// Discard message.
return Task.FromResult(0);
}
if (packet is MqttSubscribePacket subscribePacket)
{
return HandleIncomingSubscribePacketAsync(adapter, subscribePacket, cancellationToken);
}
if (packet is MqttUnsubscribePacket unsubscribePacket)
{
return adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, _subscriptionsManager.Unsubscribe(unsubscribePacket));
}
if (packet is MqttDisconnectPacket || packet is MqttConnectPacket)
{
Stop();
return Task.FromResult(0);
}
_logger.LogWarning("Client '{0}': Received not supported packet ({1}). Closing connection.", ClientId, packet);
Stop();
return Task.FromResult(0);
}
private async Task HandleIncomingSubscribePacketAsync(IMqttCommunicationAdapter adapter, MqttSubscribePacket subscribePacket, CancellationToken cancellationToken)
{
var subscribeResult = _subscriptionsManager.Subscribe(subscribePacket, ClientId);
await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, subscribeResult.ResponsePacket).ConfigureAwait(false);
await EnqueueSubscribedRetainedMessagesAsync(subscribePacket).ConfigureAwait(false);
if (subscribeResult.CloseConnection)
{
await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new MqttDisconnectPacket()).ConfigureAwait(false);
Stop();
}
}
private async Task EnqueueSubscribedRetainedMessagesAsync(MqttSubscribePacket subscribePacket)
{
var retainedMessages = await _clientRetainedMessageManager.GetSubscribedMessagesAsync(subscribePacket).ConfigureAwait(false);
foreach (var publishPacket in retainedMessages)
{
EnqueuePublishPacket(publishPacket.ToPublishPacket());
}
}
private async Task HandleIncomingPublishPacketAsync(IMqttCommunicationAdapter adapter, MqttPublishPacket publishPacket, CancellationToken cancellationToken)
{
var applicationMessage = publishPacket.ToApplicationMessage();
var interceptorContext = new MqttApplicationMessageInterceptorContext
{
ApplicationMessage = applicationMessage
};
_options.ApplicationMessageInterceptor?.Invoke(interceptorContext);
applicationMessage = interceptorContext.ApplicationMessage;
if (applicationMessage.Retain)
{
await _clientRetainedMessageManager.HandleMessageAsync(ClientId, applicationMessage).ConfigureAwait(false);
}
switch (applicationMessage.QualityOfServiceLevel)
{
case MqttQualityOfServiceLevel.AtMostOnce:
{
_sessionsManager.DispatchApplicationMessage(this, applicationMessage);
return;
}
case MqttQualityOfServiceLevel.AtLeastOnce:
{
_sessionsManager.DispatchApplicationMessage(this, applicationMessage);
await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken,
new MqttPubAckPacket { PacketIdentifier = publishPacket.PacketIdentifier });
return;
}
case MqttQualityOfServiceLevel.ExactlyOnce:
{
// QoS 2 is implement as method "B" [4.3.3 QoS 2: Exactly once delivery]
lock (_unacknowledgedPublishPackets)
{
_unacknowledgedPublishPackets.Add(publishPacket.PacketIdentifier);
}
_sessionsManager.DispatchApplicationMessage(this, applicationMessage);
await adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken,
new MqttPubRecPacket { PacketIdentifier = publishPacket.PacketIdentifier }).ConfigureAwait(false);
return;
}
default:
throw new MqttCommunicationException("Received a not supported QoS level.");
}
}
private Task HandleIncomingPubRelPacketAsync(IMqttCommunicationAdapter adapter, MqttPubRelPacket pubRelPacket, CancellationToken cancellationToken)
{
lock (_unacknowledgedPublishPackets)
{
_unacknowledgedPublishPackets.Remove(pubRelPacket.PacketIdentifier);
}
return adapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new MqttPubCompPacket { PacketIdentifier = pubRelPacket.PacketIdentifier });
}
}
}
@@ -1,193 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Core.Adapter;
using MQTTnet.Core.Exceptions;
using MQTTnet.Core.Internal;
using MQTTnet.Core.Packets;
using MQTTnet.Core.Protocol;
using MQTTnet.Core.Serializer;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace MQTTnet.Core.Server
{
public sealed class MqttClientSessionsManager
{
private readonly Dictionary<string, MqttClientSession> _clientSessions = new Dictionary<string, MqttClientSession>();
private readonly ILogger<MqttClientSessionsManager> _logger;
private readonly IMqttClientSesssionFactory _clientSesssionFactory;
private readonly MqttServerOptions _options;
public MqttClientSessionsManager(
IOptions<MqttServerOptions> options,
ILogger<MqttClientSessionsManager> logger,
IMqttClientSesssionFactory clientSesssionFactory)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_options = options.Value ?? throw new ArgumentNullException(nameof(options));
_clientSesssionFactory = clientSesssionFactory ?? throw new ArgumentNullException(nameof(clientSesssionFactory));
}
public event EventHandler<MqttClientConnectedEventArgs> ClientConnected;
public event EventHandler<MqttClientDisconnectedEventArgs> ClientDisconnected;
public event EventHandler<MqttApplicationMessageReceivedEventArgs> ApplicationMessageReceived;
public async Task RunClientSessionAsync(IMqttCommunicationAdapter clientAdapter, CancellationToken cancellationToken)
{
var clientId = string.Empty;
try
{
if (!(await clientAdapter.ReceivePacketAsync(_options.DefaultCommunicationTimeout, cancellationToken).ConfigureAwait(false) is MqttConnectPacket connectPacket))
{
throw new MqttProtocolViolationException("The first packet from a client must be a 'CONNECT' packet [MQTT-3.1.0-1].");
}
clientId = connectPacket.ClientId;
// Switch to the required protocol version before sending any response.
clientAdapter.PacketSerializer.ProtocolVersion = connectPacket.ProtocolVersion;
var connectReturnCode = ValidateConnection(connectPacket);
if (connectReturnCode != MqttConnectReturnCode.ConnectionAccepted)
{
await clientAdapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new MqttConnAckPacket
{
ConnectReturnCode = connectReturnCode
}).ConfigureAwait(false);
return;
}
var clientSession = GetOrCreateClientSession(connectPacket);
await clientAdapter.SendPacketsAsync(_options.DefaultCommunicationTimeout, cancellationToken, new MqttConnAckPacket
{
ConnectReturnCode = connectReturnCode,
IsSessionPresent = clientSession.IsExistingSession
}).ConfigureAwait(false);
ClientConnected?.Invoke(this, new MqttClientConnectedEventArgs(new ConnectedMqttClient
{
ClientId = clientId,
ProtocolVersion = clientAdapter.PacketSerializer.ProtocolVersion
}));
using (_logger.BeginScope(clientId))
{
await clientSession.Session.RunAsync(connectPacket.WillMessage, clientAdapter).ConfigureAwait(false);
}
}
catch (Exception exception)
{
_logger.LogError(new EventId(), exception, exception.Message);
}
finally
{
try
{
await clientAdapter.DisconnectAsync(_options.DefaultCommunicationTimeout).ConfigureAwait(false);
}
catch (Exception)
{
//ignored
}
ClientDisconnected?.Invoke(this, new MqttClientDisconnectedEventArgs(new ConnectedMqttClient
{
ClientId = clientId,
ProtocolVersion = clientAdapter.PacketSerializer.ProtocolVersion
}));
}
}
public void Clear()
{
lock (_clientSessions)
{
_clientSessions.Clear();
}
}
public IList<ConnectedMqttClient> GetConnectedClients()
{
lock (_clientSessions)
{
return _clientSessions.Where(s => s.Value.IsConnected).Select(s => new ConnectedMqttClient
{
ClientId = s.Value.ClientId,
ProtocolVersion = s.Value.ProtocolVersion ?? MqttProtocolVersion.V311
}).ToList();
}
}
public void DispatchApplicationMessage(MqttClientSession senderClientSession, MqttApplicationMessage applicationMessage)
{
try
{
var eventArgs = new MqttApplicationMessageReceivedEventArgs(senderClientSession?.ClientId, applicationMessage);
ApplicationMessageReceived?.Invoke(this, eventArgs);
}
catch (Exception exception)
{
_logger.LogError(new EventId(), exception, "Error while processing application message");
}
lock (_clientSessions)
{
foreach (var clientSession in _clientSessions.Values.ToList())
{
clientSession.EnqueuePublishPacket(applicationMessage.ToPublishPacket());
}
}
}
private MqttConnectReturnCode ValidateConnection(MqttConnectPacket connectPacket)
{
if (_options.ConnectionValidator != null)
{
return _options.ConnectionValidator(connectPacket);
}
return MqttConnectReturnCode.ConnectionAccepted;
}
private GetOrCreateClientSessionResult GetOrCreateClientSession(MqttConnectPacket connectPacket)
{
lock (_clientSessions)
{
var isSessionPresent = _clientSessions.TryGetValue(connectPacket.ClientId, out var clientSession);
if (isSessionPresent)
{
if (connectPacket.CleanSession)
{
_clientSessions.Remove(connectPacket.ClientId);
clientSession.Stop();
clientSession = null;
_logger.LogTrace("Stopped existing session of client '{0}'.", connectPacket.ClientId);
}
else
{
_logger.LogTrace("Reusing existing session of client '{0}'.", connectPacket.ClientId);
}
}
var isExistingSession = true;
if (clientSession == null)
{
isExistingSession = false;
clientSession = _clientSesssionFactory.CreateClientSession(connectPacket.ClientId, this);
_clientSessions[connectPacket.ClientId] = clientSession;
_logger.LogTrace("Created a new session for client '{0}'.", connectPacket.ClientId);
}
return new GetOrCreateClientSessionResult { IsExistingSession = isExistingSession, Session = clientSession };
}
}
}
}
@@ -1,93 +0,0 @@
using System;
using System.Collections.Generic;
using Microsoft.Extensions.Options;
using MQTTnet.Core.Packets;
using MQTTnet.Core.Protocol;
namespace MQTTnet.Core.Server
{
public sealed class MqttClientSubscriptionsManager
{
private readonly Dictionary<string, MqttQualityOfServiceLevel> _subscribedTopics = new Dictionary<string, MqttQualityOfServiceLevel>();
private readonly MqttServerOptions _options;
public MqttClientSubscriptionsManager(IOptions<MqttServerOptions> options)
{
_options = options?.Value ?? throw new ArgumentNullException(nameof(options));
}
public MqttClientSubscribeResult Subscribe(MqttSubscribePacket subscribePacket, string clientId)
{
if (subscribePacket == null) throw new ArgumentNullException(nameof(subscribePacket));
var responsePacket = subscribePacket.CreateResponse<MqttSubAckPacket>();
var closeConnection = false;
lock (_subscribedTopics)
{
foreach (var topicFilter in subscribePacket.TopicFilters)
{
var interceptorContext = new MqttSubscriptionInterceptorContext(clientId, topicFilter);
_options.SubscriptionsInterceptor?.Invoke(interceptorContext);
responsePacket.SubscribeReturnCodes.Add(interceptorContext.AcceptSubscription ? MqttSubscribeReturnCode.SuccessMaximumQoS1 : MqttSubscribeReturnCode.Failure);
if (interceptorContext.CloseConnection)
{
closeConnection = true;
}
if (interceptorContext.AcceptSubscription)
{
_subscribedTopics[topicFilter.Topic] = topicFilter.QualityOfServiceLevel;
}
}
}
return new MqttClientSubscribeResult
{
ResponsePacket = responsePacket,
CloseConnection = closeConnection
};
}
public MqttUnsubAckPacket Unsubscribe(MqttUnsubscribePacket unsubscribePacket)
{
if (unsubscribePacket == null) throw new ArgumentNullException(nameof(unsubscribePacket));
lock (_subscribedTopics)
{
foreach (var topicFilter in unsubscribePacket.TopicFilters)
{
_subscribedTopics.Remove(topicFilter);
}
}
return unsubscribePacket.CreateResponse<MqttUnsubAckPacket>();
}
public bool IsSubscribed(MqttPublishPacket publishPacket)
{
if (publishPacket == null) throw new ArgumentNullException(nameof(publishPacket));
lock (_subscribedTopics)
{
foreach (var subscribedTopic in _subscribedTopics)
{
if (publishPacket.QualityOfServiceLevel > subscribedTopic.Value)
{
continue;
}
if (!MqttTopicFilterComparer.IsMatch(publishPacket.Topic, subscribedTopic.Key))
{
continue;
}
return true;
}
}
return false;
}
}
}
-138
View File
@@ -1,138 +0,0 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet.Core.Adapter;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System.Linq;
namespace MQTTnet.Core.Server
{
public class MqttServer : IMqttServer
{
private readonly IMqttClientRetainedMessageManager _clientRetainedMessageManager;
private readonly ILogger<MqttServer> _logger;
private readonly MqttClientSessionsManager _clientSessionsManager;
private readonly ICollection<IMqttServerAdapter> _adapters;
private readonly MqttServerOptions _options;
private CancellationTokenSource _cancellationTokenSource;
public MqttServer(
IOptions<MqttServerOptions> options,
IEnumerable<IMqttServerAdapter> adapters,
ILogger<MqttServer> logger,
MqttClientSessionsManager clientSessionsManager,
IMqttClientRetainedMessageManager clientRetainedMessageManager
)
{
_options = options.Value ?? throw new ArgumentNullException(nameof(options));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_clientSessionsManager = clientSessionsManager ?? throw new ArgumentNullException(nameof(clientSessionsManager));
_clientRetainedMessageManager = clientRetainedMessageManager ?? throw new ArgumentNullException(nameof(clientRetainedMessageManager));
if (adapters == null)
{
throw new ArgumentNullException(nameof(adapters));
}
_adapters = adapters.ToList();
_clientSessionsManager.ApplicationMessageReceived += (s, e) => ApplicationMessageReceived?.Invoke(s, e);
_clientSessionsManager.ClientConnected += OnClientConnected;
_clientSessionsManager.ClientDisconnected += OnClientDisconnected;
}
public IList<ConnectedMqttClient> GetConnectedClients()
{
return _clientSessionsManager.GetConnectedClients();
}
public event EventHandler<MqttServerStartedEventArgs> Started;
public event EventHandler<MqttClientConnectedEventArgs> ClientConnected;
public event EventHandler<MqttClientDisconnectedEventArgs> ClientDisconnected;
public event EventHandler<MqttApplicationMessageReceivedEventArgs> ApplicationMessageReceived;
public void Publish(IEnumerable<MqttApplicationMessage> applicationMessages)
{
if (applicationMessages == null) throw new ArgumentNullException(nameof(applicationMessages));
if (_cancellationTokenSource == null)
{
throw new InvalidOperationException("The server is not started.");
}
foreach (var applicationMessage in applicationMessages)
{
var interceptorContext = new MqttApplicationMessageInterceptorContext
{
ApplicationMessage = applicationMessage
};
_options.ApplicationMessageInterceptor?.Invoke(interceptorContext);
_clientSessionsManager.DispatchApplicationMessage(null, interceptorContext.ApplicationMessage);
}
}
public Task PublishAsync(IEnumerable<MqttApplicationMessage> applicationMessages)
{
Publish(applicationMessages);
return Task.FromResult(0);
}
public async Task StartAsync()
{
if (_cancellationTokenSource != null) throw new InvalidOperationException("The MQTT server is already started.");
_cancellationTokenSource = new CancellationTokenSource();
await _clientRetainedMessageManager.LoadMessagesAsync();
foreach (var adapter in _adapters)
{
adapter.ClientAccepted += OnClientAccepted;
await adapter.StartAsync(_options);
}
_logger.LogInformation("Started.");
Started?.Invoke(this, new MqttServerStartedEventArgs());
}
public async Task StopAsync()
{
_cancellationTokenSource?.Cancel(false);
_cancellationTokenSource?.Dispose();
_cancellationTokenSource = null;
foreach (var adapter in _adapters)
{
adapter.ClientAccepted -= OnClientAccepted;
await adapter.StopAsync();
}
_clientSessionsManager.Clear();
_logger.LogInformation("Stopped.");
}
private void OnClientAccepted(object sender, MqttServerAdapterClientAcceptedEventArgs eventArgs)
{
eventArgs.SessionTask = Task.Run(async () => await _clientSessionsManager.RunClientSessionAsync(eventArgs.Client, _cancellationTokenSource.Token), _cancellationTokenSource.Token);
}
private void OnClientConnected(object sender, MqttClientConnectedEventArgs eventArgs)
{
_logger.LogInformation("Client '{0}': Connected.", eventArgs.Client.ClientId);
ClientConnected?.Invoke(this, eventArgs);
}
private void OnClientDisconnected(object sender, MqttClientDisconnectedEventArgs eventArgs)
{
_logger.LogInformation("Client '{0}': Disconnected.", eventArgs.Client.ClientId);
ClientDisconnected?.Invoke(this, eventArgs);
}
}
}
@@ -1,9 +0,0 @@
namespace MQTTnet.Core.Server
{
public sealed class MqttServerDefaultEndpointOptions
{
public bool IsEnabled { get; set; } = true;
public int? Port { get; set; }
}
}
-25
View File
@@ -1,25 +0,0 @@
using System;
using MQTTnet.Core.Packets;
using MQTTnet.Core.Protocol;
namespace MQTTnet.Core.Server
{
public class MqttServerOptions
{
public MqttServerDefaultEndpointOptions DefaultEndpointOptions { get; } = new MqttServerDefaultEndpointOptions();
public MqttServerTlsEndpointOptions TlsEndpointOptions { get; } = new MqttServerTlsEndpointOptions();
public int ConnectionBacklog { get; set; } = 10;
public TimeSpan DefaultCommunicationTimeout { get; set; } = TimeSpan.FromSeconds(15);
public Func<MqttConnectPacket, MqttConnectReturnCode> ConnectionValidator { get; set; }
public Action<MqttApplicationMessageInterceptorContext> ApplicationMessageInterceptor { get; set; }
public Action<MqttSubscriptionInterceptorContext> SubscriptionsInterceptor { get; set; }
public IMqttServerStorage Storage { get; set; }
}
}
@@ -1,31 +0,0 @@
using System;
namespace MQTTnet.Core.Server
{
public static class MqttServerOptionsExtensions
{
public static int GetTlsEndpointPort(this MqttServerOptions options)
{
if (options == null) throw new ArgumentNullException(nameof(options));
if (!options.TlsEndpointOptions.Port.HasValue)
{
return 8883;
}
return options.TlsEndpointOptions.Port.Value;
}
public static int GetDefaultEndpointPort(this MqttServerOptions options)
{
if (options == null) throw new ArgumentNullException(nameof(options));
if (!options.DefaultEndpointOptions.Port.HasValue)
{
return 1883;
}
return options.DefaultEndpointOptions.Port.Value;
}
}
}
@@ -1,8 +0,0 @@
using System;
namespace MQTTnet.Core.Server
{
public class MqttServerStartedEventArgs : EventArgs
{
}
}
@@ -1,11 +0,0 @@
namespace MQTTnet.Core.Server
{
public sealed class MqttServerTlsEndpointOptions
{
public bool IsEnabled { get; set; }
public int? Port { get; set; }
public byte[] Certificate { get; set; }
}
}
@@ -1,44 +0,0 @@
using System;
namespace MQTTnet.Core.Server
{
public static class MqttTopicFilterComparer
{
private static readonly char[] TopicLevelSeparator = { '/' };
public static bool IsMatch(string topic, string filter)
{
if (topic == null) throw new ArgumentNullException(nameof(topic));
if (filter == null) throw new ArgumentNullException(nameof(filter));
if (string.Equals(topic, filter, StringComparison.Ordinal))
{
return true;
}
var fragmentsTopic = topic.Split(TopicLevelSeparator, StringSplitOptions.None);
var fragmentsFilter = filter.Split(TopicLevelSeparator, StringSplitOptions.None);
for (var i = 0; i < fragmentsFilter.Length; i++)
{
switch (fragmentsFilter[i])
{
case "+": continue;
case "#" when i == fragmentsFilter.Length - 1: return true;
}
if (i >= fragmentsTopic.Length)
{
return false;
}
if (!string.Equals(fragmentsFilter[i], fragmentsTopic[i], StringComparison.Ordinal))
{
return false;
}
}
return fragmentsTopic.Length <= fragmentsFilter.Length;
}
}
}
+88 -23
View File
@@ -1,30 +1,32 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27004.2006
VisualStudioVersion = 15.0.27004.2010
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Core.Tests", "Tests\MQTTnet.Core.Tests\MQTTnet.Core.Tests.csproj", "{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{9248C2E1-B9D6-40BF-81EC-86004D7765B4}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Frameworks", "Frameworks", "{32A630A7-2598-41D7-B625-204CD906F5FB}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Core", "MQTTnet.Core\MQTTnet.Core.csproj", "{2ECB99E4-72D0-4C23-99BA-93D511D3967D}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Source", "Source", "{32A630A7-2598-41D7-B625-204CD906F5FB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.TestApp.UniversalWindows", "Tests\MQTTnet.TestApp.UniversalWindows\MQTTnet.TestApp.UniversalWindows.csproj", "{FF1F72D6-9524-4422-9497-3CC0002216ED}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.NetStandard", "Frameworks\MQTTnet.Netstandard\MQTTnet.NetStandard.csproj", "{3587E506-55A2-4EB3-99C7-DC01E42D25D2}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet", "Source\MQTTnet\MQTTnet.csproj", "{3587E506-55A2-4EB3-99C7-DC01E42D25D2}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{67C28AC1-BC3A-420A-BE9C-FA2401431CF9}"
ProjectSection(SolutionItems) = preProject
appveyor.yml = appveyor.yml
Build\build.ps1 = Build\build.ps1
Build\MQTTnet.AspNetCore.nuspec = Build\MQTTnet.AspNetCore.nuspec
Build\MQTTnet.Extensions.ManagedClient.nuspec = Build\MQTTnet.Extensions.ManagedClient.nuspec
Build\MQTTnet.Extensions.Rpc.nuspec = Build\MQTTnet.Extensions.Rpc.nuspec
Build\MQTTnet.nuspec = Build\MQTTnet.nuspec
Build\upload.ps1 = Build\upload.ps1
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{B3F60ECB-45BA-4C66-8903-8BB89CA67998}"
ProjectSection(SolutionItems) = preProject
.bettercodehub.yml = .bettercodehub.yml
appveyor.yml = appveyor.yml
LICENSE = LICENSE
README.md = README.md
EndProjectSection
@@ -33,7 +35,17 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.TestApp.NetCore", "
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.TestApp.AspNetCore2", "Tests\MQTTnet.TestApp.AspNetCore2\MQTTnet.TestApp.AspNetCore2.csproj", "{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.AspNetCore", "Frameworks\MQTTnet.AspnetCore\MQTTnet.AspNetCore.csproj", "{F10C4060-F7EE-4A83-919F-FF723E72F94A}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.AspNetCore", "Source\MQTTnet.AspnetCore\MQTTnet.AspNetCore.csproj", "{F10C4060-F7EE-4A83-919F-FF723E72F94A}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Extensions", "Extensions", "{12816BCC-AF9E-44A9-9AE5-C246AF2A0587}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Extensions.Rpc", "Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj", "{C444E9C8-95FA-430E-9126-274129DE16CD}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Benchmarks", "Tests\MQTTnet.Benchmarks\MQTTnet.Benchmarks.csproj", "{998D04DD-7CB0-45F5-A393-E2495C16399E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Extensions.ManagedClient", "Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj", "{C400533A-8EBA-4F0B-BF4D-295C3708604B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.AspNetCore.Tests", "Tests\MQTTnet.AspNetCore.Tests\MQTTnet.AspNetCore.Tests.csproj", "{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -63,24 +75,9 @@ Global
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|x64.Build.0 = Release|Any CPU
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|x86.ActiveCfg = Release|Any CPU
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|x86.Build.0 = Release|Any CPU
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Debug|ARM.ActiveCfg = Debug|Any CPU
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Debug|ARM.Build.0 = Debug|Any CPU
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Debug|x64.ActiveCfg = Debug|Any CPU
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Debug|x64.Build.0 = Debug|Any CPU
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Debug|x86.ActiveCfg = Debug|Any CPU
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Debug|x86.Build.0 = Debug|Any CPU
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Release|Any CPU.Build.0 = Release|Any CPU
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Release|ARM.ActiveCfg = Release|Any CPU
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Release|ARM.Build.0 = Release|Any CPU
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Release|x64.ActiveCfg = Release|Any CPU
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Release|x64.Build.0 = Release|Any CPU
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Release|x86.ActiveCfg = Release|Any CPU
{2ECB99E4-72D0-4C23-99BA-93D511D3967D}.Release|x86.Build.0 = Release|Any CPU
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|ARM.ActiveCfg = Debug|ARM
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|ARM.Build.0 = Debug|ARM
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|ARM.Deploy.0 = Debug|ARM
@@ -165,6 +162,70 @@ Global
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|x64.Build.0 = Release|Any CPU
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|x86.ActiveCfg = Release|Any CPU
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|x86.Build.0 = Release|Any CPU
{C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|ARM.ActiveCfg = Debug|Any CPU
{C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|ARM.Build.0 = Debug|Any CPU
{C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|x64.ActiveCfg = Debug|Any CPU
{C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|x64.Build.0 = Debug|Any CPU
{C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|x86.ActiveCfg = Debug|Any CPU
{C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|x86.Build.0 = Debug|Any CPU
{C444E9C8-95FA-430E-9126-274129DE16CD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C444E9C8-95FA-430E-9126-274129DE16CD}.Release|Any CPU.Build.0 = Release|Any CPU
{C444E9C8-95FA-430E-9126-274129DE16CD}.Release|ARM.ActiveCfg = Release|Any CPU
{C444E9C8-95FA-430E-9126-274129DE16CD}.Release|ARM.Build.0 = Release|Any CPU
{C444E9C8-95FA-430E-9126-274129DE16CD}.Release|x64.ActiveCfg = Release|Any CPU
{C444E9C8-95FA-430E-9126-274129DE16CD}.Release|x64.Build.0 = Release|Any CPU
{C444E9C8-95FA-430E-9126-274129DE16CD}.Release|x86.ActiveCfg = Release|Any CPU
{C444E9C8-95FA-430E-9126-274129DE16CD}.Release|x86.Build.0 = Release|Any CPU
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|ARM.ActiveCfg = Debug|Any CPU
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|ARM.Build.0 = Debug|Any CPU
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|x64.ActiveCfg = Debug|Any CPU
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|x64.Build.0 = Debug|Any CPU
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|x86.ActiveCfg = Debug|Any CPU
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|x86.Build.0 = Debug|Any CPU
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|Any CPU.Build.0 = Release|Any CPU
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|ARM.ActiveCfg = Release|Any CPU
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|ARM.Build.0 = Release|Any CPU
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|x64.ActiveCfg = Release|Any CPU
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|x64.Build.0 = Release|Any CPU
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|x86.ActiveCfg = Release|Any CPU
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|x86.Build.0 = Release|Any CPU
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|ARM.ActiveCfg = Debug|Any CPU
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|ARM.Build.0 = Debug|Any CPU
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|x64.ActiveCfg = Debug|Any CPU
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|x64.Build.0 = Debug|Any CPU
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|x86.ActiveCfg = Debug|Any CPU
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|x86.Build.0 = Debug|Any CPU
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|Any CPU.Build.0 = Release|Any CPU
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|ARM.ActiveCfg = Release|Any CPU
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|ARM.Build.0 = Release|Any CPU
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|x64.ActiveCfg = Release|Any CPU
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|x64.Build.0 = Release|Any CPU
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|x86.ActiveCfg = Release|Any CPU
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|x86.Build.0 = Release|Any CPU
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|ARM.ActiveCfg = Debug|Any CPU
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|ARM.Build.0 = Debug|Any CPU
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|x64.ActiveCfg = Debug|Any CPU
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|x64.Build.0 = Debug|Any CPU
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|x86.ActiveCfg = Debug|Any CPU
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|x86.Build.0 = Debug|Any CPU
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|Any CPU.Build.0 = Release|Any CPU
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|ARM.ActiveCfg = Release|Any CPU
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|ARM.Build.0 = Release|Any CPU
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|x64.ActiveCfg = Release|Any CPU
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|x64.Build.0 = Release|Any CPU
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|x86.ActiveCfg = Release|Any CPU
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -176,6 +237,10 @@ Global
{3D283AAD-AAA8-4339-8394-52F80B6304DB} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
{F10C4060-F7EE-4A83-919F-FF723E72F94A} = {32A630A7-2598-41D7-B625-204CD906F5FB}
{C444E9C8-95FA-430E-9126-274129DE16CD} = {12816BCC-AF9E-44A9-9AE5-C246AF2A0587}
{998D04DD-7CB0-45F5-A393-E2495C16399E} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
{C400533A-8EBA-4F0B-BF4D-295C3708604B} = {12816BCC-AF9E-44A9-9AE5-C246AF2A0587}
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {07536672-5CBC-4BE3-ACE0-708A431A7894}
+6
View File
@@ -1,7 +1,13 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_ACCESSORHOLDER_ATTRIBUTE_ON_SAME_LINE_EX/@EntryValue">NEVER</s:String>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_WITHIN_METHOD_CALL_PARENTHESES/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_WITHIN_METHOD_PARENTHESES/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_WITHIN_PARENTHESES/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpAttributeForSingleLineMethodUpgrade/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpRenamePlacementToArrangementMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAddAccessorOwnerDeclarationBracesMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002ECSharpPlaceAttributeOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
+15 -7
View File
@@ -5,6 +5,8 @@
[![NuGet Badge](https://buildstats.info/nuget/MQTTnet)](https://www.nuget.org/packages/MQTTnet)
[![Build status](https://ci.appveyor.com/api/projects/status/ycit86voxfevm2aa/branch/master?svg=true)](https://ci.appveyor.com/project/chkr1011/mqttnet/branch/develop)
[![BCH compliance](https://bettercodehub.com/edge/badge/chkr1011/MQTTnet?branch=master)](https://bettercodehub.com/)
[![OpenCollective](https://opencollective.com/mqttnet/backers/badge.svg)](https://opencollective.com/mqttnet)
[![OpenCollective](https://opencollective.com/mqttnet/sponsors/badge.svg)](https://opencollective.com/mqttnet)
# MQTTnet
@@ -18,12 +20,12 @@ MQTTnet is a high performance .NET library for MQTT based communication. It prov
* TLS 1.2 support for client and server (but not UWP servers)
* Extensible communication channels (i.e. In-Memory, TCP, TCP+TLS, WS)
* Lightweight (only the low level implementation of MQTT, no overhead)
* Performance optimized (processing ~27.000 messages / second)*
* Performance optimized (processing ~70.000 messages / second)*
* Interfaces included for mocking and testing
* Access to internal trace messages
* Unit tested (70+ tests)
* Unit tested (~90 tests)
\* Tested on local machine with MQTTnet client and server running in the same process using the TCP channel. The app for verification is part of this repository and stored in _/Tests/MQTTnet.TestApp.NetCore_.
\* Tested on local machine (Intel i7 8700K) with MQTTnet client and server running in the same process using the TCP channel. The app for verification is part of this repository and stored in _/Tests/MQTTnet.TestApp.NetCore_.
### Client
@@ -43,20 +45,22 @@ MQTTnet is a high performance .NET library for MQTT based communication. It prov
* Retained messages are supported including persisting via interface methods (own implementation required)
* WebSockets supported (via ASP.NET Core 2.0, separate nuget)
* A custom message interceptor can be added which allows transforming or extending every received application message
* Validate subscriptions and deny subscribing of certain topics depending on requesting clients
## Supported frameworks
* .NET Standard 1.3+
* .NET Core 1.1+
* .NET Core App 1.1+
* Universal Windows Platform (UWP) 10.0.10240+ (x86, x64, ARM, AnyCPU)
* .NET Framework 4.5.2+ (x86, x64, AnyCPU)
* Mono 5.2+
* Universal Windows Platform (UWP) 10.0.10240+ (x86, x64, ARM, AnyCPU, Windows 10 IoT Core)
* Xamarin.Android 7.5+
* Xamarin.iOS 10.14+
## Supported MQTT versions
* 5.0.0 (planned)
* 3.1.1
* 3.1.0
@@ -71,19 +75,23 @@ Please find examples and the documentation at the Wiki of this repository (<http
## Contributions
If you want to contribute to this project just create a pull request. But only pull requests which are matching the code style of this library will be accepted. Before creating a pull request please have a look at the library to get an overview of the required style.
Also additions and updates in the Wiki are welcome.
This project also listed at Open Collective (https://opencollective.com/mqttnet).
## References
This library is used in the following projects:
* MQTT Client Rx (Wrapper for Reactive Extensions, <https://github.com/1iveowl/MQTTClient.rx>)
* Wirehome (Open Source Home Automation system for .NET, <https://github.com/chkr1011/Wirehome>)
* MQTT Tester (MQTT client test app for [Android](https://play.google.com/store/apps/details?id=com.liveowl.mqtttester) and [iOS](https://itunes.apple.com/us/app/mqtt-tester/id1278621826?mt=8))
* HA4IoT (Open Source Home Automation system for .NET, <https://github.com/chkr1011/HA4IoT>)
If you use this library and want to see your project here please let me know.
## MIT License
Copyright (c) 2017 Christian Kratky
Copyright (c) 2017-2018 Christian Kratky
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -101,4 +109,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
SOFTWARE.
@@ -0,0 +1,58 @@
using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using System.Linq;
using MQTTnet.Server;
using System.Collections.Generic;
namespace MQTTnet.AspNetCore
{
public static class ApplicationBuilderExtensions
{
public static IApplicationBuilder UseMqttEndpoint(this IApplicationBuilder app, string path = "/mqtt")
{
app.UseWebSockets();
app.Use(async (context, next) =>
{
if (!context.WebSockets.IsWebSocketRequest || context.Request.Path != path)
{
await next();
return;
}
string subProtocol = null;
if (context.Request.Headers.TryGetValue("Sec-WebSocket-Protocol", out var requestedSubProtocolValues))
{
subProtocol = SelectSubProtocol(requestedSubProtocolValues);
}
var adapter = app.ApplicationServices.GetRequiredService<MqttWebSocketServerAdapter>();
using (var webSocket = await context.WebSockets.AcceptWebSocketAsync(subProtocol))
{
var endpoint = $"{context.Connection.RemoteIpAddress}:{context.Connection.RemotePort}";
await adapter.RunWebSocketConnectionAsync(webSocket, endpoint);
}
});
return app;
}
public static string SelectSubProtocol(IList<string> requestedSubProtocolValues)
{
// Order the protocols to also match "mqtt", "mqttv-3.1", "mqttv-3.11" etc.
return requestedSubProtocolValues
.OrderByDescending(p => p.Length)
.FirstOrDefault(p => p.ToLower().StartsWith("mqtt"));
}
public static IApplicationBuilder UseMqttServer(this IApplicationBuilder app, Action<IMqttServer> configure)
{
var server = app.ApplicationServices.GetRequiredService<IMqttServer>();
configure(server);
return app;
}
}
}
@@ -0,0 +1,34 @@
using System;
using System.Net;
using MQTTnet.Adapter;
using MQTTnet.AspNetCore.Client.Tcp;
using MQTTnet.Client;
using MQTTnet.Diagnostics;
using MQTTnet.Serializer;
namespace MQTTnet.AspNetCore.Client
{
public class MqttClientConnectionContextFactory : IMqttClientAdapterFactory
{
public IMqttChannelAdapter CreateClientAdapter(IMqttClientOptions options, IMqttNetChildLogger logger)
{
if (options == null) throw new ArgumentNullException(nameof(options));
var serializer = new MqttPacketSerializer { ProtocolVersion = options.ProtocolVersion };
switch (options.ChannelOptions)
{
case MqttClientTcpOptions tcpOptions:
{
var endpoint = new DnsEndPoint(tcpOptions.Server, tcpOptions.GetPort());
var tcpConnection = new TcpConnection(endpoint);
return new MqttConnectionContext(serializer, tcpConnection);
}
default:
{
throw new NotSupportedException();
}
}
}
}
}
@@ -0,0 +1,22 @@
using System;
using System.Runtime.InteropServices;
namespace MQTTnet.AspNetCore.Client.Tcp
{
public static class BufferExtensions
{
public static ArraySegment<byte> GetArray(this Memory<byte> memory)
{
return ((ReadOnlyMemory<byte>)memory).GetArray();
}
public static ArraySegment<byte> GetArray(this ReadOnlyMemory<byte> memory)
{
if (!MemoryMarshal.TryGetArray(memory, out var result))
{
throw new InvalidOperationException("Buffer backed by array was expected");
}
return result;
}
}
}
@@ -0,0 +1,41 @@
using System.IO.Pipelines;
namespace MQTTnet.AspNetCore.Client.Tcp
{
public class DuplexPipe : IDuplexPipe
{
public DuplexPipe(PipeReader reader, PipeWriter writer)
{
Input = reader;
Output = writer;
}
public PipeReader Input { get; }
public PipeWriter Output { get; }
public static DuplexPipePair CreateConnectionPair(PipeOptions inputOptions, PipeOptions outputOptions)
{
var input = new Pipe(inputOptions);
var output = new Pipe(outputOptions);
var transportToApplication = new DuplexPipe(output.Reader, input.Writer);
var applicationToTransport = new DuplexPipe(input.Reader, output.Writer);
return new DuplexPipePair(applicationToTransport, transportToApplication);
}
// This class exists to work around issues with value tuple on .NET Framework
public readonly struct DuplexPipePair
{
public IDuplexPipe Transport { get; }
public IDuplexPipe Application { get; }
public DuplexPipePair(IDuplexPipe transport, IDuplexPipe application)
{
Transport = transport;
Application = application;
}
}
}
}
@@ -0,0 +1,70 @@
using System;
using System.Diagnostics;
using System.IO.Pipelines;
using System.Net.Sockets;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
namespace MQTTnet.AspNetCore.Client.Tcp
{
public class SocketAwaitable : ICriticalNotifyCompletion
{
private static readonly Action _callbackCompleted = () => { };
private readonly PipeScheduler _ioScheduler;
private Action _callback;
private int _bytesTransferred;
private SocketError _error;
public SocketAwaitable(PipeScheduler ioScheduler)
{
_ioScheduler = ioScheduler;
}
public bool IsCompleted => ReferenceEquals(_callback, _callbackCompleted);
public SocketAwaitable GetAwaiter() => this;
public int GetResult()
{
Debug.Assert(ReferenceEquals(_callback, _callbackCompleted));
_callback = null;
if (_error != SocketError.Success)
{
throw new SocketException((int)_error);
}
return _bytesTransferred;
}
public void OnCompleted(Action continuation)
{
if (ReferenceEquals(_callback, _callbackCompleted) ||
ReferenceEquals(Interlocked.CompareExchange(ref _callback, continuation, null), _callbackCompleted))
{
Task.Run(continuation);
}
}
public void UnsafeOnCompleted(Action continuation)
{
OnCompleted(continuation);
}
public void Complete(int bytesTransferred, SocketError socketError)
{
_error = socketError;
_bytesTransferred = bytesTransferred;
var continuation = Interlocked.Exchange(ref _callback, _callbackCompleted);
if (continuation != null)
{
_ioScheduler.Schedule(state => ((Action)state)(), continuation);
}
}
}
}
@@ -0,0 +1,37 @@
using System;
using System.IO.Pipelines;
using System.Net.Sockets;
namespace MQTTnet.AspNetCore.Client.Tcp
{
public class SocketReceiver
{
private readonly Socket _socket;
private readonly SocketAsyncEventArgs _eventArgs = new SocketAsyncEventArgs();
private readonly SocketAwaitable _awaitable;
public SocketReceiver(Socket socket, PipeScheduler scheduler)
{
_socket = socket;
_awaitable = new SocketAwaitable(scheduler);
_eventArgs.UserToken = _awaitable;
_eventArgs.Completed += (_, e) => ((SocketAwaitable)e.UserToken).Complete(e.BytesTransferred, e.SocketError);
}
public SocketAwaitable ReceiveAsync(Memory<byte> buffer)
{
#if NETCOREAPP2_1
_eventArgs.SetBuffer(buffer);
#else
var segment = buffer.GetArray();
_eventArgs.SetBuffer(segment.Array, segment.Offset, segment.Count);
#endif
if (!_socket.ReceiveAsync(_eventArgs))
{
_awaitable.Complete(_eventArgs.BytesTransferred, _eventArgs.SocketError);
}
return _awaitable;
}
}
}
@@ -0,0 +1,97 @@
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO.Pipelines;
using System.Net.Sockets;
namespace MQTTnet.AspNetCore.Client.Tcp
{
public class SocketSender
{
private readonly Socket _socket;
private readonly SocketAsyncEventArgs _eventArgs = new SocketAsyncEventArgs();
private readonly SocketAwaitable _awaitable;
private List<ArraySegment<byte>> _bufferList;
public SocketSender(Socket socket, PipeScheduler scheduler)
{
_socket = socket;
_awaitable = new SocketAwaitable(scheduler);
_eventArgs.UserToken = _awaitable;
_eventArgs.Completed += (_, e) => ((SocketAwaitable)e.UserToken).Complete(e.BytesTransferred, e.SocketError);
}
public SocketAwaitable SendAsync(in ReadOnlySequence<byte> buffers)
{
if (buffers.IsSingleSegment)
{
return SendAsync(buffers.First);
}
#if NETCOREAPP2_1
if (!_eventArgs.MemoryBuffer.Equals(Memory<byte>.Empty))
#else
if (_eventArgs.Buffer != null)
#endif
{
_eventArgs.SetBuffer(null, 0, 0);
}
_eventArgs.BufferList = GetBufferList(buffers);
if (!_socket.SendAsync(_eventArgs))
{
_awaitable.Complete(_eventArgs.BytesTransferred, _eventArgs.SocketError);
}
return _awaitable;
}
private SocketAwaitable SendAsync(ReadOnlyMemory<byte> memory)
{
// The BufferList getter is much less expensive then the setter.
if (_eventArgs.BufferList != null)
{
_eventArgs.BufferList = null;
}
#if NETCOREAPP2_1
_eventArgs.SetBuffer(MemoryMarshal.AsMemory(memory));
#else
var segment = memory.GetArray();
_eventArgs.SetBuffer(segment.Array, segment.Offset, segment.Count);
#endif
if (!_socket.SendAsync(_eventArgs))
{
_awaitable.Complete(_eventArgs.BytesTransferred, _eventArgs.SocketError);
}
return _awaitable;
}
private List<ArraySegment<byte>> GetBufferList(in ReadOnlySequence<byte> buffer)
{
Debug.Assert(!buffer.IsEmpty);
Debug.Assert(!buffer.IsSingleSegment);
if (_bufferList == null)
{
_bufferList = new List<ArraySegment<byte>>();
}
else
{
// Buffers are pooled, so it's OK to root them until the next multi-buffer write.
_bufferList.Clear();
}
foreach (var b in buffer)
{
_bufferList.Add(b.GetArray());
}
return _bufferList;
}
}
}
@@ -0,0 +1,258 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Pipelines;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Http.Features;
using MQTTnet.Exceptions;
namespace MQTTnet.AspNetCore.Client.Tcp
{
public class TcpConnection : ConnectionContext
{
private volatile bool _aborted;
private readonly EndPoint _endPoint;
private SocketSender _sender;
private SocketReceiver _receiver;
private Socket _socket;
private IDuplexPipe _application;
public bool IsConnected { get; private set; }
public override string ConnectionId { get; set; }
public override IFeatureCollection Features { get; }
public override IDictionary<object, object> Items { get; set; }
public override IDuplexPipe Transport { get; set; }
public TcpConnection(EndPoint endPoint)
{
_endPoint = endPoint;
}
public TcpConnection(Socket socket)
{
_socket = socket;
_endPoint = socket.RemoteEndPoint;
_sender = new SocketSender(_socket, PipeScheduler.ThreadPool);
_receiver = new SocketReceiver(_socket, PipeScheduler.ThreadPool);
}
public Task DisposeAsync()
{
IsConnected = false;
Transport?.Output.Complete();
Transport?.Input.Complete();
_socket?.Dispose();
return Task.CompletedTask;
}
public async Task StartAsync()
{
if (_socket == null)
{
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_sender = new SocketSender(_socket, PipeScheduler.ThreadPool);
_receiver = new SocketReceiver(_socket, PipeScheduler.ThreadPool);
await _socket.ConnectAsync(_endPoint);
}
var pair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default);
Transport = pair.Transport;
_application = pair.Application;
_ = ExecuteAsync();
IsConnected = true;
}
private async Task ExecuteAsync()
{
Exception sendError = null;
try
{
// Spawn send and receive logic
var receiveTask = DoReceive();
var sendTask = DoSend();
// If the sending task completes then close the receive
// We don't need to do this in the other direction because the kestrel
// will trigger the output closing once the input is complete.
if (await Task.WhenAny(receiveTask, sendTask) == sendTask)
{
// Tell the reader it's being aborted
_socket.Dispose();
}
// Now wait for both to complete
await receiveTask;
sendError = await sendTask;
// Dispose the socket(should noop if already called)
_socket.Dispose();
}
catch (Exception ex)
{
Console.WriteLine($"Unexpected exception in {nameof(TcpConnection)}.{nameof(StartAsync)}: " + ex);
}
finally
{
// Complete the output after disposing the socket
_application.Input.Complete(sendError);
}
}
private async Task DoReceive()
{
Exception error = null;
try
{
await ProcessReceives();
}
catch (SocketException ex) when (ex.SocketErrorCode == SocketError.ConnectionReset)
{
error = new MqttCommunicationException(ex);
}
catch (SocketException ex) when (ex.SocketErrorCode == SocketError.OperationAborted ||
ex.SocketErrorCode == SocketError.ConnectionAborted ||
ex.SocketErrorCode == SocketError.Interrupted ||
ex.SocketErrorCode == SocketError.InvalidArgument)
{
if (!_aborted)
{
// Calling Dispose after ReceiveAsync can cause an "InvalidArgument" error on *nix.
error = ConnectionAborted();
}
}
catch (ObjectDisposedException)
{
if (!_aborted)
{
error = ConnectionAborted();
}
}
catch (IOException ex)
{
error = ex;
}
catch (Exception ex)
{
error = new IOException(ex.Message, ex);
}
finally
{
if (_aborted)
{
error = error ?? ConnectionAborted();
}
_application.Output.Complete(error);
}
}
private async Task ProcessReceives()
{
while (true)
{
// Ensure we have some reasonable amount of buffer space
var buffer = _application.Output.GetMemory();
var bytesReceived = await _receiver.ReceiveAsync(buffer);
if (bytesReceived == 0)
{
// FIN
break;
}
_application.Output.Advance(bytesReceived);
var flushTask = _application.Output.FlushAsync();
if (!flushTask.IsCompleted)
{
await flushTask;
}
var result = flushTask.GetAwaiter().GetResult();
if (result.IsCompleted)
{
// Pipe consumer is shut down, do we stop writing
break;
}
}
}
private Exception ConnectionAborted()
{
return new MqttCommunicationException("Connection Aborted");
}
private async Task<Exception> DoSend()
{
Exception error = null;
try
{
await ProcessSends();
}
catch (SocketException ex) when (ex.SocketErrorCode == SocketError.OperationAborted)
{
}
catch (ObjectDisposedException)
{
}
catch (IOException ex)
{
error = ex;
}
catch (Exception ex)
{
error = new IOException(ex.Message, ex);
}
finally
{
_aborted = true;
_socket.Shutdown(SocketShutdown.Both);
}
return error;
}
private async Task ProcessSends()
{
while (true)
{
// Wait for data to write from the pipe producer
var result = await _application.Input.ReadAsync();
var buffer = result.Buffer;
if (result.IsCanceled)
{
break;
}
var end = buffer.End;
var isCompleted = result.IsCompleted;
if (!buffer.IsEmpty)
{
await _sender.SendAsync(buffer);
}
_application.Input.AdvanceTo(end);
if (isCompleted)
{
break;
}
}
}
}
}
@@ -0,0 +1,12 @@
using Microsoft.AspNetCore.Connections;
namespace MQTTnet.AspNetCore
{
public static class ConnectionBuilderExtensions
{
public static IConnectionBuilder UseMqtt(this IConnectionBuilder builder)
{
return builder.UseConnectionHandler<MqttConnectionHandler>();
}
}
}
@@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<Product />
<Company />
<Authors />
<PackageId />
<LangVersion>7.2</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DefineConstants>RELEASE;NETSTANDARD2_0</DefineConstants>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Connections.Abstractions" Version="2.1.0" />
<PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.0.1" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="2.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MQTTnet\MQTTnet.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,117 @@
using Microsoft.AspNetCore.Connections;
using MQTTnet.Adapter;
using MQTTnet.AspNetCore.Client.Tcp;
using MQTTnet.Packets;
using MQTTnet.Serializer;
using System;
using System.IO.Pipelines;
using System.Threading;
using System.Threading.Tasks;
namespace MQTTnet.AspNetCore
{
public class MqttConnectionContext : IMqttChannelAdapter
{
public MqttConnectionContext(
IMqttPacketSerializer packetSerializer,
ConnectionContext connection)
{
PacketSerializer = packetSerializer;
Connection = connection;
}
public string Endpoint => Connection.ConnectionId;
public ConnectionContext Connection { get; }
public IMqttPacketSerializer PacketSerializer { get; }
public event EventHandler ReadingPacketStarted;
public event EventHandler ReadingPacketCompleted;
public Task ConnectAsync(TimeSpan timeout, CancellationToken cancellationToken)
{
if (Connection is TcpConnection tcp && !tcp.IsConnected)
{
return tcp.StartAsync();
}
return Task.CompletedTask;
}
public Task DisconnectAsync(TimeSpan timeout, CancellationToken cancellationToken)
{
Connection.Transport.Input.Complete();
Connection.Transport.Output.Complete();
return Task.CompletedTask;
}
public async Task<MqttBasePacket> ReceivePacketAsync(TimeSpan timeout, CancellationToken cancellationToken)
{
var input = Connection.Transport.Input;
try
{
while (!cancellationToken.IsCancellationRequested)
{
ReadResult readResult;
var readTask = input.ReadAsync(cancellationToken);
if (readTask.IsCompleted)
{
readResult = readTask.Result;
}
else
{
readResult = await readTask;
}
var buffer = readResult.Buffer;
var consumed = buffer.Start;
var observed = buffer.Start;
try
{
if (!buffer.IsEmpty)
{
if (PacketSerializer.TryDeserialize(buffer, out var packet, out consumed, out observed))
{
return packet;
}
else
{
// we did receive something but the message is not yet complete
ReadingPacketStarted?.Invoke(this, EventArgs.Empty);
}
}
else if (readResult.IsCompleted)
{
break;
}
}
finally
{
// The buffer was sliced up to where it was consumed, so we can just advance to the start.
// We mark examined as buffer.End so that if we didn't receive a full frame, we'll wait for more data
// before yielding the read again.
input.AdvanceTo(consumed, observed);
}
}
}
finally
{
ReadingPacketCompleted?.Invoke(this, EventArgs.Empty);
}
cancellationToken.ThrowIfCancellationRequested();
return null;
}
public Task SendPacketAsync(MqttBasePacket packet, CancellationToken cancellationToken)
{
var buffer = PacketSerializer.Serialize(packet);
return Connection.Transport.Output.WriteAsync(buffer.AsMemory(), cancellationToken).AsTask();
}
public void Dispose()
{
}
}
}
@@ -0,0 +1,49 @@
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Connections.Features;
using MQTTnet.Adapter;
using MQTTnet.Serializer;
using MQTTnet.Server;
using System;
using System.Threading.Tasks;
namespace MQTTnet.AspNetCore
{
public class MqttConnectionHandler : ConnectionHandler, IMqttServerAdapter
{
public event EventHandler<MqttServerAdapterClientAcceptedEventArgs> ClientAccepted;
public override async Task OnConnectedAsync(ConnectionContext connection)
{
// required for websocket transport to work
var transferFormatFeature = connection.Features.Get<ITransferFormatFeature>();
if (transferFormatFeature != null)
{
transferFormatFeature.ActiveFormat = TransferFormat.Binary;
}
var serializer = new MqttPacketSerializer();
using (var adapter = new MqttConnectionContext(serializer, connection))
{
var args = new MqttServerAdapterClientAcceptedEventArgs(adapter);
ClientAccepted?.Invoke(this, args);
await args.SessionTask;
}
}
public Task StartAsync(IMqttServerOptions options)
{
return Task.CompletedTask;
}
public Task StopAsync()
{
return Task.CompletedTask;
}
public void Dispose()
{
}
}
}
@@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using MQTTnet.Adapter;
using MQTTnet.Diagnostics;
using MQTTnet.Server;
namespace MQTTnet.AspNetCore
{
public class MqttHostedServer : MqttServer, IHostedService
{
private readonly IMqttServerOptions _options;
public MqttHostedServer(IMqttServerOptions options, IEnumerable<IMqttServerAdapter> adapters, IMqttNetLogger logger)
: base(adapters, logger.CreateChildLogger(nameof(MqttHostedServer)))
{
_options = options ?? throw new ArgumentNullException(nameof(options));
}
public Task StartAsync(CancellationToken cancellationToken)
{
return StartAsync(_options);
}
public Task StopAsync(CancellationToken cancellationToken)
{
return StopAsync();
}
}
}

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