mirror of
https://github.com/dotnet/MQTTnet.git
synced 2026-10-16 01:03:01 +08:00
Compare commits
1330
Commits
v2.7.5
...
v4.1.3.436
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e4b07f2b8c | ||
|
|
4eb6e77eb5 | ||
|
|
db1fc7625d | ||
|
|
bfec9dc5ad | ||
|
|
56f0a2c31c | ||
|
|
5ee0bb4b65 | ||
|
|
5c74728387 | ||
|
|
97c46bea0f | ||
|
|
e6c1ab4e4c | ||
|
|
6e1ec266a7 | ||
|
|
3fc6982e9a | ||
|
|
5b1cc7d713 | ||
|
|
2842f27522 | ||
|
|
7ef137e26e | ||
|
|
b1a336eb62 | ||
|
|
d481e93832 | ||
|
|
8cf5537043 | ||
|
|
d2ce9d607a | ||
|
|
36736914bc | ||
|
|
7823d8342e | ||
|
|
431142471f | ||
|
|
a3227a444a | ||
|
|
f74af45fba | ||
|
|
e050e2d61e | ||
|
|
ef85ef676a | ||
|
|
e977546297 | ||
|
|
ae92b0ed33 | ||
|
|
7e686d9aa5 | ||
|
|
d23bbf7bf3 | ||
|
|
d4dad4860a | ||
|
|
c009cbef76 | ||
|
|
37e60c73ae | ||
|
|
3ab095a568 | ||
|
|
f5d9b65139 | ||
|
|
31e5b40459 | ||
|
|
8fb55d75fe | ||
|
|
420222511b | ||
|
|
c5cec60809 | ||
|
|
12dd815421 | ||
|
|
f36f49cb3e | ||
|
|
9152fe637e | ||
|
|
b976e9c177 | ||
|
|
262a4681ad | ||
|
|
ad546adafe | ||
|
|
29a9941d86 | ||
|
|
5a21939e29 | ||
|
|
469caf656e | ||
|
|
42402fba04 | ||
|
|
a0dc8ab0d7 | ||
|
|
02ede5a2d8 | ||
|
|
cef61900df | ||
|
|
09385b1b36 | ||
|
|
cbf413f04e | ||
|
|
11249d006f | ||
|
|
0d7b020994 | ||
|
|
d71ff3d615 | ||
|
|
57745e8324 | ||
|
|
a94776eeea | ||
|
|
d3848d8cb0 | ||
|
|
6c29ea56fc | ||
|
|
ed7c150cc5 | ||
|
|
13762eedd4 | ||
|
|
e33495010e | ||
|
|
b0d1fcd161 | ||
|
|
f7a739e162 | ||
|
|
422945929e | ||
|
|
76e54dc49b | ||
|
|
b10a69a0ae | ||
|
|
4b1c7cf772 | ||
|
|
3f67df007b | ||
|
|
36b1ed06f0 | ||
|
|
4a982ea228 | ||
|
|
9fe5073186 | ||
|
|
305c552474 | ||
|
|
f632fbb447 | ||
|
|
8231d62f9f | ||
|
|
a52a717965 | ||
|
|
2b653f2379 | ||
|
|
725cfb643f | ||
|
|
29ec6edbf4 | ||
|
|
f3ef2dbf7c | ||
|
|
14eb202873 | ||
|
|
a3c7f9a22c | ||
|
|
41e0a25fc8 | ||
|
|
772cd3cd12 | ||
|
|
42cf9f73c2 | ||
|
|
ec2e08668f | ||
|
|
e9e8583d1a | ||
|
|
f68b109471 | ||
|
|
dd1e155ff4 | ||
|
|
cf6369d63f | ||
|
|
3049fc6608 | ||
|
|
b75076a1c5 | ||
|
|
ae8e2ec7c9 | ||
|
|
e7e6a080e0 | ||
|
|
dc9afa6c3a | ||
|
|
02b17f277d | ||
|
|
b9d9ea2d4d | ||
|
|
c9cc6c9c0f | ||
|
|
967c964111 | ||
|
|
80aaca63f9 | ||
|
|
eeeb479db2 | ||
|
|
9e47a0985b | ||
|
|
2d9ae0576b | ||
|
|
fcf276dd1a | ||
|
|
920728e77a | ||
|
|
1d4794b071 | ||
|
|
626baa7bb5 | ||
|
|
810746b56b | ||
|
|
7cf454952f | ||
|
|
cf2974cd34 | ||
|
|
7d95c238c3 | ||
|
|
9a94b0465d | ||
|
|
1d8b6e387d | ||
|
|
73d681bc1f | ||
|
|
2ce9b94ec9 | ||
|
|
47e674e4d3 | ||
|
|
d5691346a9 | ||
|
|
0730f21d29 | ||
|
|
6dfa7f0769 | ||
|
|
27592f1643 | ||
|
|
c6f4b59438 | ||
|
|
b362b2afc2 | ||
|
|
7a72a8d562 | ||
|
|
bd40e10a66 | ||
|
|
4b44a2b2b1 | ||
|
|
215f24a95b | ||
|
|
72f9acdc20 | ||
|
|
1e5390679f | ||
|
|
5cafce4345 | ||
|
|
dfaf3dc31f | ||
|
|
3a9397c793 | ||
|
|
1cf4a7d6be | ||
|
|
180588c3a4 | ||
|
|
bc4c86bebd | ||
|
|
ea88a8c0de | ||
|
|
d98129481a | ||
|
|
5e5818d7df | ||
|
|
72c7a8959c | ||
|
|
39e637795e | ||
|
|
731c89d2e9 | ||
|
|
25ddfc83a8 | ||
|
|
2d94abdde3 | ||
|
|
f0ea13c07f | ||
|
|
47cd5aa3cd | ||
|
|
258df6bb14 | ||
|
|
85c3c0b352 | ||
|
|
8020e66f41 | ||
|
|
bd004ec327 | ||
|
|
1e68c2e815 | ||
|
|
d7e1c89d62 | ||
|
|
b779d04636 | ||
|
|
623d8cafaa | ||
|
|
0920c86d37 | ||
|
|
c756a4092f | ||
|
|
feb53e78c6 | ||
|
|
dd0aba0640 | ||
|
|
68f1043ef5 | ||
|
|
d01d859227 | ||
|
|
fcab0d5cf0 | ||
|
|
b66a18f8dd | ||
|
|
8e1e93c00f | ||
|
|
cd8940f9b7 | ||
|
|
9d9077f17c | ||
|
|
fa6859d68a | ||
|
|
4f14373117 | ||
|
|
90ec39f2a4 | ||
|
|
2aa4d1b77a | ||
|
|
aa9f8d8ef3 | ||
|
|
50381637af | ||
|
|
8905b612c9 | ||
|
|
3cdf6d1261 | ||
|
|
d1c883ebae | ||
|
|
ed5e6ee897 | ||
|
|
2e019f372c | ||
|
|
cc9bc2d3a0 | ||
|
|
306bce5a58 | ||
|
|
6fe7ce7a0c | ||
|
|
8f1d4e3c22 | ||
|
|
77338527b4 | ||
|
|
962622a9f7 | ||
|
|
899ed7b1da | ||
|
|
10f7d809f2 | ||
|
|
324fb7e48c | ||
|
|
d913d4402e | ||
|
|
df262b5588 | ||
|
|
27daf37ba4 | ||
|
|
e5e10eee6d | ||
|
|
e174546b5a | ||
|
|
f99daee4d1 | ||
|
|
9b10eadb24 | ||
|
|
c39da42ef2 | ||
|
|
85b339e6f0 | ||
|
|
fbb5cd45a0 | ||
|
|
d430ca0250 | ||
|
|
cf8ea872d3 | ||
|
|
d4440f3be1 | ||
|
|
b97d01b78b | ||
|
|
375ee5c4ac | ||
|
|
90b37a1e2d | ||
|
|
0978f6cc75 | ||
|
|
e7c51e8dbc | ||
|
|
aab4448980 | ||
|
|
52139c6a99 | ||
|
|
f3d121120b | ||
|
|
413c68de44 | ||
|
|
f0919487f2 | ||
|
|
ffa7c51899 | ||
|
|
493cad087f | ||
|
|
4d94e1a318 | ||
|
|
28ff08ceb9 | ||
|
|
0f3d461c37 | ||
|
|
4285169108 | ||
|
|
1b1f2db1e6 | ||
|
|
d7248c0f64 | ||
|
|
d2e1b12186 | ||
|
|
dafcfb47d2 | ||
|
|
897f0f36d1 | ||
|
|
9cb12ac2ce | ||
|
|
312fb2bb3c | ||
|
|
3ee785c781 | ||
|
|
0ea4ae7501 | ||
|
|
7e764c7f0f | ||
|
|
5c2879029b | ||
|
|
6cbb8d42e3 | ||
|
|
d8d50f6dcb | ||
|
|
a01dc9fef8 | ||
|
|
3427d82efc | ||
|
|
5915285522 | ||
|
|
12f346115f | ||
|
|
05827341d4 | ||
|
|
1e88d6a05b | ||
|
|
8261ba9dc1 | ||
|
|
b766c1bebb | ||
|
|
6c188899fb | ||
|
|
23c5270c3f | ||
|
|
2d4ea8ee9b | ||
|
|
15d3761843 | ||
|
|
ed520455d0 | ||
|
|
cfc4ebe6a3 | ||
|
|
120b7fb7d1 | ||
|
|
8b164a4846 | ||
|
|
fda4272611 | ||
|
|
88cc79d52f | ||
|
|
8ba960343c | ||
|
|
0976fa682a | ||
|
|
18cc454842 | ||
|
|
e342a51d50 | ||
|
|
57652c76cf | ||
|
|
bb52159e49 | ||
|
|
109acccd19 | ||
|
|
deb2c3dec6 | ||
|
|
63ca6086d5 | ||
|
|
3f92f2ca2b | ||
|
|
a13874d8a5 | ||
|
|
c91a1f8572 | ||
|
|
b103718bd9 | ||
|
|
b88924bce5 | ||
|
|
f3f1d81b52 | ||
|
|
031b60878d | ||
|
|
1fbf20a569 | ||
|
|
b1bf7698aa | ||
|
|
fbf7d9bb5c | ||
|
|
822f582e41 | ||
|
|
f8eaf02516 | ||
|
|
df45540d6b | ||
|
|
2abcf6d8bc | ||
|
|
c1001db450 | ||
|
|
fcbe2e32f6 | ||
|
|
f955b4a3e6 | ||
|
|
3d8d06f9b3 | ||
|
|
d804b805c1 | ||
|
|
bba04e2b5d | ||
|
|
5eab337e13 | ||
|
|
e1d39a3fef | ||
|
|
3f5d627e9e | ||
|
|
2047f44554 | ||
|
|
75845d6416 | ||
|
|
74e952bb03 | ||
|
|
3773048bc5 | ||
|
|
f9aec0c067 | ||
|
|
91c8a15f65 | ||
|
|
b95d6b08bc | ||
|
|
a44ea74739 | ||
|
|
02e65bca67 | ||
|
|
37b783908a | ||
|
|
72211ae85a | ||
|
|
1f78a92c4d | ||
|
|
f92cfcd293 | ||
|
|
86256d1b10 | ||
|
|
c3e65390ab | ||
|
|
b0699003e2 | ||
|
|
1214b15260 | ||
|
|
322067466b | ||
|
|
ef2ed1c897 | ||
|
|
9fddfb3e77 | ||
|
|
ec015e7620 | ||
|
|
7738c5faa1 | ||
|
|
01bb09ff4b | ||
|
|
e1494c1004 | ||
|
|
1e2231c461 | ||
|
|
de0f5dc1f0 | ||
|
|
a93f44217b | ||
|
|
f9c3014e52 | ||
|
|
483c2d8b10 | ||
|
|
02ab0e4903 | ||
|
|
0bc25571e0 | ||
|
|
5b29b8cfd8 | ||
|
|
dd9dc054e8 | ||
|
|
b7c178b85c | ||
|
|
f8e29f8dc0 | ||
|
|
3d68951f68 | ||
|
|
7eb6d2d1e3 | ||
|
|
0446635ed6 | ||
|
|
4514348821 | ||
|
|
7b32abe625 | ||
|
|
0febc12b3d | ||
|
|
d0a848c8b4 | ||
|
|
fe69f5a633 | ||
|
|
ca95754fc3 | ||
|
|
cc405c3e90 | ||
|
|
315206f05e | ||
|
|
f252374437 | ||
|
|
38d3531db6 | ||
|
|
da5a172504 | ||
|
|
f894622884 | ||
|
|
88b0af9b21 | ||
|
|
e1bf9de347 | ||
|
|
6c15693456 | ||
|
|
4608930a37 | ||
|
|
eaf1195f95 | ||
|
|
a35dee9352 | ||
|
|
6cef746ed9 | ||
|
|
344cf674e6 | ||
|
|
26af205ab0 | ||
|
|
06c9d27c87 | ||
|
|
14224e4180 | ||
|
|
b954c9512e | ||
|
|
fb2be79673 | ||
|
|
44b7133a1c | ||
|
|
2f0513212e | ||
|
|
1a9460776b | ||
|
|
2716db04e3 | ||
|
|
3e4f4a59a8 | ||
|
|
24f0ea01f5 | ||
|
|
1625aadae9 | ||
|
|
e800901b4c | ||
|
|
34ae54d542 | ||
|
|
838d50c1d3 | ||
|
|
558d281a33 | ||
|
|
f8c956a9e2 | ||
|
|
9a6823597e | ||
|
|
42c1155bee | ||
|
|
f773257c86 | ||
|
|
4e9df98817 | ||
|
|
3208045baa | ||
|
|
7274f4390c | ||
|
|
11c8abfd54 | ||
|
|
f606a5996b | ||
|
|
58ad599783 | ||
|
|
ccd102a59f | ||
|
|
05101f0e0e | ||
|
|
f9630ef347 | ||
|
|
c5b7735a2c | ||
|
|
12fca2f05a | ||
|
|
b7fb0c1002 | ||
|
|
3ea281ff84 | ||
|
|
c778cd7a0f | ||
|
|
adce5662eb | ||
|
|
a26b641284 | ||
|
|
7be3685850 | ||
|
|
1a6be47144 | ||
|
|
5d73de2a86 | ||
|
|
2eaebe0e6a | ||
|
|
d7b552fce2 | ||
|
|
bea6d75251 | ||
|
|
f7db00c08e | ||
|
|
f259bbcc9b | ||
|
|
252772007b | ||
|
|
21a9974e5d | ||
|
|
d63143bdc9 | ||
|
|
aad9eac361 | ||
|
|
e8a683b936 | ||
|
|
e475e36ce7 | ||
|
|
611c230da2 | ||
|
|
18839de083 | ||
|
|
194664bc9c | ||
|
|
10a33fd153 | ||
|
|
868aa7d89a | ||
|
|
1f8792cf87 | ||
|
|
7bb99c8b37 | ||
|
|
7f5504b01b | ||
|
|
fd789417e7 | ||
|
|
f1f9924bce | ||
|
|
b0c603082b | ||
|
|
f3f10e9f65 | ||
|
|
beb228cc05 | ||
|
|
7a7f0640ba | ||
|
|
55d009ce6b | ||
|
|
e23ac4f703 | ||
|
|
fc367ddb64 | ||
|
|
cf217034fa | ||
|
|
6b6627f823 | ||
|
|
4f51a238b3 | ||
|
|
733c587e45 | ||
|
|
3260920828 | ||
|
|
4e6aeb91a4 | ||
|
|
5056843611 | ||
|
|
a65c015a58 | ||
|
|
e63f26789f | ||
|
|
9a598892a7 | ||
|
|
857ab005be | ||
|
|
33e4ec1943 | ||
|
|
6a564a0c72 | ||
|
|
3a4dce5080 | ||
|
|
4e6118d324 | ||
|
|
269bfa09ac | ||
|
|
59dd6b98a3 | ||
|
|
9f37306f20 | ||
|
|
85b000c6b7 | ||
|
|
5a919ce5d2 | ||
|
|
57ab8e5abc | ||
|
|
8f6d48be81 | ||
|
|
38cfa15cc3 | ||
|
|
6adf7096ec | ||
|
|
df149eb0b3 | ||
|
|
63c9329190 | ||
|
|
246a10649a | ||
|
|
0a76ac15c6 | ||
|
|
d801af5616 | ||
|
|
257211fc7a | ||
|
|
a407fc381b | ||
|
|
4fa275bf2b | ||
|
|
7cf12dc751 | ||
|
|
40aefd0ad3 | ||
|
|
c20142671f | ||
|
|
75dfe68a94 | ||
|
|
92518fc4ec | ||
|
|
16180e83ba | ||
|
|
c779b394f1 | ||
|
|
e60a732b61 | ||
|
|
7ea9415c63 | ||
|
|
13c399bc9a | ||
|
|
4565c3990a | ||
|
|
2bd2648b48 | ||
|
|
1b56155f1c | ||
|
|
626930cfae | ||
|
|
6cf9cb0af2 | ||
|
|
60941c04e5 | ||
|
|
ba68b1274b | ||
|
|
dbe12b9c48 | ||
|
|
52b79a140b | ||
|
|
c270225e9e | ||
|
|
3f5d4e9825 | ||
|
|
e80d19ba9b | ||
|
|
48837529a2 | ||
|
|
43e3ad4c00 | ||
|
|
08c9ecb011 | ||
|
|
763f0e7934 | ||
|
|
9d381c2cf9 | ||
|
|
17ceb20169 | ||
|
|
683bb43486 | ||
|
|
d25b51740b | ||
|
|
a58ab607ad | ||
|
|
0a7d450f3b | ||
|
|
999fa7a468 | ||
|
|
f0a1f9ca93 | ||
|
|
89e9cb224f | ||
|
|
fe48c9c837 | ||
|
|
2a421d1d42 | ||
|
|
48d0344a40 | ||
|
|
db5ed594f7 | ||
|
|
f85bc9b956 | ||
|
|
eec46dd9bc | ||
|
|
9c9e2293c8 | ||
|
|
856861e1b1 | ||
|
|
4b481b4833 | ||
|
|
c05f11a495 | ||
|
|
e798c91ff3 | ||
|
|
5df4fc3d5d | ||
|
|
c6236c0865 | ||
|
|
85c0cb3bcc | ||
|
|
9810e52bcb | ||
|
|
948a5eaae3 | ||
|
|
31df8247fa | ||
|
|
229418100c | ||
|
|
93d8679bbc | ||
|
|
6cec126862 | ||
|
|
9749f790ea | ||
|
|
fbbc638c75 | ||
|
|
bf679adb6b | ||
|
|
cf7e59a774 | ||
|
|
e9724c0202 | ||
|
|
7931af7487 | ||
|
|
bb5f023b74 | ||
|
|
c2ac4fffaf | ||
|
|
4b78710705 | ||
|
|
2e69f8be78 | ||
|
|
2d6c44e45b | ||
|
|
fc3965f5a3 | ||
|
|
1d553c5ece | ||
|
|
07ad9fc557 | ||
|
|
f001d7aecc | ||
|
|
d49a4384c0 | ||
|
|
4e6658e014 | ||
|
|
2b2b30912c | ||
|
|
79d95c8516 | ||
|
|
88e8691ce0 | ||
|
|
28641892f5 | ||
|
|
4b08bb41f5 | ||
|
|
c89918a890 | ||
|
|
6546175ea2 | ||
|
|
b9e9fe64ac | ||
|
|
9fd7c7bc1e | ||
|
|
fcd6b7ff52 | ||
|
|
54870adc64 | ||
|
|
892e4842ed | ||
|
|
fc358f9015 | ||
|
|
b93cd5b673 | ||
|
|
aafc99f39e | ||
|
|
b14d7affe5 | ||
|
|
67d56644b2 | ||
|
|
6e0d3ec0fb | ||
|
|
2c6613a7b7 | ||
|
|
d464bfb7c5 | ||
|
|
7a24970035 | ||
|
|
db906eca24 | ||
|
|
ad128c7889 | ||
|
|
3e096bc353 | ||
|
|
f0be8404cf | ||
|
|
6de28d32e4 | ||
|
|
2ede7df38a | ||
|
|
2a35108a73 | ||
|
|
774d49a908 | ||
|
|
c83bec6e81 | ||
|
|
763d9778bb | ||
|
|
9368eebfd5 | ||
|
|
81b547f386 | ||
|
|
e33c5ccb4c | ||
|
|
0ca4c88e69 | ||
|
|
04ccdcd562 | ||
|
|
aafd4ea658 | ||
|
|
4eb65fc3d2 | ||
|
|
f7f00f4e06 | ||
|
|
af0b98eace | ||
|
|
0da2ab9de6 | ||
|
|
ea22ef4164 | ||
|
|
cb5a0cf38b | ||
|
|
55dc940abb | ||
|
|
c40e3c64aa | ||
|
|
87c4fa14ee | ||
|
|
0653b89701 | ||
|
|
561474c572 | ||
|
|
c10145f743 | ||
|
|
dca2af47d8 | ||
|
|
9ed80daf83 | ||
|
|
0d39bbbc0c | ||
|
|
52faf6d76a | ||
|
|
20eda96921 | ||
|
|
f6a814c6d7 | ||
|
|
8e0bce7734 | ||
|
|
72fa13ca5b | ||
|
|
45e3455e6c | ||
|
|
160da00f55 | ||
|
|
6b4b829cf6 | ||
|
|
e7fa465616 | ||
|
|
799005803e | ||
|
|
a20c845a5a | ||
|
|
5f59aca62c | ||
|
|
76ddf8c503 | ||
|
|
122034296c | ||
|
|
6f71aaf1ce | ||
|
|
fd8caed9af | ||
|
|
313f90a526 | ||
|
|
4d27f42e72 | ||
|
|
8ae318fd5c | ||
|
|
7dde2f1f4c | ||
|
|
49c5278ba0 | ||
|
|
ce51d12a03 | ||
|
|
b1559a8fa5 | ||
|
|
457143c840 | ||
|
|
c60bf51b1e | ||
|
|
630400da53 | ||
|
|
a152066ed3 | ||
|
|
138cd16c8d | ||
|
|
27a61819d3 | ||
|
|
9c378fb088 | ||
|
|
d4088420cf | ||
|
|
2e7d074174 | ||
|
|
328ffc734f | ||
|
|
8ecf658059 | ||
|
|
7b25c6f274 | ||
|
|
069c19aab9 | ||
|
|
35e729bd7b | ||
|
|
ac06a001e2 | ||
|
|
a77e0b8dde | ||
|
|
698f0b66d2 | ||
|
|
14419e4d63 | ||
|
|
3600ebbc62 | ||
|
|
55676965d9 | ||
|
|
f949d54aef | ||
|
|
624fafc662 | ||
|
|
23e93212a4 | ||
|
|
f9b2c91ddb | ||
|
|
9fb1cc3320 | ||
|
|
1c89a8bc75 | ||
|
|
8be9ce8024 | ||
|
|
002ba27fd3 | ||
|
|
9aaa94e821 | ||
|
|
e8ff48bfd3 | ||
|
|
5db05f186f | ||
|
|
b2c2209b4f | ||
|
|
c490e83c91 | ||
|
|
ac326babdf | ||
|
|
a38623cb5a | ||
|
|
a63489fa5e | ||
|
|
4b7409240b | ||
|
|
896e9ed0aa | ||
|
|
0950dfbb8b | ||
|
|
4105beb7e6 | ||
|
|
5c25905510 | ||
|
|
e3f6b579b2 | ||
|
|
c467c9c818 | ||
|
|
78b57531b6 | ||
|
|
84d9f533bd | ||
|
|
7479e320ee | ||
|
|
152303dfbd | ||
|
|
a495668f2b | ||
|
|
82b714d4c7 | ||
|
|
44d71b884c | ||
|
|
781c5d4d7d | ||
|
|
b175119008 | ||
|
|
cfcb858423 | ||
|
|
5fa12649bf | ||
|
|
a9a09f4ecd | ||
|
|
3ff5806dbb | ||
|
|
0ec78cf5cc | ||
|
|
c3193c568a | ||
|
|
a1f02284e9 | ||
|
|
851db2e808 | ||
|
|
073cd5243f | ||
|
|
699558e47a | ||
|
|
15ff03c136 | ||
|
|
b12e3bc611 | ||
|
|
61c3e02242 | ||
|
|
5618c4c1f7 | ||
|
|
ba9ceed7ce | ||
|
|
ad1c198e43 | ||
|
|
2918be18c6 | ||
|
|
248e2a5bdd | ||
|
|
1a41415dd1 | ||
|
|
9302e80d73 | ||
|
|
cf6f0ece0e | ||
|
|
50d65f2fed | ||
|
|
4614df7fa5 | ||
|
|
22aedcba09 | ||
|
|
2767d897a4 | ||
|
|
a4420063c0 | ||
|
|
2320352124 | ||
|
|
c0b4155ab4 | ||
|
|
b09f31d654 | ||
|
|
30c33d46f9 | ||
|
|
651b79a124 | ||
|
|
2e3cf54f11 | ||
|
|
d7948afd98 | ||
|
|
4cefa1dfa9 | ||
|
|
0cfb8b4020 | ||
|
|
9ac726b271 | ||
|
|
232605304e | ||
|
|
6c53677e7d | ||
|
|
6d0e2c2022 | ||
|
|
8ad1fb2871 | ||
|
|
4a7ff9e9ed | ||
|
|
f54598c201 | ||
|
|
2300e68fff | ||
|
|
6996824157 | ||
|
|
524f695829 | ||
|
|
72faa067cc | ||
|
|
01aa75471c | ||
|
|
048206eb1e | ||
|
|
ad5b0caa4d | ||
|
|
0f14ddcf79 | ||
|
|
59d2a8e551 | ||
|
|
8607ceb3b7 | ||
|
|
b82e5a48a4 | ||
|
|
30c5147c62 | ||
|
|
9d63500f95 | ||
|
|
a03bdc7616 | ||
|
|
413daee551 | ||
|
|
f3ea7008e5 | ||
|
|
8bfbbd2b29 | ||
|
|
ab8c7df0b9 | ||
|
|
975eb60e90 | ||
|
|
6076151222 | ||
|
|
9cc96f1888 | ||
|
|
7aa4d5159c | ||
|
|
9b021c4093 | ||
|
|
54c303369e | ||
|
|
8f30042e49 | ||
|
|
4c33270d26 | ||
|
|
5c40577bec | ||
|
|
3d404f9755 | ||
|
|
1b5e0197e4 | ||
|
|
1dab729d84 | ||
|
|
3eac5bcaf3 | ||
|
|
8eb23f0f1d | ||
|
|
1fd1a12fce | ||
|
|
349d506b37 | ||
|
|
a981b05aa7 | ||
|
|
654f06d20d | ||
|
|
c92cd10499 | ||
|
|
ed06ea6bc9 | ||
|
|
3d531151c6 | ||
|
|
e1590fc0c8 | ||
|
|
dbb227406b | ||
|
|
b891e1d1f4 | ||
|
|
1d697e3b87 | ||
|
|
128484c55a | ||
|
|
ff22c34bb2 | ||
|
|
e7ba3f76dc | ||
|
|
1ae3324581 | ||
|
|
ba8ce15ab4 | ||
|
|
87b4ce5185 | ||
|
|
4eb2b77138 | ||
|
|
70ee49f3c6 | ||
|
|
f223a26748 | ||
|
|
4574fdc951 | ||
|
|
4f66614cdd | ||
|
|
6608172719 | ||
|
|
6c8db47e25 | ||
|
|
3eb5e82d10 | ||
|
|
8563230f9c | ||
|
|
019f428f0a | ||
|
|
1847af13d2 | ||
|
|
8916df8d7b | ||
|
|
f66da92299 | ||
|
|
979df5f17e | ||
|
|
d50c8153b2 | ||
|
|
b3ab7a1069 | ||
|
|
6af58ad90d | ||
|
|
5c36a2d402 | ||
|
|
6df70cc5ed | ||
|
|
d5b0893e90 | ||
|
|
2ed8e55394 | ||
|
|
c5b6ca204d | ||
|
|
7a767c7d2f | ||
|
|
dd96cf98a8 | ||
|
|
6ee3d1640f | ||
|
|
6f8272210c | ||
|
|
ce05cc9936 | ||
|
|
985c3084a8 | ||
|
|
7b3be3a64f | ||
|
|
c50fb740fc | ||
|
|
5895a73f85 | ||
|
|
961a497798 | ||
|
|
6a90864c44 | ||
|
|
a9017b8795 | ||
|
|
f76e130283 | ||
|
|
5a48b146b4 | ||
|
|
1d1def3275 | ||
|
|
bd75a2440e | ||
|
|
7a642802ed | ||
|
|
a8f8601bc5 | ||
|
|
0c7a5a992b | ||
|
|
bd665c3d56 | ||
|
|
7e7b36aadf | ||
|
|
a79c2484b3 | ||
|
|
e4e46f0094 | ||
|
|
5a4c735b85 | ||
|
|
42516942ac | ||
|
|
591ccfec29 | ||
|
|
ffc1d950da | ||
|
|
1c004ead6d | ||
|
|
b0ad6bda70 | ||
|
|
6262a16504 | ||
|
|
0de683979d | ||
|
|
0a6f564446 | ||
|
|
f0be3ef0cf | ||
|
|
f79eebdf64 | ||
|
|
c3c0dcf313 | ||
|
|
60c6074953 | ||
|
|
2b9b22def6 | ||
|
|
5cfb092449 | ||
|
|
a450fc4e01 | ||
|
|
ffe9c57ae8 | ||
|
|
0257e31910 | ||
|
|
7fe5f521dc | ||
|
|
2d03c267e4 | ||
|
|
ccfe67e6a6 | ||
|
|
98e17903ea | ||
|
|
c7d56a78f3 | ||
|
|
b491c74228 | ||
|
|
a2414f48ca | ||
|
|
396e0e1697 | ||
|
|
01f6c959e1 | ||
|
|
f425d87562 | ||
|
|
6614b23ab8 | ||
|
|
02c9133a1c | ||
|
|
913afa3977 | ||
|
|
e068286f15 | ||
|
|
373fe8c72c | ||
|
|
396f040aae | ||
|
|
c90b5f6fb2 | ||
|
|
744b1b302b | ||
|
|
78e504dc2c | ||
|
|
79518d4a35 | ||
|
|
a50fb2a320 | ||
|
|
9fa9d22097 | ||
|
|
e4351b73bd | ||
|
|
2d3fdd4faf | ||
|
|
0a509fd685 | ||
|
|
d1a17314ef | ||
|
|
e96b66e9f8 | ||
|
|
80b05a8944 | ||
|
|
ab3df2ba65 | ||
|
|
f169aaa79b | ||
|
|
231001850a | ||
|
|
6839970d41 | ||
|
|
4df29be155 | ||
|
|
c5cfede231 | ||
|
|
c22b6033e3 | ||
|
|
27476a9194 | ||
|
|
12973a4179 | ||
|
|
01b437dc08 | ||
|
|
ac88f0a05a | ||
|
|
b02f30844c | ||
|
|
1a8bcae47c | ||
|
|
664549ab93 | ||
|
|
914d2b641f | ||
|
|
0cc0fcf5d9 | ||
|
|
5980cbd944 | ||
|
|
3ef7f60a18 | ||
|
|
efef23e3f5 | ||
|
|
53ab2ac140 | ||
|
|
2ecbd83d06 | ||
|
|
a278b6189b | ||
|
|
cdbdfdec6d | ||
|
|
808c2da5f6 | ||
|
|
6ed3b0cedd | ||
|
|
9da3377aa7 | ||
|
|
0cac2aa1bc | ||
|
|
2d59b5c1ac | ||
|
|
9dfa0b32e4 | ||
|
|
20a7579156 | ||
|
|
c7409c2ff4 | ||
|
|
d4c6fb44c9 | ||
|
|
e6cf23aebd | ||
|
|
0fec860b25 | ||
|
|
2c927c5995 | ||
|
|
6399172582 | ||
|
|
5c903e6fbf | ||
|
|
f60bbf7dd7 | ||
|
|
519bb05aac | ||
|
|
a8434d1b58 | ||
|
|
420b35e37d | ||
|
|
a25c8cd4c7 | ||
|
|
8ac77558af | ||
|
|
953578eba2 | ||
|
|
527713ccf8 | ||
|
|
477e2d23ab | ||
|
|
03c61ad82c | ||
|
|
9475ccd0d8 | ||
|
|
73b051d159 | ||
|
|
a9944def68 | ||
|
|
aee10ba6fa | ||
|
|
cfc1b27950 | ||
|
|
149d01e173 | ||
|
|
888e1797d7 | ||
|
|
f8ade2daf4 | ||
|
|
a85792c392 | ||
|
|
fbca8a6dc1 | ||
|
|
732ea84caa | ||
|
|
65896269da | ||
|
|
631f660d5c | ||
|
|
7f08371532 | ||
|
|
1afd59325f | ||
|
|
45c033f401 | ||
|
|
7855eccb78 | ||
|
|
d51300d32d | ||
|
|
61b10a8b56 | ||
|
|
dbee941dcc | ||
|
|
fd7c98d0d1 | ||
|
|
c911797f1b | ||
|
|
b5a1d3a69f | ||
|
|
cdeb073bb0 | ||
|
|
a8db0dd3d6 | ||
|
|
2267770a72 | ||
|
|
35ee732045 | ||
|
|
ad5834dd1f | ||
|
|
a7204234f6 | ||
|
|
e8c33077b7 | ||
|
|
bd86dae8a9 | ||
|
|
7188aaf79d | ||
|
|
bafcc81672 | ||
|
|
d0f1659eec | ||
|
|
11a5b5a080 | ||
|
|
7ed5580352 | ||
|
|
efc6f124b4 | ||
|
|
7c3d88f62d | ||
|
|
b383a54416 | ||
|
|
673fc5e20c | ||
|
|
f2b9642712 | ||
|
|
4f03f22afb | ||
|
|
27a4af6070 | ||
|
|
0184ce400c | ||
|
|
653ea8a102 | ||
|
|
3450889e84 | ||
|
|
1311535eed | ||
|
|
e1799ea6a3 | ||
|
|
cd76f434e3 | ||
|
|
fd91170aee | ||
|
|
cd1bfc3a10 | ||
|
|
f074e9ed04 | ||
|
|
562a5304f6 | ||
|
|
1e697a94a1 | ||
|
|
0e2c3abd05 | ||
|
|
7321774c45 | ||
|
|
4a65cab769 | ||
|
|
510c3bd0bf | ||
|
|
0c056b511e | ||
|
|
2ca6866c5e | ||
|
|
ebcce9b8be | ||
|
|
9e7c70e649 | ||
|
|
bef0860e53 | ||
|
|
67c9177731 | ||
|
|
007991f073 | ||
|
|
532dddeb6c | ||
|
|
9070087fad | ||
|
|
de8bcb7f37 | ||
|
|
5090cfb411 | ||
|
|
6e64d653c5 | ||
|
|
461bc6e594 | ||
|
|
0d95dc563c | ||
|
|
03bfc5f00b | ||
|
|
8708609498 | ||
|
|
dffc27c1f4 | ||
|
|
6f5f507323 | ||
|
|
26d6d817cd | ||
|
|
b68247f56d | ||
|
|
3b02a5f6aa | ||
|
|
09718a00e7 | ||
|
|
ecc2cd5f0c | ||
|
|
618580dc15 | ||
|
|
4860cec6c7 | ||
|
|
b9e0dbf150 | ||
|
|
2a4eb460da | ||
|
|
087c622122 | ||
|
|
2000de15e7 | ||
|
|
5e6b902430 | ||
|
|
a1f414a06d | ||
|
|
de3a39bdc2 | ||
|
|
6c5c9e5bf2 | ||
|
|
506873ed5b | ||
|
|
6a2bded181 | ||
|
|
ebf0e91ab8 | ||
|
|
107e96ff7d | ||
|
|
0d6e244f84 | ||
|
|
96a67579e2 | ||
|
|
4cba814716 | ||
|
|
b527059b72 | ||
|
|
c0507fcc55 | ||
|
|
38f2453c92 | ||
|
|
058bb65d31 | ||
|
|
38b215e9b5 | ||
|
|
45df33c9a5 | ||
|
|
89becaa191 | ||
|
|
6c87e4e039 | ||
|
|
cbe06d6b87 | ||
|
|
4fed80c2d0 | ||
|
|
74e4fd8b3f | ||
|
|
cc113a6c34 | ||
|
|
fca9876111 | ||
|
|
ac9f6a36f8 | ||
|
|
929b60e044 | ||
|
|
acb433dd4c | ||
|
|
c4e8bf7f39 | ||
|
|
ba4fdfb51c | ||
|
|
dfda916283 | ||
|
|
40326df9a9 | ||
|
|
a94444098a | ||
|
|
0c13e7a504 | ||
|
|
0dba57fe75 | ||
|
|
6483f896c1 | ||
|
|
8fa9943c3d | ||
|
|
195357835b | ||
|
|
28b2562112 | ||
|
|
ab24368de1 | ||
|
|
9b51404b4d | ||
|
|
619eaf284d | ||
|
|
86c348df58 | ||
|
|
fdb2ec897c | ||
|
|
b091fee554 | ||
|
|
14ec6ee793 | ||
|
|
0cfef0649a | ||
|
|
7292af7db4 | ||
|
|
bc1a197c4f | ||
|
|
ccc275902f | ||
|
|
0f5fb8aa3a | ||
|
|
83eca982e9 | ||
|
|
017f17a6b1 | ||
|
|
b3564e01a4 | ||
|
|
43105f71d8 | ||
|
|
40c408a9c0 | ||
|
|
7baff7c080 | ||
|
|
91a918d30b | ||
|
|
2a93fc18e4 | ||
|
|
31de6ee186 | ||
|
|
74d59673a8 | ||
|
|
d1c3d9b453 | ||
|
|
baa9241216 | ||
|
|
2c6c3ac6a4 | ||
|
|
b2c31331b6 | ||
|
|
9ff39c1fd9 | ||
|
|
210d57b254 | ||
|
|
7292cedd2f | ||
|
|
48d5afa799 | ||
|
|
3db22eea51 | ||
|
|
2fbb44ec66 | ||
|
|
d7b98080f5 | ||
|
|
3e7b41e712 | ||
|
|
5f18f35e02 | ||
|
|
af39e87703 | ||
|
|
ace25af0e8 | ||
|
|
8d78455414 | ||
|
|
fed16d7710 | ||
|
|
fcff3a2c8d | ||
|
|
803dd6f64e | ||
|
|
a1e258eb24 | ||
|
|
e9df698a1f | ||
|
|
bddcbf6999 | ||
|
|
be22e2551d | ||
|
|
4e01c7f13d | ||
|
|
7181813f12 | ||
|
|
6fc9f12c34 | ||
|
|
cc4e37fa40 | ||
|
|
ccadd4e41a | ||
|
|
a5caab62f8 | ||
|
|
2bff218806 | ||
|
|
95c30a2843 | ||
|
|
28efbcdb3d | ||
|
|
ca7952ab33 | ||
|
|
62693af196 | ||
|
|
5abee2512f | ||
|
|
c9a6155834 | ||
|
|
aad3ab903f | ||
|
|
4c76a898b6 | ||
|
|
d273148bc5 | ||
|
|
5314f5c67a | ||
|
|
22aab44d49 | ||
|
|
b819746165 | ||
|
|
73d33e2200 | ||
|
|
740bc3aa30 | ||
|
|
583b32f17a | ||
|
|
b7ab29473b | ||
|
|
ffc4f1408a | ||
|
|
c8b3066883 | ||
|
|
fb4f89b412 | ||
|
|
6bee6a0469 | ||
|
|
0c2ab9b231 | ||
|
|
03bef4c15d | ||
|
|
ba62ca7306 | ||
|
|
56a1530cee | ||
|
|
699cd5fc23 | ||
|
|
381c59d1e5 | ||
|
|
70f4dc9e10 | ||
|
|
0367ad31f1 | ||
|
|
5c6bcdbca6 | ||
|
|
400c0f05ad | ||
|
|
00df7c9b7b | ||
|
|
a272ed7daa | ||
|
|
b2e7a2e370 | ||
|
|
39bc70cbf0 | ||
|
|
682298b69f | ||
|
|
028a39ec25 | ||
|
|
3b104049b4 | ||
|
|
dd83a360b4 | ||
|
|
976c62d9c2 | ||
|
|
4cc9f12a03 | ||
|
|
0b5a751c56 | ||
|
|
5a59cd278c | ||
|
|
cdb05e5894 | ||
|
|
6c08b5caf3 | ||
|
|
d5b53f3712 | ||
|
|
80fbb2f0b2 | ||
|
|
b4f90decc7 | ||
|
|
61b324043a | ||
|
|
2ced921008 | ||
|
|
1e2fee0f33 | ||
|
|
ac36c9c406 | ||
|
|
f70c79aaf9 | ||
|
|
0839fa5757 | ||
|
|
60bc4ebcee | ||
|
|
65e239652d | ||
|
|
6b9015a928 | ||
|
|
a740c38ee6 | ||
|
|
603639126e | ||
|
|
043cfdb3f9 | ||
|
|
b0da4b2b2d | ||
|
|
5402859a30 | ||
|
|
2b10200101 | ||
|
|
55e8927ab7 | ||
|
|
d30fe61264 | ||
|
|
f7ea2d29e1 | ||
|
|
e6cfef5295 | ||
|
|
0dea9e6a2b | ||
|
|
ae80de26ec | ||
|
|
53bd2a0960 | ||
|
|
b55f5ff298 | ||
|
|
21048b51ae | ||
|
|
773fd09166 | ||
|
|
57532a290a | ||
|
|
740a35b018 | ||
|
|
6e995230a7 | ||
|
|
65a8b091b9 | ||
|
|
fa34908d82 | ||
|
|
b09eb1213c | ||
|
|
23ae8ab3b5 | ||
|
|
0bdb7e9e44 | ||
|
|
54e3cd7cb7 | ||
|
|
16ad07cfc9 | ||
|
|
dee0adcd8c | ||
|
|
acbf1226ab | ||
|
|
f002744d87 | ||
|
|
9efee7af21 | ||
|
|
9814b10496 | ||
|
|
978694b3c0 | ||
|
|
bc6f155f12 | ||
|
|
d494c2104b | ||
|
|
8035c6859f | ||
|
|
2f10295022 | ||
|
|
e5f32730a7 | ||
|
|
cd257b4d25 | ||
|
|
ce534baa75 | ||
|
|
2b420b8e66 | ||
|
|
35300abfb5 | ||
|
|
3d0ecc05ff | ||
|
|
bfb6ec8856 | ||
|
|
bf1d682a85 | ||
|
|
7d9326a147 | ||
|
|
7fd09009e7 | ||
|
|
9fa5e2dc1f | ||
|
|
15d4ee4d68 | ||
|
|
6ca6a52ae8 | ||
|
|
3d643aa4cb | ||
|
|
9739d23b6f | ||
|
|
c43acf7dac | ||
|
|
995c226893 | ||
|
|
8574bbb64f | ||
|
|
b2bb545947 | ||
|
|
bf8dae67de | ||
|
|
c6309da015 | ||
|
|
2e83a1010e | ||
|
|
d5efb31e3a | ||
|
|
b77798f885 | ||
|
|
5923c7c408 | ||
|
|
7dfb6fdc52 | ||
|
|
4e40b2ddf3 | ||
|
|
4e9abd41c0 | ||
|
|
7240b86d98 | ||
|
|
211853d519 | ||
|
|
f39589a8c1 | ||
|
|
e387971292 | ||
|
|
4fc1912ee8 | ||
|
|
31a704b675 | ||
|
|
c5c088300f | ||
|
|
ccea8e6eb5 | ||
|
|
ac60ed4559 | ||
|
|
8026263a27 | ||
|
|
6fa6abe6f0 | ||
|
|
e8d5bd7bfb | ||
|
|
2a03c1f1f4 | ||
|
|
0c6b92536e | ||
|
|
dddc1ec112 | ||
|
|
8b376874c0 | ||
|
|
0322660561 | ||
|
|
a7205ddf02 | ||
|
|
64b38487bc | ||
|
|
bc20850fba | ||
|
|
66048931a2 | ||
|
|
8444b6b3be | ||
|
|
baac26c5f6 | ||
|
|
56e9e15ac0 | ||
|
|
beb54acc2f | ||
|
|
d573423372 | ||
|
|
410bc211a5 | ||
|
|
e65c3f2f2b | ||
|
|
cf947415e8 | ||
|
|
c32a606a70 | ||
|
|
78c6059668 | ||
|
|
7c2adf636f | ||
|
|
d5ac8c7183 | ||
|
|
7a7fab0907 | ||
|
|
c758ae89f4 | ||
|
|
caea7910b4 | ||
|
|
6192685671 | ||
|
|
26eed30dbf | ||
|
|
20779dfece | ||
|
|
6bcfa436ea | ||
|
|
e62316105f | ||
|
|
9e318fc40d | ||
|
|
4b407d5213 | ||
|
|
841a22b2b2 | ||
|
|
840d8ab4fd | ||
|
|
2e3e58ed73 | ||
|
|
eb48f87667 | ||
|
|
a164c120d5 | ||
|
|
9dddd40dbe | ||
|
|
ad778636cf | ||
|
|
78360bc24a | ||
|
|
3480d9ac35 | ||
|
|
ae72bc3f99 | ||
|
|
c8bf36044b | ||
|
|
b05cb0d2ea | ||
|
|
fd1880f1ff | ||
|
|
70c5bd1875 | ||
|
|
00ac9ce961 | ||
|
|
1d455221e4 | ||
|
|
31b4eb7937 | ||
|
|
c88b46783b | ||
|
|
39edd22a9d | ||
|
|
afe25db0d3 | ||
|
|
f86a5af794 | ||
|
|
e6de4d014e | ||
|
|
b38df9eecd | ||
|
|
54abf84e96 | ||
|
|
66ebdfd61a | ||
|
|
4fe7c13d8e | ||
|
|
2578b380f9 | ||
|
|
cadf9f797f | ||
|
|
fd50e4e260 | ||
|
|
1cbec27b7f | ||
|
|
8d110c44f7 | ||
|
|
66a77db851 | ||
|
|
0505efbac1 | ||
|
|
99614f2e15 | ||
|
|
08e41244fc | ||
|
|
4e74b59796 | ||
|
|
2b6b268b4e | ||
|
|
09aacf011e | ||
|
|
9bea46adcb | ||
|
|
a10183628b | ||
|
|
7e0017d14a | ||
|
|
62e9a333c7 | ||
|
|
4838da48ef | ||
|
|
b5a43c416a | ||
|
|
c1297eadd4 | ||
|
|
06886fe6c2 | ||
|
|
137d1d181b | ||
|
|
97d0286a31 | ||
|
|
44e5b2c2fd | ||
|
|
f844a8be5e | ||
|
|
449d8aca65 | ||
|
|
9b29f8c23a | ||
|
|
5e79581662 | ||
|
|
79a2952f94 | ||
|
|
8077d8dfeb | ||
|
|
d72ac9694e | ||
|
|
5e0bc17341 | ||
|
|
932aa1fd39 | ||
|
|
2c45e9fedd | ||
|
|
c1352d50c1 | ||
|
|
2499d29196 | ||
|
|
fbf1cbc76e | ||
|
|
719838de3b | ||
|
|
ba66c108fa | ||
|
|
5287bf5cac | ||
|
|
10740f12c4 | ||
|
|
3e6edb92c5 | ||
|
|
dc7b9414d9 | ||
|
|
8df8cb1eb6 | ||
|
|
e14ea9a240 | ||
|
|
57287a5303 | ||
|
|
febc8f774d | ||
|
|
e51042ed04 | ||
|
|
910c123887 | ||
|
|
e0899abaac | ||
|
|
9df1bb2ff2 | ||
|
|
7b987635f7 | ||
|
|
8c37cf16c0 | ||
|
|
c569c10740 | ||
|
|
325541642e | ||
|
|
97d91ec0f8 | ||
|
|
17db90d5ab | ||
|
|
e7a7bb1a7b | ||
|
|
b621806a7c | ||
|
|
7720a3f091 | ||
|
|
b22b02a0b6 | ||
|
|
d14088512b | ||
|
|
62527a91ac | ||
|
|
f9e4a97eb8 | ||
|
|
ff45be1f71 | ||
|
|
aaed2ed790 | ||
|
|
d9501de252 | ||
|
|
9fdcb08364 | ||
|
|
49cf364352 | ||
|
|
b9a73cc23d | ||
|
|
357fd844d7 | ||
|
|
74a5522965 | ||
|
|
84dab18c2a | ||
|
|
9e90111b37 | ||
|
|
ffb1645f14 | ||
|
|
6448b217d5 | ||
|
|
1b84ab40ce | ||
|
|
af8d1ec6be | ||
|
|
04a4080768 | ||
|
|
942bf70582 | ||
|
|
b9f0ee2317 | ||
|
|
cb09bac246 | ||
|
|
109e4b2cf1 | ||
|
|
e4cf2978e6 | ||
|
|
17b74f20c9 | ||
|
|
ae11e31048 | ||
|
|
e455ce9c78 | ||
|
|
41df5cdd8b | ||
|
|
383225d7f4 | ||
|
|
a62c15ad1a | ||
|
|
5300742543 | ||
|
|
cd09a2f176 | ||
|
|
11ba46d140 | ||
|
|
e006377e64 | ||
|
|
786fa53334 | ||
|
|
9bc34e4406 | ||
|
|
c915af8dad | ||
|
|
98fbbc2864 | ||
|
|
d2049d63d6 | ||
|
|
63a12d4d40 | ||
|
|
a5f33bc244 | ||
|
|
1df6798934 | ||
|
|
d076aac421 | ||
|
|
e6d811ea7c |
@@ -1,8 +0,0 @@
|
|||||||
exclude:
|
|
||||||
- /Frameworks/.*
|
|
||||||
- /MQTTnet.TestApp.NetCore/Program.cs
|
|
||||||
- /Tests/MQTTnet.TestApp.NetFramework/.*
|
|
||||||
- /Tests/MQTTnet.TestApp.UniversalWindows/.*
|
|
||||||
component_depth: 1
|
|
||||||
languages:
|
|
||||||
- csharp
|
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
[*.cs]
|
||||||
|
|
||||||
|
|
||||||
|
# CA1031: Do not catch general exception types
|
||||||
|
|
||||||
|
# Microsoft .NET properties
|
||||||
|
csharp_new_line_before_members_in_object_initializers = false
|
||||||
|
csharp_preferred_modifier_order = public, private, protected, internal, new, abstract, virtual, sealed, override, static, readonly, extern, unsafe, volatile, async:suggestion
|
||||||
|
csharp_style_var_elsewhere = true:suggestion
|
||||||
|
csharp_style_var_for_built_in_types = true:suggestion
|
||||||
|
csharp_style_var_when_type_is_apparent = true:suggestion
|
||||||
|
dotnet_naming_rule.constants_rule.severity = warning
|
||||||
|
dotnet_naming_rule.constants_rule.style = upper_camel_case_style
|
||||||
|
dotnet_naming_rule.constants_rule.symbols = constants_symbols
|
||||||
|
dotnet_naming_rule.public_fields_rule.severity = warning
|
||||||
|
dotnet_naming_rule.public_fields_rule.style = lower_camel_case_style
|
||||||
|
dotnet_naming_rule.public_fields_rule.symbols = public_fields_symbols
|
||||||
|
dotnet_naming_rule.static_readonly_rule.severity = warning
|
||||||
|
dotnet_naming_rule.static_readonly_rule.style = upper_camel_case_style
|
||||||
|
dotnet_naming_rule.static_readonly_rule.symbols = static_readonly_symbols
|
||||||
|
dotnet_naming_style.lower_camel_case_style.capitalization = camel_case
|
||||||
|
dotnet_naming_style.lower_camel_case_style.required_prefix = _
|
||||||
|
dotnet_naming_style.upper_camel_case_style.capitalization = pascal_case
|
||||||
|
dotnet_naming_symbols.constants_symbols.applicable_accessibilities = public,internal,protected,protected_internal,private_protected
|
||||||
|
dotnet_naming_symbols.constants_symbols.applicable_kinds = field
|
||||||
|
dotnet_naming_symbols.constants_symbols.required_modifiers = const
|
||||||
|
dotnet_naming_symbols.public_fields_symbols.applicable_accessibilities = public,internal,protected,protected_internal,private_protected
|
||||||
|
dotnet_naming_symbols.public_fields_symbols.applicable_kinds = field
|
||||||
|
dotnet_naming_symbols.static_readonly_symbols.applicable_accessibilities = public,internal,protected,protected_internal,private_protected
|
||||||
|
dotnet_naming_symbols.static_readonly_symbols.applicable_kinds = field
|
||||||
|
dotnet_naming_symbols.static_readonly_symbols.required_modifiers = static,readonly
|
||||||
|
dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:none
|
||||||
|
dotnet_style_parentheses_in_other_binary_operators = never_if_unnecessary:none
|
||||||
|
dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:none
|
||||||
|
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
|
||||||
|
dotnet_style_predefined_type_for_member_access = true:suggestion
|
||||||
|
dotnet_style_qualification_for_event = false:suggestion
|
||||||
|
dotnet_style_qualification_for_field = false:suggestion
|
||||||
|
dotnet_style_qualification_for_method = false:suggestion
|
||||||
|
dotnet_style_qualification_for_property = false:suggestion
|
||||||
|
|
||||||
|
# ReSharper properties
|
||||||
|
resharper_autodetect_indent_settings = true
|
||||||
|
resharper_default_private_modifier = implicit
|
||||||
|
resharper_place_accessorholder_attribute_on_same_line = false
|
||||||
|
resharper_use_indent_from_vs = false
|
||||||
|
|
||||||
|
# ReSharper inspection severities
|
||||||
|
resharper_arrange_redundant_parentheses_highlighting = hint
|
||||||
|
resharper_arrange_this_qualifier_highlighting = hint
|
||||||
|
resharper_built_in_type_reference_style_for_member_access_highlighting = hint
|
||||||
|
resharper_built_in_type_reference_style_highlighting = hint
|
||||||
|
resharper_redundant_base_qualifier_highlighting = warning
|
||||||
|
resharper_suggest_var_or_type_built_in_types_highlighting = hint
|
||||||
|
resharper_suggest_var_or_type_elsewhere_highlighting = hint
|
||||||
|
resharper_suggest_var_or_type_simple_types_highlighting = hint
|
||||||
|
resharper_web_config_module_not_resolved_highlighting = warning
|
||||||
|
resharper_web_config_type_not_resolved_highlighting = warning
|
||||||
|
resharper_web_config_wrong_module_highlighting = warning
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
end_of_line = crlf
|
||||||
|
trim_trailing_whitespace = false
|
||||||
|
insert_final_newline = false
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
[{*.har,*.inputactions,*.jsb2,*.jsb3,*.json,.babelrc,.eslintrc,.stylelintrc,bowerrc,jest.config}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[{*.yaml,*.yml}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[{tsconfig.app.json,tsconfig.e2e.json,tsconfig.json,tsconfig.lib.json,tsconfig.spec.json}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[*.{appxmanifest,asax,ascx,aspx,axaml,build,cg,cginc,compute,cs,cshtml,dtd,hlsl,hlsli,hlslinc,master,nuspec,paml,razor,resw,resx,shader,skin,usf,ush,vb,xaml,xamlx,xoml,xsd}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
tab_width = 4
|
||||||
+1
-63
@@ -1,63 +1 @@
|
|||||||
###############################################################################
|
* text=auto
|
||||||
# Set default behavior to automatically normalize line endings.
|
|
||||||
###############################################################################
|
|
||||||
* text=auto
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# Set default behavior for command prompt diff.
|
|
||||||
#
|
|
||||||
# This is need for earlier builds of msysgit that does not have it on by
|
|
||||||
# default for csharp files.
|
|
||||||
# Note: This is only used by command line
|
|
||||||
###############################################################################
|
|
||||||
#*.cs diff=csharp
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# Set the merge driver for project and solution files
|
|
||||||
#
|
|
||||||
# Merging from the command prompt will add diff markers to the files if there
|
|
||||||
# are conflicts (Merging from VS is not affected by the settings below, in VS
|
|
||||||
# the diff markers are never inserted). Diff markers may cause the following
|
|
||||||
# file extensions to fail to load in VS. An alternative would be to treat
|
|
||||||
# these files as binary and thus will always conflict and require user
|
|
||||||
# intervention with every merge. To do so, just uncomment the entries below
|
|
||||||
###############################################################################
|
|
||||||
#*.sln merge=binary
|
|
||||||
#*.csproj merge=binary
|
|
||||||
#*.vbproj merge=binary
|
|
||||||
#*.vcxproj merge=binary
|
|
||||||
#*.vcproj merge=binary
|
|
||||||
#*.dbproj merge=binary
|
|
||||||
#*.fsproj merge=binary
|
|
||||||
#*.lsproj merge=binary
|
|
||||||
#*.wixproj merge=binary
|
|
||||||
#*.modelproj merge=binary
|
|
||||||
#*.sqlproj merge=binary
|
|
||||||
#*.wwaproj merge=binary
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# behavior for image files
|
|
||||||
#
|
|
||||||
# image files are treated as binary by default.
|
|
||||||
###############################################################################
|
|
||||||
#*.jpg binary
|
|
||||||
#*.png binary
|
|
||||||
#*.gif binary
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# diff behavior for common document formats
|
|
||||||
#
|
|
||||||
# Convert binary document formats to text before diffing them. This feature
|
|
||||||
# is only available from the command line. Turn it on by uncommenting the
|
|
||||||
# entries below.
|
|
||||||
###############################################################################
|
|
||||||
#*.doc diff=astextplain
|
|
||||||
#*.DOC diff=astextplain
|
|
||||||
#*.docx diff=astextplain
|
|
||||||
#*.DOCX diff=astextplain
|
|
||||||
#*.dot diff=astextplain
|
|
||||||
#*.DOT diff=astextplain
|
|
||||||
#*.pdf diff=astextplain
|
|
||||||
#*.PDF diff=astextplain
|
|
||||||
#*.rtf diff=astextplain
|
|
||||||
#*.RTF diff=astextplain
|
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
---
|
||||||
|
name: Bug report
|
||||||
|
about: Create a report to help us improve.
|
||||||
|
title: ''
|
||||||
|
labels: 'bug'
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Verification
|
||||||
|
|
||||||
|
Before opening a bug make sure that the following conditions are met.
|
||||||
|
<!-- Remove this chapter when all conditions are met. -->
|
||||||
|
|
||||||
|
1. Performance issues are also appearing in RELEASE mode of your application.
|
||||||
|
2. An increased memory consumption or high CPU load also happens when your application runs in RELEASE mode AND logging is DISABLED.
|
||||||
|
3. Client issues with not received messages, connection drops etc. can be reproduced via another client application like "MQTTnetApp" (https://github.com/chkr1011/MQTTnetApp) or similar.
|
||||||
|
4. The bug also appears in the VERY LATEST version of this library. There is no support for older version (due to limited resources) but pull requests for older versions are welcome.
|
||||||
|
|
||||||
|
### Describe the bug
|
||||||
|
A clear and concise description of what the bug is.
|
||||||
|
|
||||||
|
### Which component is your bug related to?
|
||||||
|
<!-- Remove the items which don't apply from the following list. -->
|
||||||
|
- Client
|
||||||
|
- ManagedClient
|
||||||
|
- RpcClient
|
||||||
|
- Server
|
||||||
|
|
||||||
|
### To Reproduce
|
||||||
|
Steps to reproduce the behavior:
|
||||||
|
1. Using this version of MQTTnet '...'.
|
||||||
|
2. Run this code '....'.
|
||||||
|
3. With these arguments '....'.
|
||||||
|
4. See error.
|
||||||
|
|
||||||
|
### Expected behavior
|
||||||
|
A clear and concise description of what you expected to happen.
|
||||||
|
|
||||||
|
### Screenshots
|
||||||
|
If applicable, add screenshots to help explain your problem.
|
||||||
|
|
||||||
|
### Additional context / logging
|
||||||
|
Add any other context about the problem here.
|
||||||
|
Include debugging or logging information here:
|
||||||
|
|
||||||
|
```batch
|
||||||
|
\\ Put your logging output here.
|
||||||
|
```
|
||||||
|
### Code example
|
||||||
|
Please provide full code examples below where possible to make it easier for the developers to check your issues.
|
||||||
|
|
||||||
|
**Ideally a Unit Test (which shows the error) is provided so that the behavior can be reproduced easily.**
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
\\ Put your code here.
|
||||||
|
```
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
---
|
||||||
|
name: Custom issue template
|
||||||
|
about: Do you have a question related to the project? Use this template.
|
||||||
|
title: ''
|
||||||
|
labels: 'question'
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Describe your question
|
||||||
|
A clear and concise description of what you want to know.
|
||||||
|
|
||||||
|
### Which project is your question related to?
|
||||||
|
<!-- Remove the items which don't apply from the following list -->
|
||||||
|
- Client
|
||||||
|
- ManagedClient
|
||||||
|
- Server
|
||||||
|
- Generic
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
---
|
||||||
|
name: Feature request
|
||||||
|
about: Suggest an idea for this project.
|
||||||
|
title: ''
|
||||||
|
labels: 'feature-request'
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Describe the feature request
|
||||||
|
Is your feature request related to a problem? Please describe.
|
||||||
|
A clear and concise description of what the problem is. Example. I'm am trying to do [...] but [...]
|
||||||
|
|
||||||
|
### Which project is your feature request related to?
|
||||||
|
<!-- Remove the items which don't apply from the following list -->
|
||||||
|
- Client
|
||||||
|
- ManagedClient
|
||||||
|
- Server
|
||||||
|
- Generic
|
||||||
|
|
||||||
|
### Describe the solution you'd like
|
||||||
|
A clear and concise description of what you want to happen.
|
||||||
|
|
||||||
|
### Describe alternatives you've considered
|
||||||
|
A clear and concise description of any alternative solutions or features you've considered.
|
||||||
|
|
||||||
|
### Additional context
|
||||||
|
Add any other context or screenshots about the feature request here.
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
* [Core] Fixed several dead locks by implementing a new async signal (#1552)
|
||||||
|
* [Client] All waiting tasks will now fail when the client connection gets closed. This fixes several freezes (#1561)
|
||||||
|
* [ManagedClient] The topic is no longer declared as invalid if a topic alias is used (#1395)
|
||||||
|
* [Server] Fixed duplicated invocation of the event _ClientAcknowledgedPublishPacketAsync_ for QoS level 2 (#1550)
|
||||||
|
* [Server] Fixed issue in upgrading and downgrading of QoS levels for subscriptions and retained messages (#1560)
|
||||||
|
* [Server] Fixed memory leak when old sessions are discarded (#1553)
|
||||||
|
* [Server] When using MQTTv5 the reason code _NoMatchingSubscribers_ is returned when no client is subscribed to a published topic (#802)
|
||||||
|
* [Server] Fix sending of duplicate messages with QoS > 0 when session is recovered and session is reused (#1546)
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
name: CI
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
env:
|
||||||
|
VERSION: "4.1.3.${{github.run_number}}"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
|
||||||
|
runs-on: windows-2022
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Setup Windows SDK
|
||||||
|
uses: GuillaumeFalourd/setup-windows10-sdk-action@v1
|
||||||
|
with:
|
||||||
|
sdk-version: 18362
|
||||||
|
|
||||||
|
- name: Setup .NET SDK
|
||||||
|
uses: actions/setup-dotnet@v1.9.0
|
||||||
|
with:
|
||||||
|
dotnet-version: |
|
||||||
|
3.1.x
|
||||||
|
6.0.x
|
||||||
|
|
||||||
|
- name: Setup MSBuild
|
||||||
|
uses: microsoft/setup-msbuild@v1.1
|
||||||
|
|
||||||
|
- name: Checkout Code
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Setup Signing Certificate
|
||||||
|
run: |
|
||||||
|
$secret = '${{ secrets.SNC_BASE64 }}'
|
||||||
|
$decoded = [System.Convert]::FromBase64CharArray($secret, 0, $secret.Length)
|
||||||
|
Set-Content -Path ${{ github.workspace }}\certificate.snk -Value $decoded -AsByteStream
|
||||||
|
|
||||||
|
- name: Restore nuget packages
|
||||||
|
run: msbuild MQTTnet.sln /t:Restore /p:Configuration="Release" /verbosity:m
|
||||||
|
|
||||||
|
- name: Build solution
|
||||||
|
run: msbuild MQTTnet.sln /t:Build /p:Configuration="Release" /verbosity:m /p:FileVersion=${{ env.VERSION }} /p:AssemblyVersion=${{ env.VERSION }} /p:PackageVersion=${{ env.VERSION }} /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=${{ github.workspace }}\certificate.snk
|
||||||
|
|
||||||
|
- name: Collect nuget Packages
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: nuget Packages
|
||||||
|
path: |
|
||||||
|
**\*.nupkg
|
||||||
|
**\*.snupkg
|
||||||
|
|
||||||
|
- name: Setup VSTest
|
||||||
|
uses: darenm/Setup-VSTest@v1
|
||||||
|
|
||||||
|
- name: Core Tests
|
||||||
|
run: vstest.console.exe Source\MQTTnet.Tests\bin\Release\net6.0\MQTTnet.Tests.dll
|
||||||
|
|
||||||
|
- name: ASP.NET Tests
|
||||||
|
run: vstest.console.exe Source\MQTTnet.AspNetCore.Tests\bin\Release\netcoreapp3.1\MQTTnet.AspNetCore.Tests.dll
|
||||||
|
|
||||||
|
- name: Publish MyGet nugets
|
||||||
|
if: ${{ github.event_name == 'push' }}
|
||||||
|
run: dotnet nuget push **/*.nupkg -k ${{ secrets.MYGET_API_KEY }} -s https://www.myget.org/F/mqttnet/api/v3/index.json --skip-duplicate
|
||||||
|
|
||||||
|
# - name: Publish nugets
|
||||||
|
# if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
|
||||||
|
# run: dotnet nuget push **/*.nupkg -k ${{ secrets.NUGET_API_KEY }} -s https://api.nuget.org/v3/index.json --skip-duplicate
|
||||||
+5
-2
@@ -235,6 +235,7 @@ FakesAssemblies/
|
|||||||
# Node.js Tools for Visual Studio
|
# Node.js Tools for Visual Studio
|
||||||
.ntvs_analysis.dat
|
.ntvs_analysis.dat
|
||||||
node_modules/
|
node_modules/
|
||||||
|
/*/**/package-lock.json
|
||||||
|
|
||||||
# Typescript v1 declaration files
|
# Typescript v1 declaration files
|
||||||
typings/
|
typings/
|
||||||
@@ -287,9 +288,11 @@ __pycache__/
|
|||||||
*.odx.cs
|
*.odx.cs
|
||||||
*.xsd.cs
|
*.xsd.cs
|
||||||
|
|
||||||
|
|
||||||
Build/nuget.exe
|
|
||||||
*.js
|
*.js
|
||||||
*.map
|
*.map
|
||||||
|
|
||||||
/Tests/MQTTnet.TestApp.NetCore/RetainedMessages.json
|
/Tests/MQTTnet.TestApp.NetCore/RetainedMessages.json
|
||||||
|
|
||||||
|
Build/NuGet/
|
||||||
|
|
||||||
|
Build/nuget.exe
|
||||||
|
|||||||
-10
@@ -1,10 +0,0 @@
|
|||||||
language: csharp
|
|
||||||
solution: MQTTnet.sln
|
|
||||||
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- dotnet: 2.0.0
|
|
||||||
mono: none
|
|
||||||
dist: trusty
|
|
||||||
env: DOTNETCORE=1 # optional, can be used to take different code paths in your script
|
|
||||||
- mono: latest
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
<?xml version="1.0"?>
|
|
||||||
<package >
|
|
||||||
<metadata>
|
|
||||||
<id>MQTTnet.AspNetCore</id>
|
|
||||||
<version>2.7.5</version>
|
|
||||||
<authors>Christian Kratky</authors>
|
|
||||||
<owners>Christian Kratky</owners>
|
|
||||||
<licenseUrl>https://github.com/chkr1011/MQTTnet/blob/master/LICENSE</licenseUrl>
|
|
||||||
<projectUrl>https://github.com/chkr1011/MQTTnet</projectUrl>
|
|
||||||
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
|
|
||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
|
||||||
<description>This is a support library to integrate MQTTnet into AspNetCore.</description>
|
|
||||||
<releaseNotes>* Updated to MQTTnet 2.7.5.
|
|
||||||
</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>
|
|
||||||
<group targetFramework="netstandard2.0">
|
|
||||||
<dependency id="MQTTnet" version="2.7.5" />
|
|
||||||
</group>
|
|
||||||
</dependencies>
|
|
||||||
</metadata>
|
|
||||||
|
|
||||||
<files>
|
|
||||||
<!-- .NET Standard 2.0 -->
|
|
||||||
<file src="..\Frameworks\MQTTnet.AspNetCore\bin\Release\netstandard2.0\MQTTnet.AspNetCore.*" target="lib\netstandard2.0\"/>
|
|
||||||
</files>
|
|
||||||
</package>
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
<?xml version="1.0"?>
|
|
||||||
<package >
|
|
||||||
<metadata>
|
|
||||||
<id>MQTTnet.Extensions.Rpc</id>
|
|
||||||
<version>2.7.5</version>
|
|
||||||
<authors>Christian Kratky</authors>
|
|
||||||
<owners>Christian Kratky</owners>
|
|
||||||
<licenseUrl>https://github.com/chkr1011/MQTTnet/blob/master/LICENSE</licenseUrl>
|
|
||||||
<projectUrl>https://github.com/chkr1011/MQTTnet</projectUrl>
|
|
||||||
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
|
|
||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
|
||||||
<description>This is a extension library which allows executing synchronous device calls including a response using MQTTnet.</description>
|
|
||||||
<releaseNotes>* Updated to MQTTnet 2.7.5.
|
|
||||||
</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>
|
|
||||||
<dependencies>
|
|
||||||
<group targetFramework="netstandard2.0">
|
|
||||||
<dependency id="MQTTnet" version="2.7.5" />
|
|
||||||
</group>
|
|
||||||
<group targetFramework="netstandard1.3">
|
|
||||||
<dependency id="MQTTnet" version="2.7.5" />
|
|
||||||
</group>
|
|
||||||
|
|
||||||
<group targetFramework="netstandard2.0">
|
|
||||||
<dependency id="MQTTnet" version="2.7.5" />
|
|
||||||
</group>
|
|
||||||
|
|
||||||
<group targetFramework="uap10.0">
|
|
||||||
<dependency id="MQTTnet" version="2.7.5" />
|
|
||||||
</group>
|
|
||||||
|
|
||||||
<group targetFramework="net452">
|
|
||||||
<dependency id="MQTTnet" version="2.7.5" />
|
|
||||||
</group>
|
|
||||||
|
|
||||||
<group targetFramework="net461">
|
|
||||||
<dependency id="MQTTnet" version="2.7.5" />
|
|
||||||
</group>
|
|
||||||
</dependencies>
|
|
||||||
</metadata>
|
|
||||||
|
|
||||||
<files>
|
|
||||||
<!-- .NET Standard 2.0 -->
|
|
||||||
<file src="..\Frameworks\MQTTnet.AspNetCore\bin\Release\netstandard2.0\MQTTnet.AspNetCore.*" target="lib\netstandard2.0\"/>
|
|
||||||
</files>
|
|
||||||
</package>
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
<?xml version="1.0"?>
|
|
||||||
<package >
|
|
||||||
<metadata>
|
|
||||||
<id>MQTTnet</id>
|
|
||||||
<version>2.7.5</version>
|
|
||||||
<authors>Christian Kratky</authors>
|
|
||||||
<owners>Christian Kratky</owners>
|
|
||||||
<licenseUrl>https://github.com/chkr1011/MQTTnet/blob/master/LICENSE</licenseUrl>
|
|
||||||
<projectUrl>https://github.com/chkr1011/MQTTnet</projectUrl>
|
|
||||||
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
|
|
||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
|
||||||
<description>MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker).</description>
|
|
||||||
<releaseNotes> * [Client] Fixed a deadlock while the client disconnects.
|
|
||||||
* [Client] Fixed broken support for protocol version 3.1.0.
|
|
||||||
* [Server] The _MqttTcpServerAdapter_ is now added to the ASP.NET services.
|
|
||||||
* [Server] _MqttServerAdapter_ is renamed to _MqttTcpServerAdapter_ (BREAKING CHANGE!).
|
|
||||||
* [Server] The server no longer sends the will message of a client if the disconnect was clean (via _Disconnect_ packet).
|
|
||||||
* [Server] The application message interceptor now allows closing the connection.
|
|
||||||
* [Server] Added a new flag for the _ClientDisconnected_ event which contains a value indicating whether the disconnect was clean (via _Disconnect_ packet).
|
|
||||||
</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>
|
|
||||||
|
|
||||||
<group targetFramework="netstandard1.3">
|
|
||||||
<dependency id="NETStandard.Library" version="1.3.0" />
|
|
||||||
<dependency id="System.Net.Security" version="4.3.2" />
|
|
||||||
<dependency id="System.Net.WebSockets" version="4.3.0" />
|
|
||||||
<dependency id="System.Net.WebSockets.Client" version="4.3.1" />
|
|
||||||
</group>
|
|
||||||
|
|
||||||
<group targetFramework="netstandard2.0">
|
|
||||||
<dependency id="NETStandard.Library" version="2.0.0" />
|
|
||||||
<dependency id="System.Net.Security" version="4.3.2" />
|
|
||||||
<dependency id="System.Net.WebSockets" version="4.3.0" />
|
|
||||||
<dependency id="System.Net.WebSockets.Client" version="4.3.1" />
|
|
||||||
</group>
|
|
||||||
|
|
||||||
<group targetFramework="uap10.0">
|
|
||||||
<dependency id="Microsoft.NETCore.UniversalWindowsPlatform" version="5.4.1" />
|
|
||||||
</group>
|
|
||||||
|
|
||||||
<group targetFramework="net452">
|
|
||||||
</group>
|
|
||||||
|
|
||||||
<group targetFramework="net461">
|
|
||||||
</group>
|
|
||||||
|
|
||||||
</dependencies>
|
|
||||||
</metadata>
|
|
||||||
|
|
||||||
<files>
|
|
||||||
|
|
||||||
<!-- .NET Standard 1.3 -->
|
|
||||||
<file src="..\Frameworks\MQTTnet.Netstandard\bin\Release\netstandard1.3\MQTTnet.*" target="lib\netstandard1.3\"/>
|
|
||||||
|
|
||||||
<!-- .NET Standard 2.0 -->
|
|
||||||
<file src="..\Frameworks\MQTTnet.Netstandard\bin\Release\netstandard2.0\MQTTnet.*" target="lib\netstandard2.0\"/>
|
|
||||||
|
|
||||||
<!-- Universal Windows -->
|
|
||||||
<file src="..\Frameworks\MQTTnet.Netstandard\bin\Release\uap10.0\MQTTnet.*" target="lib\uap10.0\"/>
|
|
||||||
|
|
||||||
<!-- .NET Framework -->
|
|
||||||
<file src="..\Frameworks\MQTTnet.Netstandard\bin\Release\net452\MQTTnet.*" target="lib\net452\"/>
|
|
||||||
<file src="..\Frameworks\MQTTnet.Netstandard\bin\Release\net461\MQTTnet.*" target="lib\net461\"/>
|
|
||||||
|
|
||||||
</files>
|
|
||||||
</package>
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
param([string]$assemblyVersion, [string]$nugetVersion)
|
|
||||||
|
|
||||||
if ([string]::IsNullOrEmpty($assemblyVersion)) {$assemblyVersion = "0.0.1"}
|
|
||||||
if ([string]::IsNullOrEmpty($nugetVersion)) {$nugetVersion = "0.0.1"}
|
|
||||||
|
|
||||||
$vswhere = ${Env:\ProgramFiles(x86)} + '\Microsoft Visual Studio\Installer\vswhere'
|
|
||||||
|
|
||||||
$path = &$vswhere -latest -products * -requires Microsoft.Component.MSBuild -property installationPath
|
|
||||||
if ($path) {
|
|
||||||
$msbuild = join-path $path 'MSBuild\15.0\Bin\MSBuild.exe'
|
|
||||||
|
|
||||||
# Build the core library
|
|
||||||
&$msbuild ..\Frameworks\MQTTnet.Netstandard\MQTTnet.Netstandard.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 ..\Frameworks\MQTTnet.Netstandard\MQTTnet.Netstandard.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 ..\Frameworks\MQTTnet.Netstandard\MQTTnet.Netstandard.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 ..\Frameworks\MQTTnet.Netstandard\MQTTnet.Netstandard.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 ..\Frameworks\MQTTnet.Netstandard\MQTTnet.Netstandard.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="uap10.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
|
||||||
|
|
||||||
# Build the ASP.NET Core 2.0 extension
|
|
||||||
&$msbuild ..\Frameworks\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 ..\Extensions\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net452" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m
|
|
||||||
&$msbuild ..\Extensions\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net461" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m
|
|
||||||
&$msbuild ..\Extensions\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard1.3" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m
|
|
||||||
&$msbuild ..\Extensions\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m
|
|
||||||
&$msbuild ..\Extensions\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="uap10.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m
|
|
||||||
|
|
||||||
Remove-Item .\NuGet -Force -Recurse -ErrorAction SilentlyContinue
|
|
||||||
|
|
||||||
New-Item -ItemType Directory -Force -Path .\NuGet
|
|
||||||
.\NuGet.exe pack MQTTnet.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $nugetVersion
|
|
||||||
.\NuGet.exe pack MQTTnet.AspNetCore.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $nugetVersion
|
|
||||||
.\NuGet.exe pack MQTTnet.Extensions.Rpc.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $nugetVersion
|
|
||||||
}
|
|
||||||
Binary file not shown.
@@ -0,0 +1,6 @@
|
|||||||
|
# Code of Conduct
|
||||||
|
|
||||||
|
This project has adopted the code of conduct defined by the Contributor Covenant
|
||||||
|
to clarify expected behavior in our community.
|
||||||
|
|
||||||
|
For more information, see the [.NET Foundation Code of Conduct](https://dotnetfoundation.org/code-of-conduct).
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
# 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,31 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFrameworks>netstandard1.3;netstandard2.0;net452;net461;uap10.0</TargetFrameworks>
|
|
||||||
<AssemblyVersion>0.0.0.0</AssemblyVersion>
|
|
||||||
<FileVersion>0.0.0.0</FileVersion>
|
|
||||||
<Product />
|
|
||||||
<Company />
|
|
||||||
<Authors />
|
|
||||||
<PackageId />
|
|
||||||
<Version>0.0.0.0</Version>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(TargetFramework)' == 'uap10.0'">
|
|
||||||
<CopyLocalLockFileAssemblies>false</CopyLocalLockFileAssemblies>
|
|
||||||
<NugetTargetMoniker>UAP,Version=v10.0</NugetTargetMoniker>
|
|
||||||
<TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
|
|
||||||
<TargetPlatformVersion>10.0.16299.0</TargetPlatformVersion>
|
|
||||||
<TargetPlatformMinVersion>10.0.10240.0</TargetPlatformMinVersion>
|
|
||||||
<TargetFrameworkIdentifier>.NETCore</TargetFrameworkIdentifier>
|
|
||||||
<TargetFrameworkVersion>v5.0</TargetFrameworkVersion>
|
|
||||||
<DefineConstants>$(DefineConstants);WINDOWS_UWP</DefineConstants>
|
|
||||||
<DefaultLanguage>en</DefaultLanguage>
|
|
||||||
<LanguageTargets>$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets</LanguageTargets>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\..\Frameworks\MQTTnet.Netstandard\MQTTnet.NetStandard.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using MQTTnet.Client;
|
|
||||||
using MQTTnet.Internal;
|
|
||||||
using MQTTnet.Protocol;
|
|
||||||
|
|
||||||
namespace MQTTnet.Extensions.Rpc
|
|
||||||
{
|
|
||||||
public sealed class MqttRpcClient : IDisposable
|
|
||||||
{
|
|
||||||
private const string ResponseTopic = "$MQTTnet.RPC/+/+/response";
|
|
||||||
private readonly ConcurrentDictionary<string, TaskCompletionSource<byte[]>> _waitingCalls = new ConcurrentDictionary<string, TaskCompletionSource<byte[]>>();
|
|
||||||
private readonly IMqttClient _mqttClient;
|
|
||||||
private bool _isEnabled;
|
|
||||||
|
|
||||||
public MqttRpcClient(IMqttClient mqttClient)
|
|
||||||
{
|
|
||||||
_mqttClient = mqttClient ?? throw new ArgumentNullException(nameof(mqttClient));
|
|
||||||
|
|
||||||
_mqttClient.ApplicationMessageReceived += OnApplicationMessageReceived;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task EnableAsync()
|
|
||||||
{
|
|
||||||
await _mqttClient.SubscribeAsync(new TopicFilterBuilder().WithTopic(ResponseTopic).WithAtLeastOnceQoS().Build());
|
|
||||||
_isEnabled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task DisableAsync()
|
|
||||||
{
|
|
||||||
await _mqttClient.UnsubscribeAsync(ResponseTopic);
|
|
||||||
_isEnabled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<byte[]> ExecuteAsync(TimeSpan timeout, string methodName, byte[] payload, MqttQualityOfServiceLevel qualityOfServiceLevel)
|
|
||||||
{
|
|
||||||
if (methodName == null) throw new ArgumentNullException(nameof(methodName));
|
|
||||||
|
|
||||||
if (methodName.Contains("/") || methodName.Contains("+") || methodName.Contains("#"))
|
|
||||||
{
|
|
||||||
throw new ArgumentException("The method name cannot contain /, + or #.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_isEnabled)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("The RPC client is not enabled.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var requestTopic = $"$MQTTnet.RPC/{Guid.NewGuid():N}/{methodName}";
|
|
||||||
var responseTopic = requestTopic + "/response";
|
|
||||||
|
|
||||||
var requestMessage = new MqttApplicationMessageBuilder()
|
|
||||||
.WithTopic(requestTopic)
|
|
||||||
.WithPayload(payload)
|
|
||||||
.WithQualityOfServiceLevel(qualityOfServiceLevel)
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var tcs = new TaskCompletionSource<byte[]>();
|
|
||||||
if (!_waitingCalls.TryAdd(responseTopic, tcs))
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
await _mqttClient.PublishAsync(requestMessage);
|
|
||||||
return await tcs.Task.TimeoutAfter(timeout);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_waitingCalls.TryRemove(responseTopic, out _);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnApplicationMessageReceived(object sender, MqttApplicationMessageReceivedEventArgs eventArgs)
|
|
||||||
{
|
|
||||||
if (!_waitingCalls.TryRemove(eventArgs.ApplicationMessage.Topic, out TaskCompletionSource<byte[]> tcs))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tcs.Task.IsCompleted || tcs.Task.IsCanceled)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
tcs.TrySetResult(eventArgs.ApplicationMessage.Payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
foreach (var tcs in _waitingCalls)
|
|
||||||
{
|
|
||||||
tcs.Value.SetCanceled();
|
|
||||||
}
|
|
||||||
|
|
||||||
_waitingCalls.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Microsoft.AspNetCore.Builder;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using System.Linq;
|
|
||||||
using MQTTnet.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)
|
|
||||||
{
|
|
||||||
string subprotocol = null;
|
|
||||||
|
|
||||||
if (context.Request.Headers.TryGetValue("Sec-WebSocket-Protocol", out var requestedSubProtocolValues)
|
|
||||||
&& requestedSubProtocolValues.Count > 0
|
|
||||||
&& requestedSubProtocolValues.Any(v => v.ToLower() == "mqtt")
|
|
||||||
)
|
|
||||||
{
|
|
||||||
subprotocol = "mqtt";
|
|
||||||
}
|
|
||||||
|
|
||||||
var adapter = app.ApplicationServices.GetRequiredService<MqttWebSocketServerAdapter>();
|
|
||||||
using (var webSocket = await context.WebSockets.AcceptWebSocketAsync(subprotocol))
|
|
||||||
{
|
|
||||||
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,28 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>netstandard2.0</TargetFramework>
|
|
||||||
<AssemblyVersion>0.0.0.0</AssemblyVersion>
|
|
||||||
<FileVersion>0.0.0.0</FileVersion>
|
|
||||||
<Version>0.0.0.0</Version>
|
|
||||||
<Product />
|
|
||||||
<Company />
|
|
||||||
<Authors />
|
|
||||||
<PackageId />
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
|
||||||
<DefineConstants>RELEASE;NETSTANDARD2_0</DefineConstants>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.0.1" />
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.0.1" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="2.0.1" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\MQTTnet.Netstandard\MQTTnet.NetStandard.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
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)
|
|
||||||
{
|
|
||||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task StartAsync(CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
return StartAsync(_options);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task StopAsync(CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
return StopAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Net.WebSockets;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using MQTTnet.Adapter;
|
|
||||||
using MQTTnet.Diagnostics;
|
|
||||||
using MQTTnet.Serializer;
|
|
||||||
using MQTTnet.Server;
|
|
||||||
|
|
||||||
namespace MQTTnet.AspNetCore
|
|
||||||
{
|
|
||||||
public sealed class MqttWebSocketServerAdapter : IMqttServerAdapter, IDisposable
|
|
||||||
{
|
|
||||||
public event EventHandler<MqttServerAdapterClientAcceptedEventArgs> ClientAccepted;
|
|
||||||
|
|
||||||
public Task StartAsync(IMqttServerOptions 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 = new MqttChannelAdapter(channel, new MqttPacketSerializer(), new MqttNetLogger());
|
|
||||||
|
|
||||||
var eventArgs = new MqttServerAdapterClientAcceptedEventArgs(clientAdapter);
|
|
||||||
ClientAccepted?.Invoke(this, eventArgs);
|
|
||||||
return eventArgs.SessionTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
StopAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Net.WebSockets;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using MQTTnet.Channel;
|
|
||||||
using MQTTnet.Implementations;
|
|
||||||
|
|
||||||
namespace MQTTnet.AspNetCore
|
|
||||||
{
|
|
||||||
public class MqttWebSocketServerChannel : IMqttChannel, IDisposable
|
|
||||||
{
|
|
||||||
private WebSocket _webSocket;
|
|
||||||
|
|
||||||
public MqttWebSocketServerChannel(WebSocket webSocket)
|
|
||||||
{
|
|
||||||
_webSocket = webSocket ?? throw new ArgumentNullException(nameof(webSocket));
|
|
||||||
|
|
||||||
SendStream = new WebSocketStream(_webSocket);
|
|
||||||
ReceiveStream = SendStream;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Stream SendStream { get; private set; }
|
|
||||||
public Stream ReceiveStream { get; private set; }
|
|
||||||
|
|
||||||
public Task ConnectAsync()
|
|
||||||
{
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task DisconnectAsync()
|
|
||||||
{
|
|
||||||
if (_webSocket == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
SendStream?.Dispose();
|
|
||||||
ReceiveStream?.Dispose();
|
|
||||||
|
|
||||||
_webSocket?.Dispose();
|
|
||||||
|
|
||||||
SendStream = null;
|
|
||||||
ReceiveStream = null;
|
|
||||||
_webSocket = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Microsoft.Extensions.Hosting;
|
|
||||||
using MQTTnet.Adapter;
|
|
||||||
using MQTTnet.Diagnostics;
|
|
||||||
using MQTTnet.Server;
|
|
||||||
using MQTTnet.Implementations;
|
|
||||||
|
|
||||||
namespace MQTTnet.AspNetCore
|
|
||||||
{
|
|
||||||
public static class ServiceCollectionExtensions
|
|
||||||
{
|
|
||||||
public static IServiceCollection AddHostedMqttServer(this IServiceCollection services, IMqttServerOptions options)
|
|
||||||
{
|
|
||||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
|
||||||
|
|
||||||
services.AddSingleton(options);
|
|
||||||
services.AddSingleton<IMqttNetLogger>(new MqttNetLogger());
|
|
||||||
services.AddSingleton<MqttHostedServer>();
|
|
||||||
services.AddSingleton<IHostedService>(s => s.GetService<MqttHostedServer>());
|
|
||||||
services.AddSingleton<IMqttServer>(s => s.GetService<MqttHostedServer>());
|
|
||||||
|
|
||||||
services.AddSingleton<MqttWebSocketServerAdapter>();
|
|
||||||
services.AddSingleton<MqttTcpServerAdapter>();
|
|
||||||
services.AddSingleton<IMqttServerAdapter>(s => s.GetService<MqttWebSocketServerAdapter>());
|
|
||||||
services.AddSingleton<IMqttServerAdapter>(s => s.GetService<MqttTcpServerAdapter>());
|
|
||||||
|
|
||||||
return services;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using MQTTnet.Packets;
|
|
||||||
using MQTTnet.Serializer;
|
|
||||||
|
|
||||||
namespace MQTTnet.Adapter
|
|
||||||
{
|
|
||||||
public interface IMqttChannelAdapter : IDisposable
|
|
||||||
{
|
|
||||||
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,14 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using MQTTnet.Server;
|
|
||||||
|
|
||||||
namespace MQTTnet.Adapter
|
|
||||||
{
|
|
||||||
public interface IMqttServerAdapter
|
|
||||||
{
|
|
||||||
event EventHandler<MqttServerAdapterClientAcceptedEventArgs> ClientAccepted;
|
|
||||||
|
|
||||||
Task StartAsync(IMqttServerOptions options);
|
|
||||||
Task StopAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,255 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Net.Sockets;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using MQTTnet.Channel;
|
|
||||||
using MQTTnet.Diagnostics;
|
|
||||||
using MQTTnet.Exceptions;
|
|
||||||
using MQTTnet.Internal;
|
|
||||||
using MQTTnet.Packets;
|
|
||||||
using MQTTnet.Serializer;
|
|
||||||
|
|
||||||
namespace MQTTnet.Adapter
|
|
||||||
{
|
|
||||||
public sealed class MqttChannelAdapter : IMqttChannelAdapter
|
|
||||||
{
|
|
||||||
private const uint ErrorOperationAborted = 0x800703E3;
|
|
||||||
private const int ReadBufferSize = 4096; // TODO: Move buffer size to config
|
|
||||||
|
|
||||||
private bool _isDisposed;
|
|
||||||
private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
|
|
||||||
private readonly IMqttNetLogger _logger;
|
|
||||||
private readonly IMqttChannel _channel;
|
|
||||||
|
|
||||||
public MqttChannelAdapter(IMqttChannel channel, IMqttPacketSerializer serializer, IMqttNetLogger 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 Task ConnectAsync(TimeSpan timeout)
|
|
||||||
{
|
|
||||||
ThrowIfDisposed();
|
|
||||||
_logger.Verbose<MqttChannelAdapter>("Connecting [Timeout={0}]", timeout);
|
|
||||||
|
|
||||||
return ExecuteAndWrapExceptionAsync(() => _channel.ConnectAsync().TimeoutAfter(timeout));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task DisconnectAsync(TimeSpan timeout)
|
|
||||||
{
|
|
||||||
ThrowIfDisposed();
|
|
||||||
_logger.Verbose<MqttChannelAdapter>("Disconnecting [Timeout={0}]", timeout);
|
|
||||||
|
|
||||||
return ExecuteAndWrapExceptionAsync(() => _channel.DisconnectAsync().TimeoutAfter(timeout));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task SendPacketsAsync(TimeSpan timeout, CancellationToken cancellationToken, IEnumerable<MqttBasePacket> packets)
|
|
||||||
{
|
|
||||||
ThrowIfDisposed();
|
|
||||||
|
|
||||||
return ExecuteAndWrapExceptionAsync(async () =>
|
|
||||||
{
|
|
||||||
await _semaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
foreach (var packet in packets)
|
|
||||||
{
|
|
||||||
if (cancellationToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packet == null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
_logger.Verbose<MqttChannelAdapter>("TX >>> {0} [Timeout={1}]", packet, timeout);
|
|
||||||
|
|
||||||
var chunks = PacketSerializer.Serialize(packet);
|
|
||||||
foreach (var chunk in chunks)
|
|
||||||
{
|
|
||||||
if (cancellationToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await _channel.SendStream.WriteAsync(chunk.Array, chunk.Offset, chunk.Count, cancellationToken).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cancellationToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (timeout > TimeSpan.Zero)
|
|
||||||
{
|
|
||||||
await _channel.SendStream.FlushAsync(cancellationToken).TimeoutAfter(timeout).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await _channel.SendStream.FlushAsync(cancellationToken).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_semaphore.Release();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<MqttBasePacket> ReceivePacketAsync(TimeSpan timeout, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
ThrowIfDisposed();
|
|
||||||
|
|
||||||
MqttBasePacket packet = null;
|
|
||||||
await ExecuteAndWrapExceptionAsync(async () =>
|
|
||||||
{
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
packet = PacketSerializer.Deserialize(receivedMqttPacket.Header, receivedMqttPacket.Body);
|
|
||||||
if (packet == null)
|
|
||||||
{
|
|
||||||
throw new MqttProtocolViolationException("Received malformed packet.");
|
|
||||||
}
|
|
||||||
|
|
||||||
_logger.Verbose<MqttChannelAdapter>("RX <<< {0}", packet);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
receivedMqttPacket?.Dispose();
|
|
||||||
}
|
|
||||||
}).ConfigureAwait(false);
|
|
||||||
|
|
||||||
return packet;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task<ReceivedMqttPacket> ReceiveAsync(Stream stream, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
var header = await MqttPacketReader.ReadHeaderAsync(stream, cancellationToken).ConfigureAwait(false);
|
|
||||||
if (header == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (header.BodyLength == 0)
|
|
||||||
{
|
|
||||||
return new ReceivedMqttPacket(header, new MemoryStream(new byte[0], false));
|
|
||||||
}
|
|
||||||
|
|
||||||
var body = header.BodyLength <= ReadBufferSize ? new MemoryStream(header.BodyLength) : new MemoryStream();
|
|
||||||
|
|
||||||
var buffer = new byte[ReadBufferSize];
|
|
||||||
while (body.Length < header.BodyLength)
|
|
||||||
{
|
|
||||||
var bytesLeft = header.BodyLength - (int)body.Length;
|
|
||||||
if (bytesLeft > buffer.Length)
|
|
||||||
{
|
|
||||||
bytesLeft = buffer.Length;
|
|
||||||
}
|
|
||||||
|
|
||||||
var readBytesCount = await stream.ReadAsync(buffer, 0, bytesLeft, cancellationToken).ConfigureAwait(false);
|
|
||||||
|
|
||||||
// Check if the client closed the connection before sending the full body.
|
|
||||||
if (readBytesCount == 0)
|
|
||||||
{
|
|
||||||
throw new MqttCommunicationException("Connection closed while reading remaining packet body.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Here is no need to await because internally only an array is used and no real I/O operation is made.
|
|
||||||
// Using async here will only generate overhead.
|
|
||||||
body.Write(buffer, 0, readBytesCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
body.Seek(0L, SeekOrigin.Begin);
|
|
||||||
|
|
||||||
return new ReceivedMqttPacket(header, body);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task ExecuteAndWrapExceptionAsync(Func<Task> action)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await action().ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
catch (TaskCanceledException)
|
|
||||||
{
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
catch (OperationCanceledException)
|
|
||||||
{
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
catch (MqttCommunicationTimedOutException)
|
|
||||||
{
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
catch (MqttCommunicationException)
|
|
||||||
{
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
catch (COMException comException)
|
|
||||||
{
|
|
||||||
if ((uint)comException.HResult == ErrorOperationAborted)
|
|
||||||
{
|
|
||||||
throw new OperationCanceledException();
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new MqttCommunicationException(comException);
|
|
||||||
}
|
|
||||||
catch (IOException exception)
|
|
||||||
{
|
|
||||||
if (exception.InnerException is SocketException socketException)
|
|
||||||
{
|
|
||||||
if (socketException.SocketErrorCode == SocketError.ConnectionAborted)
|
|
||||||
{
|
|
||||||
throw new OperationCanceledException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new MqttCommunicationException(exception);
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
throw new MqttCommunicationException(exception);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_isDisposed = true;
|
|
||||||
_semaphore?.Dispose();
|
|
||||||
_channel?.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ThrowIfDisposed()
|
|
||||||
{
|
|
||||||
if (_isDisposed)
|
|
||||||
{
|
|
||||||
throw new ObjectDisposedException(nameof(MqttChannelAdapter));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
using MQTTnet.Exceptions;
|
|
||||||
using MQTTnet.Protocol;
|
|
||||||
|
|
||||||
namespace MQTTnet.Adapter
|
|
||||||
{
|
|
||||||
public class MqttConnectingFailedException : MqttCommunicationException
|
|
||||||
{
|
|
||||||
public MqttConnectingFailedException(MqttConnectReturnCode returnCode)
|
|
||||||
: base($"Connecting with MQTT server failed ({returnCode}).")
|
|
||||||
{
|
|
||||||
ReturnCode = returnCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MqttConnectReturnCode ReturnCode { get; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace MQTTnet.Adapter
|
|
||||||
{
|
|
||||||
public class MqttServerAdapterClientAcceptedEventArgs : EventArgs
|
|
||||||
{
|
|
||||||
public MqttServerAdapterClientAcceptedEventArgs(IMqttChannelAdapter client)
|
|
||||||
{
|
|
||||||
Client = client ?? throw new ArgumentNullException(nameof(client));
|
|
||||||
}
|
|
||||||
|
|
||||||
public IMqttChannelAdapter Client { get; }
|
|
||||||
|
|
||||||
public Task SessionTask { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using MQTTnet.Packets;
|
|
||||||
|
|
||||||
namespace MQTTnet.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,41 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using MQTTnet.Protocol;
|
|
||||||
|
|
||||||
namespace MQTTnet
|
|
||||||
{
|
|
||||||
public static class ApplicationMessagePublisherExtensions
|
|
||||||
{
|
|
||||||
public static Task PublishAsync(this IApplicationMessagePublisher publisher, params MqttApplicationMessage[] applicationMessages)
|
|
||||||
{
|
|
||||||
if (publisher == null) throw new ArgumentNullException(nameof(publisher));
|
|
||||||
if (applicationMessages == null) throw new ArgumentNullException(nameof(applicationMessages));
|
|
||||||
|
|
||||||
return publisher.PublishAsync(applicationMessages);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Task PublishAsync(this IApplicationMessagePublisher publisher, string topic)
|
|
||||||
{
|
|
||||||
if (publisher == null) throw new ArgumentNullException(nameof(publisher));
|
|
||||||
if (topic == null) throw new ArgumentNullException(nameof(topic));
|
|
||||||
|
|
||||||
return publisher.PublishAsync(new MqttApplicationMessageBuilder().WithTopic(topic).Build());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Task PublishAsync(this IApplicationMessagePublisher publisher, string topic, string payload)
|
|
||||||
{
|
|
||||||
if (publisher == null) throw new ArgumentNullException(nameof(publisher));
|
|
||||||
if (topic == null) throw new ArgumentNullException(nameof(topic));
|
|
||||||
|
|
||||||
return publisher.PublishAsync(new MqttApplicationMessageBuilder().WithTopic(topic).WithPayload(payload).Build());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Task PublishAsync(this IApplicationMessagePublisher publisher, string topic, string payload, MqttQualityOfServiceLevel qualityOfServiceLevel)
|
|
||||||
{
|
|
||||||
if (publisher == null) throw new ArgumentNullException(nameof(publisher));
|
|
||||||
if (topic == null) throw new ArgumentNullException(nameof(topic));
|
|
||||||
|
|
||||||
return publisher.PublishAsync(new MqttApplicationMessageBuilder().WithTopic(topic).WithPayload(payload).WithQualityOfServiceLevel(qualityOfServiceLevel).Build());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace MQTTnet.Channel
|
|
||||||
{
|
|
||||||
public interface IMqttChannel : IDisposable
|
|
||||||
{
|
|
||||||
Stream SendStream { get; }
|
|
||||||
Stream ReceiveStream { get; }
|
|
||||||
|
|
||||||
Task ConnectAsync();
|
|
||||||
Task DisconnectAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace MQTTnet.Client
|
|
||||||
{
|
|
||||||
public interface IMqttClient : IApplicationMessageReceiver, IApplicationMessagePublisher, IDisposable
|
|
||||||
{
|
|
||||||
bool IsConnected { get; }
|
|
||||||
|
|
||||||
event EventHandler<MqttClientConnectedEventArgs> Connected;
|
|
||||||
event EventHandler<MqttClientDisconnectedEventArgs> Disconnected;
|
|
||||||
|
|
||||||
Task<MqttClientConnectResult> ConnectAsync(IMqttClientOptions options);
|
|
||||||
Task DisconnectAsync();
|
|
||||||
|
|
||||||
Task<IList<MqttSubscribeResult>> SubscribeAsync(IEnumerable<TopicFilter> topicFilters);
|
|
||||||
Task UnsubscribeAsync(IEnumerable<string> topics);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
using MQTTnet.Adapter;
|
|
||||||
using MQTTnet.Diagnostics;
|
|
||||||
|
|
||||||
namespace MQTTnet.Client
|
|
||||||
{
|
|
||||||
public interface IMqttClientAdapterFactory
|
|
||||||
{
|
|
||||||
IMqttChannelAdapter CreateClientAdapter(IMqttClientOptions options, IMqttNetLogger logger);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
namespace MQTTnet.Client
|
|
||||||
{
|
|
||||||
public interface IMqttClientChannelOptions
|
|
||||||
{
|
|
||||||
MqttClientTlsOptions TlsOptions { get; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
namespace MQTTnet.Client
|
|
||||||
{
|
|
||||||
public interface IMqttClientCredentials
|
|
||||||
{
|
|
||||||
string Password { get; }
|
|
||||||
string Username { get; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
using MQTTnet.Diagnostics;
|
|
||||||
using MQTTnet.ManagedClient;
|
|
||||||
|
|
||||||
namespace MQTTnet.Client
|
|
||||||
{
|
|
||||||
public interface IMqttClientFactory
|
|
||||||
{
|
|
||||||
IMqttClient CreateMqttClient();
|
|
||||||
|
|
||||||
IMqttClient CreateMqttClient(IMqttNetLogger logger);
|
|
||||||
|
|
||||||
IManagedMqttClient CreateManagedMqttClient();
|
|
||||||
|
|
||||||
IManagedMqttClient CreateManagedMqttClient(IMqttNetLogger logger);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
using System;
|
|
||||||
using MQTTnet.Serializer;
|
|
||||||
|
|
||||||
namespace MQTTnet.Client
|
|
||||||
{
|
|
||||||
public interface IMqttClientOptions
|
|
||||||
{
|
|
||||||
string ClientId { get; }
|
|
||||||
|
|
||||||
IMqttClientCredentials Credentials { get; }
|
|
||||||
bool CleanSession { get; }
|
|
||||||
MqttApplicationMessage WillMessage { get; }
|
|
||||||
|
|
||||||
TimeSpan CommunicationTimeout { get; }
|
|
||||||
TimeSpan KeepAlivePeriod { get; }
|
|
||||||
TimeSpan? KeepAliveSendInterval { get; }
|
|
||||||
|
|
||||||
MqttProtocolVersion ProtocolVersion { get; }
|
|
||||||
|
|
||||||
IMqttClientChannelOptions ChannelOptions { get; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,516 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using MQTTnet.Adapter;
|
|
||||||
using MQTTnet.Diagnostics;
|
|
||||||
using MQTTnet.Exceptions;
|
|
||||||
using MQTTnet.Internal;
|
|
||||||
using MQTTnet.Packets;
|
|
||||||
using MQTTnet.Protocol;
|
|
||||||
|
|
||||||
namespace MQTTnet.Client
|
|
||||||
{
|
|
||||||
public class MqttClient : IMqttClient
|
|
||||||
{
|
|
||||||
private readonly MqttPacketIdentifierProvider _packetIdentifierProvider = new MqttPacketIdentifierProvider();
|
|
||||||
private readonly Stopwatch _sendTracker = new Stopwatch();
|
|
||||||
private readonly SemaphoreSlim _disconnectLock = new SemaphoreSlim(1, 1);
|
|
||||||
private readonly IMqttClientAdapterFactory _adapterFactory;
|
|
||||||
private readonly MqttPacketDispatcher _packetDispatcher;
|
|
||||||
private readonly IMqttNetLogger _logger;
|
|
||||||
|
|
||||||
private IMqttClientOptions _options;
|
|
||||||
private bool _isReceivingPackets;
|
|
||||||
private CancellationTokenSource _cancellationTokenSource;
|
|
||||||
private Task _packetReceiverTask;
|
|
||||||
private Task _keepAliveMessageSenderTask;
|
|
||||||
private IMqttChannelAdapter _adapter;
|
|
||||||
|
|
||||||
public MqttClient(IMqttClientAdapterFactory channelFactory, IMqttNetLogger logger)
|
|
||||||
{
|
|
||||||
_adapterFactory = channelFactory ?? throw new ArgumentNullException(nameof(channelFactory));
|
|
||||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
|
||||||
|
|
||||||
_packetDispatcher = new MqttPacketDispatcher(logger);
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
_packetIdentifierProvider.Reset();
|
|
||||||
_packetDispatcher.Reset();
|
|
||||||
|
|
||||||
_adapter = _adapterFactory.CreateClientAdapter(options, _logger);
|
|
||||||
|
|
||||||
_logger.Verbose<MqttClient>("Trying to connect with server.");
|
|
||||||
await _adapter.ConnectAsync(_options.CommunicationTimeout).ConfigureAwait(false);
|
|
||||||
_logger.Verbose<MqttClient>("Connection with server established.");
|
|
||||||
|
|
||||||
await StartReceivingPacketsAsync().ConfigureAwait(false);
|
|
||||||
|
|
||||||
var connectResponse = await AuthenticateAsync(options.WillMessage).ConfigureAwait(false);
|
|
||||||
_logger.Verbose<MqttClient>("MQTT connection with server established.");
|
|
||||||
|
|
||||||
_sendTracker.Restart();
|
|
||||||
|
|
||||||
if (_options.KeepAlivePeriod != TimeSpan.Zero)
|
|
||||||
{
|
|
||||||
StartSendingKeepAliveMessages();
|
|
||||||
}
|
|
||||||
|
|
||||||
IsConnected = true;
|
|
||||||
Connected?.Invoke(this, new MqttClientConnectedEventArgs(connectResponse.IsSessionPresent));
|
|
||||||
|
|
||||||
_logger.Info<MqttClient>("Connected.");
|
|
||||||
return new MqttClientConnectResult(connectResponse.IsSessionPresent);
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
_logger.Error<MqttClient>(exception, "Error while connecting with server.");
|
|
||||||
await DisconnectInternalAsync(null, exception).ConfigureAwait(false);
|
|
||||||
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task DisconnectAsync()
|
|
||||||
{
|
|
||||||
if (!IsConnected)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (!_cancellationTokenSource.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
await SendAsync(new MqttDisconnectPacket()).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
await DisconnectInternalAsync(null, null).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IList<MqttSubscribeResult>> SubscribeAsync(IEnumerable<TopicFilter> topicFilters)
|
|
||||||
{
|
|
||||||
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
|
|
||||||
|
|
||||||
ThrowIfNotConnected();
|
|
||||||
|
|
||||||
var subscribePacket = new MqttSubscribePacket
|
|
||||||
{
|
|
||||||
PacketIdentifier = _packetIdentifierProvider.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 = _packetIdentifierProvider.GetNewPacketIdentifier(),
|
|
||||||
TopicFilters = topicFilters.ToList()
|
|
||||||
};
|
|
||||||
|
|
||||||
await SendAndReceiveAsync<MqttUnsubAckPacket>(unsubscribePacket).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task PublishAsync(IEnumerable<MqttApplicationMessage> applicationMessages)
|
|
||||||
{
|
|
||||||
ThrowIfNotConnected();
|
|
||||||
|
|
||||||
var publishPackets = applicationMessages.Select(m => m.ToPublishPacket());
|
|
||||||
var packetGroups = publishPackets.GroupBy(p => p.QualityOfServiceLevel).OrderBy(g => g.Key);
|
|
||||||
|
|
||||||
foreach (var qosGroup in packetGroups)
|
|
||||||
{
|
|
||||||
switch (qosGroup.Key)
|
|
||||||
{
|
|
||||||
case MqttQualityOfServiceLevel.AtMostOnce:
|
|
||||||
{
|
|
||||||
// No packet identifier is used for QoS 0 [3.3.2.2 Packet Identifier]
|
|
||||||
await SendAsync(qosGroup.Cast<MqttBasePacket>().ToArray()).ConfigureAwait(false);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case MqttQualityOfServiceLevel.AtLeastOnce:
|
|
||||||
{
|
|
||||||
foreach (var publishPacket in qosGroup)
|
|
||||||
{
|
|
||||||
publishPacket.PacketIdentifier = _packetIdentifierProvider.GetNewPacketIdentifier();
|
|
||||||
await SendAndReceiveAsync<MqttPubAckPacket>(publishPacket).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case MqttQualityOfServiceLevel.ExactlyOnce:
|
|
||||||
{
|
|
||||||
foreach (var publishPacket in qosGroup)
|
|
||||||
{
|
|
||||||
publishPacket.PacketIdentifier = _packetIdentifierProvider.GetNewPacketIdentifier();
|
|
||||||
|
|
||||||
var pubRecPacket = await SendAndReceiveAsync<MqttPubRecPacket>(publishPacket).ConfigureAwait(false);
|
|
||||||
var pubRelPacket = new MqttPubRelPacket
|
|
||||||
{
|
|
||||||
PacketIdentifier = pubRecPacket.PacketIdentifier
|
|
||||||
};
|
|
||||||
|
|
||||||
await SendAndReceiveAsync<MqttPubCompPacket>(pubRelPacket).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_cancellationTokenSource?.Dispose();
|
|
||||||
_cancellationTokenSource = null;
|
|
||||||
|
|
||||||
_adapter?.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
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 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(Task sender, Exception exception)
|
|
||||||
{
|
|
||||||
await _disconnectLock.WaitAsync();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (_cancellationTokenSource == null || _cancellationTokenSource.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_cancellationTokenSource.Cancel(false);
|
|
||||||
}
|
|
||||||
catch (Exception adapterException)
|
|
||||||
{
|
|
||||||
_logger.Warning<MqttClient>(adapterException, "Error while disconnecting from adapter.");
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_disconnectLock.Release();
|
|
||||||
}
|
|
||||||
|
|
||||||
var clientWasConnected = IsConnected;
|
|
||||||
IsConnected = false;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (_packetReceiverTask != null && _packetReceiverTask != sender)
|
|
||||||
{
|
|
||||||
_packetReceiverTask.Wait();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_keepAliveMessageSenderTask != null && _keepAliveMessageSenderTask != sender)
|
|
||||||
{
|
|
||||||
_keepAliveMessageSenderTask.Wait();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_adapter != null)
|
|
||||||
{
|
|
||||||
await _adapter.DisconnectAsync(_options.CommunicationTimeout).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
_logger.Verbose<MqttClient>("Disconnected from adapter.");
|
|
||||||
}
|
|
||||||
catch (Exception adapterException)
|
|
||||||
{
|
|
||||||
_logger.Warning<MqttClient>(adapterException, "Error while disconnecting from adapter.");
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_adapter?.Dispose();
|
|
||||||
_adapter = null;
|
|
||||||
_cancellationTokenSource?.Dispose();
|
|
||||||
_cancellationTokenSource = null;
|
|
||||||
|
|
||||||
_logger.Info<MqttClient>("Disconnected.");
|
|
||||||
Disconnected?.Invoke(this, new MqttClientDisconnectedEventArgs(clientWasConnected, exception));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task ProcessReceivedPacketAsync(MqttBasePacket packet)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (packet is MqttPublishPacket publishPacket)
|
|
||||||
{
|
|
||||||
await ProcessReceivedPublishPacketAsync(publishPacket).ConfigureAwait(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packet is MqttPingReqPacket)
|
|
||||||
{
|
|
||||||
await SendAsync(new MqttPingRespPacket()).ConfigureAwait(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packet is MqttDisconnectPacket)
|
|
||||||
{
|
|
||||||
await DisconnectAsync().ConfigureAwait(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packet is MqttPubRelPacket pubRelPacket)
|
|
||||||
{
|
|
||||||
await ProcessReceivedPubRelPacket(pubRelPacket).ConfigureAwait(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_packetDispatcher.Dispatch(packet);
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
_logger.Error<MqttClient>(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.Error<MqttClient>(exception, "Unhandled exception while handling application message.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Task ProcessReceivedPublishPacketAsync(MqttPublishPacket publishPacket)
|
|
||||||
{
|
|
||||||
if (_cancellationTokenSource.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
return Task.FromResult(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtMostOnce)
|
|
||||||
{
|
|
||||||
FireApplicationMessageReceivedEvent(publishPacket);
|
|
||||||
return Task.FromResult(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.AtLeastOnce)
|
|
||||||
{
|
|
||||||
FireApplicationMessageReceivedEvent(publishPacket);
|
|
||||||
return SendAsync(new MqttPubAckPacket { PacketIdentifier = publishPacket.PacketIdentifier });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (publishPacket.QualityOfServiceLevel == MqttQualityOfServiceLevel.ExactlyOnce)
|
|
||||||
{
|
|
||||||
// QoS 2 is implement as method "B" [4.3.3 QoS 2: Exactly once delivery]
|
|
||||||
FireApplicationMessageReceivedEvent(publishPacket);
|
|
||||||
return SendAsync(new MqttPubRecPacket { PacketIdentifier = publishPacket.PacketIdentifier });
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new MqttCommunicationException("Received a not supported QoS level.");
|
|
||||||
}
|
|
||||||
|
|
||||||
private Task ProcessReceivedPubRelPacket(MqttPubRelPacket pubRelPacket)
|
|
||||||
{
|
|
||||||
var response = new MqttPubCompPacket
|
|
||||||
{
|
|
||||||
PacketIdentifier = pubRelPacket.PacketIdentifier
|
|
||||||
};
|
|
||||||
|
|
||||||
return SendAsync(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Task SendAsync(params MqttBasePacket[] packets)
|
|
||||||
{
|
|
||||||
_sendTracker.Restart();
|
|
||||||
return _adapter.SendPacketsAsync(_options.CommunicationTimeout, _cancellationTokenSource.Token, packets);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<TResponsePacket> SendAndReceiveAsync<TResponsePacket>(MqttBasePacket requestPacket) where TResponsePacket : MqttBasePacket
|
|
||||||
{
|
|
||||||
ushort? identifier = null;
|
|
||||||
if (requestPacket is IMqttPacketWithIdentifier requestPacketWithIdentifier)
|
|
||||||
{
|
|
||||||
identifier = requestPacketWithIdentifier.PacketIdentifier;
|
|
||||||
}
|
|
||||||
|
|
||||||
var packetAwaiter = _packetDispatcher.WaitForPacketAsync(typeof(TResponsePacket), identifier, _options.CommunicationTimeout);
|
|
||||||
await SendAsync(requestPacket).ConfigureAwait(false);
|
|
||||||
|
|
||||||
return (TResponsePacket)await packetAwaiter.ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task SendKeepAliveMessagesAsync()
|
|
||||||
{
|
|
||||||
_logger.Verbose<MqttClient>("Start sending keep alive packets.");
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
while (!_cancellationTokenSource.Token.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
var keepAliveSendInterval = TimeSpan.FromSeconds(_options.KeepAlivePeriod.TotalSeconds * 0.75);
|
|
||||||
if (_options.KeepAliveSendInterval.HasValue)
|
|
||||||
{
|
|
||||||
keepAliveSendInterval = _options.KeepAliveSendInterval.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_sendTracker.Elapsed > keepAliveSendInterval)
|
|
||||||
{
|
|
||||||
await SendAndReceiveAsync<MqttPingRespPacket>(new MqttPingReqPacket()).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
await Task.Delay(keepAliveSendInterval, _cancellationTokenSource.Token).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
if (exception is OperationCanceledException)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
else if (exception is MqttCommunicationException)
|
|
||||||
{
|
|
||||||
_logger.Warning<MqttClient>(exception, "MQTT communication exception while sending/receiving keep alive packets.");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_logger.Error<MqttClient>(exception, "Unhandled exception while sending/receiving keep alive packets.");
|
|
||||||
}
|
|
||||||
|
|
||||||
await DisconnectInternalAsync(_keepAliveMessageSenderTask, exception).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_logger.Verbose<MqttClient>("Stopped sending keep alive packets.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task ReceivePacketsAsync()
|
|
||||||
{
|
|
||||||
_logger.Verbose<MqttClient>("Start receiving packets.");
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
while (!_cancellationTokenSource.Token.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
_isReceivingPackets = true;
|
|
||||||
|
|
||||||
var packet = await _adapter.ReceivePacketAsync(TimeSpan.Zero, _cancellationTokenSource.Token).ConfigureAwait(false);
|
|
||||||
|
|
||||||
if (_cancellationTokenSource.Token.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
StartProcessReceivedPacket(packet);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
if (exception is OperationCanceledException)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
else if (exception is MqttCommunicationException)
|
|
||||||
{
|
|
||||||
_logger.Warning<MqttClient>(exception, "MQTT communication exception while receiving packets.");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_logger.Error<MqttClient>(exception, "Unhandled exception while receiving packets.");
|
|
||||||
}
|
|
||||||
|
|
||||||
await DisconnectInternalAsync(_packetReceiverTask, exception).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_logger.Verbose<MqttClient>("Stopped receiving packets.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void StartProcessReceivedPacket(MqttBasePacket packet)
|
|
||||||
{
|
|
||||||
Task.Run(() => ProcessReceivedPacketAsync(packet), _cancellationTokenSource.Token);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task StartReceivingPacketsAsync()
|
|
||||||
{
|
|
||||||
_isReceivingPackets = false;
|
|
||||||
|
|
||||||
_packetReceiverTask = Task.Run(ReceivePacketsAsync, _cancellationTokenSource.Token);
|
|
||||||
|
|
||||||
while (!_isReceivingPackets && !_cancellationTokenSource.Token.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
await Task.Delay(TimeSpan.FromMilliseconds(100), _cancellationTokenSource.Token).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void StartSendingKeepAliveMessages()
|
|
||||||
{
|
|
||||||
_keepAliveMessageSenderTask = Task.Run(SendKeepAliveMessagesAsync, _cancellationTokenSource.Token);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
namespace MQTTnet.Client
|
|
||||||
{
|
|
||||||
public class MqttClientConnectResult
|
|
||||||
{
|
|
||||||
public MqttClientConnectResult(bool isSessionPresent)
|
|
||||||
{
|
|
||||||
IsSessionPresent = isSessionPresent;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsSessionPresent { get; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace MQTTnet.Client
|
|
||||||
{
|
|
||||||
public class MqttClientConnectedEventArgs : EventArgs
|
|
||||||
{
|
|
||||||
public MqttClientConnectedEventArgs(bool isSessionPresent)
|
|
||||||
{
|
|
||||||
IsSessionPresent = isSessionPresent;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsSessionPresent { get; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
namespace MQTTnet.Client
|
|
||||||
{
|
|
||||||
public class MqttClientCredentials : IMqttClientCredentials
|
|
||||||
{
|
|
||||||
public string Username { get; set; }
|
|
||||||
|
|
||||||
public string Password { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace MQTTnet.Client
|
|
||||||
{
|
|
||||||
public class MqttClientDisconnectedEventArgs : EventArgs
|
|
||||||
{
|
|
||||||
public MqttClientDisconnectedEventArgs(bool clientWasConnected, Exception exception)
|
|
||||||
{
|
|
||||||
ClientWasConnected = clientWasConnected;
|
|
||||||
Exception = exception;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool ClientWasConnected { get; }
|
|
||||||
|
|
||||||
public Exception Exception { get; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using MQTTnet.Protocol;
|
|
||||||
|
|
||||||
namespace MQTTnet.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<IList<MqttSubscribeResult>> SubscribeAsync(this IMqttClient client, string topic, MqttQualityOfServiceLevel qualityOfServiceLevel)
|
|
||||||
{
|
|
||||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
|
||||||
if (topic == null) throw new ArgumentNullException(nameof(topic));
|
|
||||||
|
|
||||||
return client.SubscribeAsync(new TopicFilterBuilder().WithTopic(topic).WithQualityOfServiceLevel(qualityOfServiceLevel).Build());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Task<IList<MqttSubscribeResult>> SubscribeAsync(this IMqttClient client, string topic)
|
|
||||||
{
|
|
||||||
if (client == null) throw new ArgumentNullException(nameof(client));
|
|
||||||
if (topic == null) throw new ArgumentNullException(nameof(topic));
|
|
||||||
|
|
||||||
return client.SubscribeAsync(new TopicFilterBuilder().WithTopic(topic).Build());
|
|
||||||
}
|
|
||||||
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
using System;
|
|
||||||
using MQTTnet.Serializer;
|
|
||||||
|
|
||||||
namespace MQTTnet.Client
|
|
||||||
{
|
|
||||||
public class MqttClientOptions : IMqttClientOptions
|
|
||||||
{
|
|
||||||
public MqttApplicationMessage WillMessage { get; set; }
|
|
||||||
|
|
||||||
public string ClientId { get; set; } = Guid.NewGuid().ToString("N");
|
|
||||||
|
|
||||||
public bool CleanSession { get; set; } = true;
|
|
||||||
|
|
||||||
public IMqttClientCredentials Credentials { get; set; } = new MqttClientCredentials();
|
|
||||||
|
|
||||||
public TimeSpan KeepAlivePeriod { get; set; } = TimeSpan.FromSeconds(15);
|
|
||||||
|
|
||||||
public TimeSpan? KeepAliveSendInterval { get; set; }
|
|
||||||
|
|
||||||
public TimeSpan CommunicationTimeout { get; set; } = TimeSpan.FromSeconds(10);
|
|
||||||
|
|
||||||
public MqttProtocolVersion ProtocolVersion { get; set; } = MqttProtocolVersion.V311;
|
|
||||||
|
|
||||||
public IMqttClientChannelOptions ChannelOptions { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,135 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
using MQTTnet.Serializer;
|
|
||||||
|
|
||||||
namespace MQTTnet.Client
|
|
||||||
{
|
|
||||||
public class MqttClientOptionsBuilder
|
|
||||||
{
|
|
||||||
private readonly MqttClientOptions _options = new MqttClientOptions();
|
|
||||||
private MqttClientTcpOptions _tcpOptions;
|
|
||||||
private MqttClientWebSocketOptions _webSocketOptions;
|
|
||||||
|
|
||||||
private MqttClientTlsOptions _tlsOptions;
|
|
||||||
|
|
||||||
public MqttClientOptionsBuilder WithProtocolVersion(MqttProtocolVersion value)
|
|
||||||
{
|
|
||||||
_options.ProtocolVersion = value;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MqttClientOptionsBuilder WithCommunicationTimeout(TimeSpan value)
|
|
||||||
{
|
|
||||||
_options.CommunicationTimeout = value;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MqttClientOptionsBuilder WithCleanSession(bool value = true)
|
|
||||||
{
|
|
||||||
_options.CleanSession = value;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MqttClientOptionsBuilder WithKeepAlivePeriod(TimeSpan value)
|
|
||||||
{
|
|
||||||
_options.KeepAlivePeriod = value;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MqttClientOptionsBuilder WithClientId(string value)
|
|
||||||
{
|
|
||||||
_options.ClientId = value;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MqttClientOptionsBuilder WithWillMessage(MqttApplicationMessage value)
|
|
||||||
{
|
|
||||||
_options.WillMessage = value;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MqttClientOptionsBuilder WithCredentials(string username, string password = null)
|
|
||||||
{
|
|
||||||
_options.Credentials = new MqttClientCredentials
|
|
||||||
{
|
|
||||||
Username = username,
|
|
||||||
Password = password
|
|
||||||
};
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MqttClientOptionsBuilder WithTcpServer(string server, int? port = null)
|
|
||||||
{
|
|
||||||
_tcpOptions = new MqttClientTcpOptions
|
|
||||||
{
|
|
||||||
Server = server,
|
|
||||||
Port = port
|
|
||||||
};
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MqttClientOptionsBuilder WithWebSocketServer(string uri)
|
|
||||||
{
|
|
||||||
_webSocketOptions = new MqttClientWebSocketOptions
|
|
||||||
{
|
|
||||||
Uri = uri
|
|
||||||
};
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MqttClientOptionsBuilder WithTls(
|
|
||||||
bool allowUntrustedCertificates = false,
|
|
||||||
bool ignoreCertificateChainErrors = false,
|
|
||||||
bool ignoreCertificateRevocationErrors = false,
|
|
||||||
params byte[][] certificates)
|
|
||||||
{
|
|
||||||
_tlsOptions = new MqttClientTlsOptions
|
|
||||||
{
|
|
||||||
UseTls = true,
|
|
||||||
AllowUntrustedCertificates = allowUntrustedCertificates,
|
|
||||||
IgnoreCertificateChainErrors = ignoreCertificateChainErrors,
|
|
||||||
IgnoreCertificateRevocationErrors = ignoreCertificateRevocationErrors,
|
|
||||||
Certificates = certificates?.ToList()
|
|
||||||
};
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MqttClientOptionsBuilder WithTls()
|
|
||||||
{
|
|
||||||
_tlsOptions = new MqttClientTlsOptions
|
|
||||||
{
|
|
||||||
UseTls = true
|
|
||||||
};
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IMqttClientOptions Build()
|
|
||||||
{
|
|
||||||
if (_tlsOptions != null)
|
|
||||||
{
|
|
||||||
if (_tcpOptions == null && _webSocketOptions == null)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("A channel (TCP or WebSocket) must be set if TLS is configured.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_tcpOptions != null)
|
|
||||||
{
|
|
||||||
_tcpOptions.TlsOptions = _tlsOptions;
|
|
||||||
}
|
|
||||||
else if (_webSocketOptions != null)
|
|
||||||
{
|
|
||||||
_webSocketOptions.TlsOptions = _tlsOptions;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_options.ChannelOptions = (IMqttClientChannelOptions)_tcpOptions ?? _webSocketOptions;
|
|
||||||
|
|
||||||
return _options;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
namespace MQTTnet.Client
|
|
||||||
{
|
|
||||||
public class MqttClientTcpOptions : IMqttClientChannelOptions
|
|
||||||
{
|
|
||||||
public string Server { get; set; }
|
|
||||||
|
|
||||||
public int? Port { get; set; }
|
|
||||||
|
|
||||||
public int BufferSize { get; set; } = 20 * 4096;
|
|
||||||
|
|
||||||
public MqttClientTlsOptions TlsOptions { get; set; } = new MqttClientTlsOptions();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace MQTTnet.Client
|
|
||||||
{
|
|
||||||
public class MqttClientTlsOptions
|
|
||||||
{
|
|
||||||
public bool UseTls { get; set; }
|
|
||||||
|
|
||||||
public bool IgnoreCertificateRevocationErrors { get; set; }
|
|
||||||
|
|
||||||
public bool IgnoreCertificateChainErrors { get; set; }
|
|
||||||
|
|
||||||
public bool AllowUntrustedCertificates { get; set; }
|
|
||||||
|
|
||||||
public List<byte[]> Certificates { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using System.Net;
|
|
||||||
|
|
||||||
namespace MQTTnet.Client
|
|
||||||
{
|
|
||||||
public class MqttClientWebSocketOptions : IMqttClientChannelOptions
|
|
||||||
{
|
|
||||||
public string Uri { get; set; }
|
|
||||||
|
|
||||||
public IDictionary<string, string> RequestHeaders { get; set; }
|
|
||||||
|
|
||||||
public ICollection<string> SubProtocols { get; set; } = new List<string> { "mqtt" };
|
|
||||||
|
|
||||||
public CookieContainer CookieContainer { get; set; }
|
|
||||||
|
|
||||||
public MqttClientTlsOptions TlsOptions { get; set; } = new MqttClientTlsOptions();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using MQTTnet.Diagnostics;
|
|
||||||
using MQTTnet.Exceptions;
|
|
||||||
using MQTTnet.Internal;
|
|
||||||
using MQTTnet.Packets;
|
|
||||||
|
|
||||||
namespace MQTTnet.Client
|
|
||||||
{
|
|
||||||
public class MqttPacketDispatcher
|
|
||||||
{
|
|
||||||
private readonly ConcurrentDictionary<Type, ConcurrentDictionary<ushort, TaskCompletionSource<MqttBasePacket>>> _awaiters = new ConcurrentDictionary<Type, ConcurrentDictionary<ushort, TaskCompletionSource<MqttBasePacket>>>();
|
|
||||||
private readonly IMqttNetLogger _logger;
|
|
||||||
|
|
||||||
public MqttPacketDispatcher(IMqttNetLogger logger)
|
|
||||||
{
|
|
||||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<MqttBasePacket> WaitForPacketAsync(Type responseType, ushort? identifier, TimeSpan timeout)
|
|
||||||
{
|
|
||||||
var packetAwaiter = AddPacketAwaiter(responseType, identifier);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return await packetAwaiter.Task.TimeoutAfter(timeout).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
catch (MqttCommunicationTimedOutException)
|
|
||||||
{
|
|
||||||
_logger.Warning<MqttPacketDispatcher>("Timeout while waiting for packet of type '{0}'.", responseType.Name);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
RemovePacketAwaiter(responseType, identifier);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispatch(MqttBasePacket packet)
|
|
||||||
{
|
|
||||||
if (packet == null) throw new ArgumentNullException(nameof(packet));
|
|
||||||
|
|
||||||
var type = packet.GetType();
|
|
||||||
|
|
||||||
if (_awaiters.TryGetValue(type, out var byId))
|
|
||||||
{
|
|
||||||
ushort? identifier = 0;
|
|
||||||
if (packet is IMqttPacketWithIdentifier packetWithIdentifier)
|
|
||||||
{
|
|
||||||
identifier = packetWithIdentifier.PacketIdentifier;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (byId.TryRemove(identifier.Value, out var tcs))
|
|
||||||
{
|
|
||||||
tcs.TrySetResult(packet);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new InvalidOperationException($"Packet of type '{type.Name}' not handled or dispatched.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Reset()
|
|
||||||
{
|
|
||||||
_awaiters.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
private TaskCompletionSource<MqttBasePacket> AddPacketAwaiter(Type responseType, ushort? identifier)
|
|
||||||
{
|
|
||||||
var tcs = new TaskCompletionSource<MqttBasePacket>();
|
|
||||||
|
|
||||||
if (!identifier.HasValue)
|
|
||||||
{
|
|
||||||
identifier = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
var byId = _awaiters.GetOrAdd(responseType, key => new ConcurrentDictionary<ushort, TaskCompletionSource<MqttBasePacket>>());
|
|
||||||
if (!byId.TryAdd(identifier.Value, tcs))
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException($"The packet dispatcher already has an awaiter for packet of type '{responseType}' with identifier {identifier}.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return tcs;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RemovePacketAwaiter(Type responseType, ushort? identifier)
|
|
||||||
{
|
|
||||||
if (!identifier.HasValue)
|
|
||||||
{
|
|
||||||
identifier = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
var byId = _awaiters.GetOrAdd(responseType, key => new ConcurrentDictionary<ushort, TaskCompletionSource<MqttBasePacket>>());
|
|
||||||
byId.TryRemove(identifier.Value, out var _);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
using MQTTnet.Protocol;
|
|
||||||
|
|
||||||
namespace MQTTnet.Client
|
|
||||||
{
|
|
||||||
public class MqttSubscribeResult
|
|
||||||
{
|
|
||||||
public MqttSubscribeResult(TopicFilter topicFilter, MqttSubscribeReturnCode returnCode)
|
|
||||||
{
|
|
||||||
TopicFilter = topicFilter;
|
|
||||||
ReturnCode = returnCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TopicFilter TopicFilter { get; }
|
|
||||||
|
|
||||||
public MqttSubscribeReturnCode ReturnCode { get; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace MQTTnet.Diagnostics
|
|
||||||
{
|
|
||||||
public interface IMqttNetLogger
|
|
||||||
{
|
|
||||||
event EventHandler<MqttNetLogMessagePublishedEventArgs> LogMessagePublished;
|
|
||||||
|
|
||||||
void Verbose<TSource>(string message, params object[] parameters);
|
|
||||||
|
|
||||||
void Info<TSource>(string message, params object[] parameters);
|
|
||||||
|
|
||||||
void Warning<TSource>(Exception exception, string message, params object[] parameters);
|
|
||||||
|
|
||||||
void Warning<TSource>(string message, params object[] parameters);
|
|
||||||
|
|
||||||
void Error<TSource>(Exception exception, string message, params object[] parameters);
|
|
||||||
|
|
||||||
void Error<TSource>(string message, params object[] parameters);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace MQTTnet.Diagnostics
|
|
||||||
{
|
|
||||||
public static class MqttNetGlobalLogger
|
|
||||||
{
|
|
||||||
public static event EventHandler<MqttNetLogMessagePublishedEventArgs> LogMessagePublished;
|
|
||||||
|
|
||||||
public static bool HasListeners => LogMessagePublished != null;
|
|
||||||
|
|
||||||
public static void Publish(MqttNetLogMessage logMessage)
|
|
||||||
{
|
|
||||||
if (logMessage == null) throw new ArgumentNullException(nameof(logMessage));
|
|
||||||
|
|
||||||
LogMessagePublished?.Invoke(null, new MqttNetLogMessagePublishedEventArgs(logMessage));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
namespace MQTTnet.Diagnostics
|
|
||||||
{
|
|
||||||
public enum MqttNetLogLevel
|
|
||||||
{
|
|
||||||
Verbose,
|
|
||||||
|
|
||||||
Info,
|
|
||||||
|
|
||||||
Warning,
|
|
||||||
|
|
||||||
Error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace MQTTnet.Diagnostics
|
|
||||||
{
|
|
||||||
public sealed class MqttNetLogMessage
|
|
||||||
{
|
|
||||||
public MqttNetLogMessage(string logId, DateTime timestamp, int threadId, string source, MqttNetLogLevel level, string message, Exception exception)
|
|
||||||
{
|
|
||||||
LogId = logId;
|
|
||||||
Timestamp = timestamp;
|
|
||||||
ThreadId = threadId;
|
|
||||||
Source = source;
|
|
||||||
Level = level;
|
|
||||||
Message = message;
|
|
||||||
Exception = exception;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string LogId { get; }
|
|
||||||
|
|
||||||
public DateTime Timestamp { get; }
|
|
||||||
|
|
||||||
public int ThreadId { get; }
|
|
||||||
|
|
||||||
public string Source { get; }
|
|
||||||
|
|
||||||
public MqttNetLogLevel Level { get; }
|
|
||||||
|
|
||||||
public string Message { get; }
|
|
||||||
|
|
||||||
public Exception Exception { get; }
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
var result = $"[{Timestamp:O}] [{LogId}] [{ThreadId}] [{Source}] [{Level}]: {Message}";
|
|
||||||
if (Exception != null)
|
|
||||||
{
|
|
||||||
result += Environment.NewLine + Exception;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace MQTTnet.Diagnostics
|
|
||||||
{
|
|
||||||
public sealed class MqttNetLogMessagePublishedEventArgs : EventArgs
|
|
||||||
{
|
|
||||||
public MqttNetLogMessagePublishedEventArgs(MqttNetLogMessage logMessage)
|
|
||||||
{
|
|
||||||
TraceMessage = logMessage ?? throw new ArgumentNullException(nameof(logMessage));
|
|
||||||
}
|
|
||||||
|
|
||||||
public MqttNetLogMessage TraceMessage { get; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace MQTTnet.Diagnostics
|
|
||||||
{
|
|
||||||
public class MqttNetLogger : IMqttNetLogger
|
|
||||||
{
|
|
||||||
private readonly string _logId;
|
|
||||||
|
|
||||||
public MqttNetLogger(string logId = null)
|
|
||||||
{
|
|
||||||
_logId = logId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public event EventHandler<MqttNetLogMessagePublishedEventArgs> LogMessagePublished;
|
|
||||||
|
|
||||||
public void Verbose<TSource>(string message, params object[] parameters)
|
|
||||||
{
|
|
||||||
Publish<TSource>(MqttNetLogLevel.Verbose, null, message, parameters);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Info<TSource>(string message, params object[] parameters)
|
|
||||||
{
|
|
||||||
Publish<TSource>(MqttNetLogLevel.Info, null, message, parameters);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Warning<TSource>(Exception exception, string message, params object[] parameters)
|
|
||||||
{
|
|
||||||
Publish<TSource>(MqttNetLogLevel.Warning, exception, message, parameters);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Warning<TSource>(string message, params object[] parameters)
|
|
||||||
{
|
|
||||||
Warning<TSource>(null, message, parameters);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Error<TSource>(Exception exception, string message, params object[] parameters)
|
|
||||||
{
|
|
||||||
Publish<TSource>(MqttNetLogLevel.Error, exception, message, parameters);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Error<TSource>(string message, params object[] parameters)
|
|
||||||
{
|
|
||||||
Warning<TSource>(null, message, parameters);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Publish<TSource>(MqttNetLogLevel logLevel, Exception exception, string message, object[] parameters)
|
|
||||||
{
|
|
||||||
var hasLocalListeners = LogMessagePublished != null;
|
|
||||||
var hasGlobalListeners = MqttNetGlobalLogger.HasListeners;
|
|
||||||
|
|
||||||
if (!hasLocalListeners && !hasGlobalListeners)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parameters.Length > 0)
|
|
||||||
{
|
|
||||||
message = string.Format(message, parameters);
|
|
||||||
}
|
|
||||||
|
|
||||||
var traceMessage = new MqttNetLogMessage(_logId, DateTime.Now, Environment.CurrentManagedThreadId, typeof(TSource).Name, logLevel, message, exception);
|
|
||||||
|
|
||||||
if (hasGlobalListeners)
|
|
||||||
{
|
|
||||||
MqttNetGlobalLogger.Publish(traceMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasLocalListeners)
|
|
||||||
{
|
|
||||||
LogMessagePublished?.Invoke(this, new MqttNetLogMessagePublishedEventArgs(traceMessage));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
namespace MQTTnet.Diagnostics
|
|
||||||
{
|
|
||||||
public static class TargetFrameworkInfoProvider
|
|
||||||
{
|
|
||||||
public static string TargetFramework
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
#if NET452
|
|
||||||
return "net452";
|
|
||||||
#elif NET461
|
|
||||||
return "net461";
|
|
||||||
#elif NETSTANDARD1_3
|
|
||||||
return "netstandard1.3";
|
|
||||||
#elif NETSTANDARD2_0
|
|
||||||
return "netstandard2.0";
|
|
||||||
#elif WINDOWS_UWP
|
|
||||||
return "uap10.0";
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace MQTTnet.Exceptions
|
|
||||||
{
|
|
||||||
public class MqttCommunicationException : Exception
|
|
||||||
{
|
|
||||||
protected MqttCommunicationException()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public MqttCommunicationException(Exception innerException)
|
|
||||||
: base(innerException.Message, innerException)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public MqttCommunicationException(string message)
|
|
||||||
: base(message)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
namespace MQTTnet.Exceptions
|
|
||||||
{
|
|
||||||
public sealed class MqttCommunicationTimedOutException : MqttCommunicationException
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace MQTTnet.Exceptions
|
|
||||||
{
|
|
||||||
public sealed class MqttProtocolViolationException : Exception
|
|
||||||
{
|
|
||||||
public MqttProtocolViolationException(string message)
|
|
||||||
: base(message)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace MQTTnet
|
|
||||||
{
|
|
||||||
public interface IApplicationMessagePublisher
|
|
||||||
{
|
|
||||||
Task PublishAsync(IEnumerable<MqttApplicationMessage> applicationMessages);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace MQTTnet
|
|
||||||
{
|
|
||||||
public interface IApplicationMessageReceiver
|
|
||||||
{
|
|
||||||
event EventHandler<MqttApplicationMessageReceivedEventArgs> ApplicationMessageReceived;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
using System;
|
|
||||||
using MQTTnet.Adapter;
|
|
||||||
using MQTTnet.Client;
|
|
||||||
using MQTTnet.Diagnostics;
|
|
||||||
using MQTTnet.Serializer;
|
|
||||||
|
|
||||||
namespace MQTTnet.Implementations
|
|
||||||
{
|
|
||||||
public class MqttClientAdapterFactory : IMqttClientAdapterFactory
|
|
||||||
{
|
|
||||||
public IMqttChannelAdapter CreateClientAdapter(IMqttClientOptions options, IMqttNetLogger logger)
|
|
||||||
{
|
|
||||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
|
||||||
|
|
||||||
var serializer = new MqttPacketSerializer { ProtocolVersion = options.ProtocolVersion };
|
|
||||||
|
|
||||||
switch (options.ChannelOptions)
|
|
||||||
{
|
|
||||||
case MqttClientTcpOptions tcpOptions:
|
|
||||||
{
|
|
||||||
return new MqttChannelAdapter(new MqttTcpChannel(tcpOptions), serializer, logger);
|
|
||||||
}
|
|
||||||
|
|
||||||
case MqttClientWebSocketOptions webSocketOptions:
|
|
||||||
{
|
|
||||||
return new MqttChannelAdapter(new MqttWebSocketChannel(webSocketOptions), serializer, logger);
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,176 +0,0 @@
|
|||||||
#if WINDOWS_UWP
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Runtime.InteropServices.WindowsRuntime;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Windows.Networking;
|
|
||||||
using Windows.Networking.Sockets;
|
|
||||||
using Windows.Security.Cryptography.Certificates;
|
|
||||||
using MQTTnet.Channel;
|
|
||||||
using MQTTnet.Client;
|
|
||||||
|
|
||||||
namespace MQTTnet.Implementations
|
|
||||||
{
|
|
||||||
public sealed class MqttTcpChannel : IMqttChannel
|
|
||||||
{
|
|
||||||
// ReSharper disable once MemberCanBePrivate.Global
|
|
||||||
// ReSharper disable once AutoPropertyCanBeMadeGetOnly.Global
|
|
||||||
public static int BufferSize { get; set; } = 4096 * 20; // Can be changed for fine tuning by library user.
|
|
||||||
|
|
||||||
private readonly int _bufferSize = BufferSize;
|
|
||||||
private readonly MqttClientTcpOptions _options;
|
|
||||||
|
|
||||||
private StreamSocket _socket;
|
|
||||||
|
|
||||||
public MqttTcpChannel(MqttClientTcpOptions options)
|
|
||||||
{
|
|
||||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
|
||||||
_bufferSize = _options.BufferSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MqttTcpChannel(StreamSocket socket)
|
|
||||||
{
|
|
||||||
_socket = socket ?? throw new ArgumentNullException(nameof(socket));
|
|
||||||
|
|
||||||
CreateStreams();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Stream SendStream { get; private set; }
|
|
||||||
public Stream ReceiveStream { get; private set; }
|
|
||||||
|
|
||||||
public static Func<MqttClientTcpOptions, IEnumerable<ChainValidationResult>> CustomIgnorableServerCertificateErrorsResolver { get; set; }
|
|
||||||
|
|
||||||
public async Task ConnectAsync()
|
|
||||||
{
|
|
||||||
if (_socket == null)
|
|
||||||
{
|
|
||||||
_socket = new StreamSocket();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_options.TlsOptions.UseTls)
|
|
||||||
{
|
|
||||||
await _socket.ConnectAsync(new HostName(_options.Server), _options.GetPort().ToString());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_socket.Control.ClientCertificate = LoadCertificate(_options);
|
|
||||||
|
|
||||||
foreach (var ignorableChainValidationResult in ResolveIgnorableServerCertificateErrors())
|
|
||||||
{
|
|
||||||
_socket.Control.IgnorableServerCertificateErrors.Add(ignorableChainValidationResult);
|
|
||||||
}
|
|
||||||
|
|
||||||
await _socket.ConnectAsync(new HostName(_options.Server), _options.GetPort().ToString(), SocketProtectionLevel.Tls12);
|
|
||||||
}
|
|
||||||
|
|
||||||
CreateStreams();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task DisconnectAsync()
|
|
||||||
{
|
|
||||||
Dispose();
|
|
||||||
return Task.FromResult(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
SendStream?.Dispose();
|
|
||||||
}
|
|
||||||
catch (ObjectDisposedException)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
catch (NullReferenceException)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
SendStream = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
ReceiveStream?.Dispose();
|
|
||||||
}
|
|
||||||
catch (ObjectDisposedException)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
catch (NullReferenceException)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
ReceiveStream = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_socket?.Dispose();
|
|
||||||
}
|
|
||||||
catch (ObjectDisposedException)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
catch (NullReferenceException)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_socket = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CreateStreams()
|
|
||||||
{
|
|
||||||
SendStream = _socket.OutputStream.AsStreamForWrite(_bufferSize);
|
|
||||||
ReceiveStream = _socket.InputStream.AsStreamForRead(_bufferSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Certificate LoadCertificate(MqttClientTcpOptions options)
|
|
||||||
{
|
|
||||||
if (options.TlsOptions.Certificates == null || !options.TlsOptions.Certificates.Any())
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.TlsOptions.Certificates.Count > 1)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException("Only one client certificate is supported for UWP.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Certificate(options.TlsOptions.Certificates.First().AsBuffer());
|
|
||||||
}
|
|
||||||
|
|
||||||
private IEnumerable<ChainValidationResult> ResolveIgnorableServerCertificateErrors()
|
|
||||||
{
|
|
||||||
if (CustomIgnorableServerCertificateErrorsResolver != null)
|
|
||||||
{
|
|
||||||
return CustomIgnorableServerCertificateErrorsResolver(_options);
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = new List<ChainValidationResult>();
|
|
||||||
|
|
||||||
if (_options.TlsOptions.IgnoreCertificateRevocationErrors)
|
|
||||||
{
|
|
||||||
result.Add(ChainValidationResult.RevocationInformationMissing);
|
|
||||||
//_socket.Control.IgnorableServerCertificateErrors.Add(ChainValidationResult.Revoked); Not supported.
|
|
||||||
result.Add(ChainValidationResult.RevocationFailure);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_options.TlsOptions.IgnoreCertificateChainErrors)
|
|
||||||
{
|
|
||||||
result.Add(ChainValidationResult.IncompleteChain);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_options.TlsOptions.AllowUntrustedCertificates)
|
|
||||||
{
|
|
||||||
result.Add(ChainValidationResult.Untrusted);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
@@ -1,223 +0,0 @@
|
|||||||
#if NET452 || NET461 || NETSTANDARD1_3 || NETSTANDARD2_0
|
|
||||||
using System;
|
|
||||||
using System.Net.Security;
|
|
||||||
using System.Net.Sockets;
|
|
||||||
using System.Security.Authentication;
|
|
||||||
using System.Security.Cryptography.X509Certificates;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using MQTTnet.Channel;
|
|
||||||
using MQTTnet.Client;
|
|
||||||
|
|
||||||
namespace MQTTnet.Implementations
|
|
||||||
{
|
|
||||||
public sealed class MqttTcpChannel : IMqttChannel
|
|
||||||
{
|
|
||||||
#if NET452 || NET461
|
|
||||||
// ReSharper disable once MemberCanBePrivate.Global
|
|
||||||
// ReSharper disable once AutoPropertyCanBeMadeGetOnly.Global
|
|
||||||
public static int BufferSize { get; set; } = 4096 * 20; // Can be changed for fine tuning by library user.
|
|
||||||
|
|
||||||
private readonly int _bufferSize = BufferSize;
|
|
||||||
#else
|
|
||||||
private readonly int _bufferSize = 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
private readonly MqttClientTcpOptions _options;
|
|
||||||
|
|
||||||
private Socket _socket;
|
|
||||||
private SslStream _sslStream;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// called on client sockets are created in connect
|
|
||||||
/// </summary>
|
|
||||||
public MqttTcpChannel(MqttClientTcpOptions options)
|
|
||||||
{
|
|
||||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
|
||||||
_bufferSize = options.BufferSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// called on server, sockets are passed in
|
|
||||||
/// connect will not be called
|
|
||||||
/// </summary>
|
|
||||||
public MqttTcpChannel(Socket socket, SslStream sslStream)
|
|
||||||
{
|
|
||||||
_socket = socket ?? throw new ArgumentNullException(nameof(socket));
|
|
||||||
_sslStream = sslStream;
|
|
||||||
|
|
||||||
CreateStreams();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Stream SendStream { get; private set; }
|
|
||||||
public Stream ReceiveStream { get; private set; }
|
|
||||||
|
|
||||||
public static Func<X509Certificate, X509Chain, SslPolicyErrors, MqttClientTcpOptions, bool> CustomCertificateValidationCallback { get; set; }
|
|
||||||
|
|
||||||
public async Task ConnectAsync()
|
|
||||||
{
|
|
||||||
if (_socket == null)
|
|
||||||
{
|
|
||||||
_socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if NET452 || NET461
|
|
||||||
await Task.Factory.FromAsync(_socket.BeginConnect, _socket.EndConnect, _options.Server, _options.GetPort(), null).ConfigureAwait(false);
|
|
||||||
#else
|
|
||||||
await _socket.ConnectAsync(_options.Server, _options.GetPort()).ConfigureAwait(false);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (_options.TlsOptions.UseTls)
|
|
||||||
{
|
|
||||||
_sslStream = new SslStream(new NetworkStream(_socket, true), false, InternalUserCertificateValidationCallback);
|
|
||||||
await _sslStream.AuthenticateAsClientAsync(_options.Server, LoadCertificates(), SslProtocols.Tls12, _options.TlsOptions.IgnoreCertificateRevocationErrors).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
CreateStreams();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task DisconnectAsync()
|
|
||||||
{
|
|
||||||
Dispose();
|
|
||||||
return Task.FromResult(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
var oneStreamIsUsed = SendStream != null && ReceiveStream != null && ReferenceEquals(SendStream, ReceiveStream);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
SendStream?.Dispose();
|
|
||||||
}
|
|
||||||
catch (ObjectDisposedException)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
catch (NullReferenceException)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
SendStream = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (!oneStreamIsUsed)
|
|
||||||
{
|
|
||||||
ReceiveStream?.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (ObjectDisposedException)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
catch (NullReferenceException)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
ReceiveStream = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_sslStream?.Dispose();
|
|
||||||
}
|
|
||||||
catch (ObjectDisposedException)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
catch (NullReferenceException)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_sslStream = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_socket?.Dispose();
|
|
||||||
}
|
|
||||||
catch (ObjectDisposedException)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
catch (NullReferenceException)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_socket = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool InternalUserCertificateValidationCallback(object sender, X509Certificate x509Certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
|
|
||||||
{
|
|
||||||
if (CustomCertificateValidationCallback != null)
|
|
||||||
{
|
|
||||||
return CustomCertificateValidationCallback(x509Certificate, chain, sslPolicyErrors, _options);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sslPolicyErrors == SslPolicyErrors.None)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (chain.ChainStatus.Any(c => c.Status == X509ChainStatusFlags.RevocationStatusUnknown || c.Status == X509ChainStatusFlags.Revoked || c.Status == X509ChainStatusFlags.RevocationStatusUnknown))
|
|
||||||
{
|
|
||||||
if (!_options.TlsOptions.IgnoreCertificateRevocationErrors)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (chain.ChainStatus.Any(c => c.Status == X509ChainStatusFlags.PartialChain))
|
|
||||||
{
|
|
||||||
if (!_options.TlsOptions.IgnoreCertificateChainErrors)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return _options.TlsOptions.AllowUntrustedCertificates;
|
|
||||||
}
|
|
||||||
|
|
||||||
private X509CertificateCollection LoadCertificates()
|
|
||||||
{
|
|
||||||
var certificates = new X509CertificateCollection();
|
|
||||||
if (_options.TlsOptions.Certificates == null)
|
|
||||||
{
|
|
||||||
return certificates;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var certificate in _options.TlsOptions.Certificates)
|
|
||||||
{
|
|
||||||
certificates.Add(new X509Certificate2(certificate));
|
|
||||||
}
|
|
||||||
|
|
||||||
return certificates;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CreateStreams()
|
|
||||||
{
|
|
||||||
Stream stream;
|
|
||||||
if (_sslStream != null)
|
|
||||||
{
|
|
||||||
stream = _sslStream;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
stream = new NetworkStream(_socket, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if NET452 || NET461
|
|
||||||
SendStream = new BufferedStream(stream, _bufferSize);
|
|
||||||
ReceiveStream = new BufferedStream(stream, _bufferSize);
|
|
||||||
#else
|
|
||||||
SendStream = stream;
|
|
||||||
ReceiveStream = stream;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
#if WINDOWS_UWP
|
|
||||||
using System;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Windows.Networking.Sockets;
|
|
||||||
using MQTTnet.Adapter;
|
|
||||||
using MQTTnet.Diagnostics;
|
|
||||||
using MQTTnet.Serializer;
|
|
||||||
using MQTTnet.Server;
|
|
||||||
|
|
||||||
namespace MQTTnet.Implementations
|
|
||||||
{
|
|
||||||
public class MqttTcpServerAdapter : IMqttServerAdapter, IDisposable
|
|
||||||
{
|
|
||||||
private readonly IMqttNetLogger _logger;
|
|
||||||
private StreamSocketListener _defaultEndpointSocket;
|
|
||||||
|
|
||||||
public MqttTcpServerAdapter(IMqttNetLogger logger)
|
|
||||||
{
|
|
||||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
|
||||||
}
|
|
||||||
|
|
||||||
public event EventHandler<MqttServerAdapterClientAcceptedEventArgs> ClientAccepted;
|
|
||||||
|
|
||||||
public async Task StartAsync(IMqttServerOptions 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 = new MqttChannelAdapter(new MqttTcpChannel(args.Socket), new MqttPacketSerializer(), _logger);
|
|
||||||
ClientAccepted?.Invoke(this, new MqttServerAdapterClientAcceptedEventArgs(clientAdapter));
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
_logger.Error<MqttTcpServerAdapter>(exception, "Error while accepting connection at default endpoint.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
@@ -1,162 +0,0 @@
|
|||||||
#if NET452 || NET461 || 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.Adapter;
|
|
||||||
using MQTTnet.Diagnostics;
|
|
||||||
using MQTTnet.Serializer;
|
|
||||||
using MQTTnet.Server;
|
|
||||||
|
|
||||||
namespace MQTTnet.Implementations
|
|
||||||
{
|
|
||||||
public class MqttTcpServerAdapter : IMqttServerAdapter, IDisposable
|
|
||||||
{
|
|
||||||
private readonly IMqttNetLogger _logger;
|
|
||||||
|
|
||||||
private CancellationTokenSource _cancellationTokenSource;
|
|
||||||
private Socket _defaultEndpointSocket;
|
|
||||||
private Socket _tlsEndpointSocket;
|
|
||||||
private X509Certificate2 _tlsCertificate;
|
|
||||||
|
|
||||||
public MqttTcpServerAdapter(IMqttNetLogger logger)
|
|
||||||
{
|
|
||||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
|
||||||
}
|
|
||||||
|
|
||||||
public event EventHandler<MqttServerAdapterClientAcceptedEventArgs> ClientAccepted;
|
|
||||||
|
|
||||||
public Task StartAsync(IMqttServerOptions 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(options.DefaultEndpointOptions.BoundIPAddress, options.GetDefaultEndpointPort()));
|
|
||||||
_defaultEndpointSocket.Listen(options.ConnectionBacklog);
|
|
||||||
|
|
||||||
Task.Run(async () => await AcceptDefaultEndpointConnectionsAsync(_cancellationTokenSource.Token).ConfigureAwait(false), _cancellationTokenSource.Token).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
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(options.TlsEndpointOptions.BoundIPAddress, options.GetTlsEndpointPort()));
|
|
||||||
_tlsEndpointSocket.Listen(options.ConnectionBacklog);
|
|
||||||
|
|
||||||
Task.Run(async () => await AcceptTlsEndpointConnectionsAsync(_cancellationTokenSource.Token).ConfigureAwait(false), _cancellationTokenSource.Token).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 || NET461
|
|
||||||
var clientSocket = await Task.Factory.FromAsync(_defaultEndpointSocket.BeginAccept, _defaultEndpointSocket.EndAccept, null).ConfigureAwait(false);
|
|
||||||
#else
|
|
||||||
var clientSocket = await _defaultEndpointSocket.AcceptAsync().ConfigureAwait(false);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
var clientAdapter = new MqttChannelAdapter(new MqttTcpChannel(clientSocket, null), new MqttPacketSerializer(), _logger);
|
|
||||||
ClientAccepted?.Invoke(this, new MqttServerAdapterClientAcceptedEventArgs(clientAdapter));
|
|
||||||
}
|
|
||||||
catch (ObjectDisposedException)
|
|
||||||
{
|
|
||||||
// It can happen that the listener socket is accessed after the cancellation token is already set and the listener socket is disposed.
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
if (exception is SocketException s && s.SocketErrorCode == SocketError.OperationAborted)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_logger.Error<MqttTcpServerAdapter>(exception, "Error while accepting connection at default endpoint.");
|
|
||||||
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task AcceptTlsEndpointConnectionsAsync(CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
while (!cancellationToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
#if NET452 || NET461
|
|
||||||
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 = new MqttChannelAdapter(new MqttTcpChannel(clientSocket, sslStream), new MqttPacketSerializer(), _logger);
|
|
||||||
ClientAccepted?.Invoke(this, new MqttServerAdapterClientAcceptedEventArgs(clientAdapter));
|
|
||||||
}
|
|
||||||
catch (ObjectDisposedException)
|
|
||||||
{
|
|
||||||
// It can happen that the listener socket is accessed after the cancellation token is already set and the listener socket is disposed.
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
if (exception is SocketException s && s.SocketErrorCode == SocketError.OperationAborted)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_logger.Error<MqttTcpServerAdapter>(exception, "Error while accepting connection at TLS endpoint.");
|
|
||||||
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
@@ -1,112 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Net.WebSockets;
|
|
||||||
using System.Security.Cryptography.X509Certificates;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using MQTTnet.Channel;
|
|
||||||
using MQTTnet.Client;
|
|
||||||
|
|
||||||
namespace MQTTnet.Implementations
|
|
||||||
{
|
|
||||||
public sealed class MqttWebSocketChannel : IMqttChannel
|
|
||||||
{
|
|
||||||
// ReSharper disable once MemberCanBePrivate.Global
|
|
||||||
// ReSharper disable once AutoPropertyCanBeMadeGetOnly.Global
|
|
||||||
public static int BufferSize { get; set; } = 4096 * 20; // Can be changed for fine tuning by library user.
|
|
||||||
|
|
||||||
private readonly MqttClientWebSocketOptions _options;
|
|
||||||
private ClientWebSocket _webSocket;
|
|
||||||
|
|
||||||
public MqttWebSocketChannel(MqttClientWebSocketOptions options)
|
|
||||||
{
|
|
||||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Stream SendStream { get; private set; }
|
|
||||||
public Stream ReceiveStream { get; private set; }
|
|
||||||
|
|
||||||
public async Task ConnectAsync()
|
|
||||||
{
|
|
||||||
var uri = _options.Uri;
|
|
||||||
if (!uri.StartsWith("ws://", StringComparison.OrdinalIgnoreCase) && !uri.StartsWith("wss://", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
if (_options.TlsOptions?.UseTls == false)
|
|
||||||
{
|
|
||||||
uri = "ws://" + uri;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
uri = "wss://" + 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);
|
|
||||||
|
|
||||||
SendStream = new WebSocketStream(_webSocket);
|
|
||||||
ReceiveStream = SendStream;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task DisconnectAsync()
|
|
||||||
{
|
|
||||||
if (_webSocket == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_webSocket.State == WebSocketState.Open || _webSocket.State == WebSocketState.Connecting)
|
|
||||||
{
|
|
||||||
await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_webSocket?.Dispose();
|
|
||||||
}
|
|
||||||
catch (ObjectDisposedException)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_webSocket = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,137 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Net.WebSockets;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using MQTTnet.Exceptions;
|
|
||||||
|
|
||||||
namespace MQTTnet.Implementations
|
|
||||||
{
|
|
||||||
public class WebSocketStream : Stream
|
|
||||||
{
|
|
||||||
private readonly byte[] _chunckBuffer = new byte[MqttWebSocketChannel.BufferSize];
|
|
||||||
private readonly Queue<byte> _buffer = new Queue<byte>(MqttWebSocketChannel.BufferSize);
|
|
||||||
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 Task FlushAsync(CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
return Task.FromResult(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int Read(byte[] buffer, int offset, int count)
|
|
||||||
{
|
|
||||||
return ReadAsync(buffer, offset, count).GetAwaiter().GetResult();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
var bytesRead = 0;
|
|
||||||
|
|
||||||
// Use existing date from buffer.
|
|
||||||
while (count > 0 && _buffer.Any())
|
|
||||||
{
|
|
||||||
buffer[offset] = _buffer.Dequeue();
|
|
||||||
count--;
|
|
||||||
bytesRead++;
|
|
||||||
offset++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count == 0)
|
|
||||||
{
|
|
||||||
return bytesRead;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch new data if the buffer is not full.
|
|
||||||
while (_webSocket.State == WebSocketState.Open)
|
|
||||||
{
|
|
||||||
await FetchChunkAsync(cancellationToken).ConfigureAwait(false);
|
|
||||||
|
|
||||||
while (count > 0 && _buffer.Any())
|
|
||||||
{
|
|
||||||
buffer[offset] = _buffer.Dequeue();
|
|
||||||
count--;
|
|
||||||
bytesRead++;
|
|
||||||
offset++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count == 0)
|
|
||||||
{
|
|
||||||
return bytesRead;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_webSocket.State == WebSocketState.Closed)
|
|
||||||
{
|
|
||||||
throw new MqttCommunicationException("WebSocket connection closed.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return bytesRead;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Write(byte[] buffer, int offset, int count)
|
|
||||||
{
|
|
||||||
WriteAsync(buffer, offset, count).GetAwaiter().GetResult();
|
|
||||||
}
|
|
||||||
|
|
||||||
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 long Seek(long offset, SeekOrigin origin)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void SetLength(long value)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task FetchChunkAsync(CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
var response = await _webSocket.ReceiveAsync(new ArraySegment<byte>(_chunckBuffer, 0, _chunckBuffer.Length), cancellationToken).ConfigureAwait(false);
|
|
||||||
|
|
||||||
if (response.MessageType == WebSocketMessageType.Close)
|
|
||||||
{
|
|
||||||
await _webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, cancellationToken).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
else if (response.MessageType == WebSocketMessageType.Binary)
|
|
||||||
{
|
|
||||||
for (var i = 0; i < response.Count; i++)
|
|
||||||
{
|
|
||||||
_buffer.Enqueue(_chunckBuffer[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (response.MessageType == WebSocketMessageType.Text)
|
|
||||||
{
|
|
||||||
throw new MqttProtocolViolationException("WebSocket channel received TEXT message.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
using MQTTnet.Packets;
|
|
||||||
|
|
||||||
namespace MQTTnet.Internal
|
|
||||||
{
|
|
||||||
internal static class MqttApplicationMessageExtensions
|
|
||||||
{
|
|
||||||
public static MqttApplicationMessage ToApplicationMessage(this MqttPublishPacket publishPacket)
|
|
||||||
{
|
|
||||||
return new MqttApplicationMessage
|
|
||||||
{
|
|
||||||
Topic = publishPacket.Topic,
|
|
||||||
Payload = publishPacket.Payload,
|
|
||||||
QualityOfServiceLevel = publishPacket.QualityOfServiceLevel,
|
|
||||||
Retain = publishPacket.Retain
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static MqttPublishPacket ToPublishPacket(this MqttApplicationMessage applicationMessage)
|
|
||||||
{
|
|
||||||
if (applicationMessage == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new MqttPublishPacket
|
|
||||||
{
|
|
||||||
Topic = applicationMessage.Topic,
|
|
||||||
Payload = applicationMessage.Payload,
|
|
||||||
QualityOfServiceLevel = applicationMessage.QualityOfServiceLevel,
|
|
||||||
Retain = applicationMessage.Retain,
|
|
||||||
Dup = false
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using MQTTnet.Exceptions;
|
|
||||||
|
|
||||||
namespace MQTTnet.Internal
|
|
||||||
{
|
|
||||||
public static class TaskExtensions
|
|
||||||
{
|
|
||||||
public static async Task TimeoutAfter(this Task task, TimeSpan timeout)
|
|
||||||
{
|
|
||||||
if (task == null) throw new ArgumentNullException(nameof(task));
|
|
||||||
|
|
||||||
using (var timeoutCts = new CancellationTokenSource())
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var timeoutTask = Task.Delay(timeout, timeoutCts.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
|
|
||||||
{
|
|
||||||
timeoutCts.Cancel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task<TResult> TimeoutAfter<TResult>(this Task<TResult> task, TimeSpan timeout)
|
|
||||||
{
|
|
||||||
if (task == null) throw new ArgumentNullException(nameof(task));
|
|
||||||
|
|
||||||
using (var timeoutCts = new CancellationTokenSource())
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var timeoutTask = Task.Delay(timeout, timeoutCts.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
|
|
||||||
{
|
|
||||||
timeoutCts.Cancel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">netstandard1.3;netstandard2.0;net452;net461;uap10.0</TargetFrameworks>
|
|
||||||
<TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">netstandard1.3;netstandard2.0</TargetFrameworks>
|
|
||||||
<AssemblyName>MQTTnet</AssemblyName>
|
|
||||||
<RootNamespace>MQTTnet</RootNamespace>
|
|
||||||
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
|
|
||||||
<DebugType>Full</DebugType>
|
|
||||||
<AssemblyVersion>0.0.0.0</AssemblyVersion>
|
|
||||||
<FileVersion>0.0.0.0</FileVersion>
|
|
||||||
<Version>0.0.0.0</Version>
|
|
||||||
<Company />
|
|
||||||
<Product />
|
|
||||||
<Description />
|
|
||||||
<Authors />
|
|
||||||
<PackageId />
|
|
||||||
<SignAssembly>false</SignAssembly>
|
|
||||||
<DelaySign>false</DelaySign>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(TargetFramework)' == 'uap10.0'">
|
|
||||||
<CopyLocalLockFileAssemblies>false</CopyLocalLockFileAssemblies>
|
|
||||||
<NugetTargetMoniker>UAP,Version=v10.0</NugetTargetMoniker>
|
|
||||||
<TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
|
|
||||||
<TargetPlatformVersion>10.0.16299.0</TargetPlatformVersion>
|
|
||||||
<TargetPlatformMinVersion>10.0.10240.0</TargetPlatformMinVersion>
|
|
||||||
<TargetFrameworkIdentifier>.NETCore</TargetFrameworkIdentifier>
|
|
||||||
<TargetFrameworkVersion>v5.0</TargetFrameworkVersion>
|
|
||||||
<DefineConstants>$(DefineConstants);WINDOWS_UWP</DefineConstants>
|
|
||||||
<DefaultLanguage>en</DefaultLanguage>
|
|
||||||
<LanguageTargets>$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets</LanguageTargets>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x86'" />
|
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" />
|
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|netstandard1.3|AnyCPU'">
|
|
||||||
<DefineConstants>RELEASE;NETSTANDARD1_3</DefineConstants>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFramework)'=='netstandard2.0'">
|
|
||||||
<PackageReference Include="System.Net.Security" Version="4.3.2" />
|
|
||||||
<PackageReference Include="System.Net.WebSockets" Version="4.3.0" />
|
|
||||||
<PackageReference Include="System.Net.WebSockets.Client" Version="4.3.1" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFramework)'=='netstandard1.3'">
|
|
||||||
<PackageReference Include="System.Net.Security" Version="4.3.2" />
|
|
||||||
<PackageReference Include="System.Net.WebSockets" Version="4.3.0" />
|
|
||||||
<PackageReference Include="System.Net.WebSockets.Client" Version="4.3.1" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFramework)'=='uap10.0'">
|
|
||||||
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform" Version="5.4.1" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFramework)'=='net452'">
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup Condition="'$(TargetFramework)'=='net461'">
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace MQTTnet.ManagedClient
|
|
||||||
{
|
|
||||||
public class ApplicationMessageProcessedEventArgs : EventArgs
|
|
||||||
{
|
|
||||||
public ApplicationMessageProcessedEventArgs(MqttApplicationMessage applicationMessage, Exception exception)
|
|
||||||
{
|
|
||||||
ApplicationMessage = applicationMessage ?? throw new ArgumentNullException(nameof(applicationMessage));
|
|
||||||
Exception = exception;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MqttApplicationMessage ApplicationMessage { get; }
|
|
||||||
public Exception Exception { get; }
|
|
||||||
|
|
||||||
public bool HasFailed => Exception != null;
|
|
||||||
public bool HasSucceeded => Exception == null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using MQTTnet.Client;
|
|
||||||
|
|
||||||
namespace MQTTnet.ManagedClient
|
|
||||||
{
|
|
||||||
public interface IManagedMqttClient : IApplicationMessageReceiver, IApplicationMessagePublisher, IDisposable
|
|
||||||
{
|
|
||||||
bool IsStarted { get; }
|
|
||||||
bool IsConnected { get; }
|
|
||||||
|
|
||||||
event EventHandler<MqttClientConnectedEventArgs> Connected;
|
|
||||||
event EventHandler<MqttClientDisconnectedEventArgs> Disconnected;
|
|
||||||
|
|
||||||
event EventHandler<ApplicationMessageProcessedEventArgs> ApplicationMessageProcessed;
|
|
||||||
|
|
||||||
Task StartAsync(IManagedMqttClientOptions options);
|
|
||||||
Task StopAsync();
|
|
||||||
|
|
||||||
Task SubscribeAsync(IEnumerable<TopicFilter> topicFilters);
|
|
||||||
Task UnsubscribeAsync(IEnumerable<string> topics);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
using System;
|
|
||||||
using MQTTnet.Client;
|
|
||||||
|
|
||||||
namespace MQTTnet.ManagedClient
|
|
||||||
{
|
|
||||||
public interface IManagedMqttClientOptions
|
|
||||||
{
|
|
||||||
IMqttClientOptions ClientOptions { get; }
|
|
||||||
|
|
||||||
TimeSpan AutoReconnectDelay { get; }
|
|
||||||
|
|
||||||
IManagedMqttClientStorage Storage { get; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace MQTTnet.ManagedClient
|
|
||||||
{
|
|
||||||
public interface IManagedMqttClientStorage
|
|
||||||
{
|
|
||||||
Task SaveQueuedMessagesAsync(IList<MqttApplicationMessage> messages);
|
|
||||||
|
|
||||||
Task<IList<MqttApplicationMessage>> LoadQueuedMessagesAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,396 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using MQTTnet.Client;
|
|
||||||
using MQTTnet.Diagnostics;
|
|
||||||
using MQTTnet.Exceptions;
|
|
||||||
using MQTTnet.Protocol;
|
|
||||||
|
|
||||||
namespace MQTTnet.ManagedClient
|
|
||||||
{
|
|
||||||
public class ManagedMqttClient : IManagedMqttClient
|
|
||||||
{
|
|
||||||
private readonly BlockingCollection<MqttApplicationMessage> _messageQueue = new BlockingCollection<MqttApplicationMessage>();
|
|
||||||
private readonly Dictionary<string, MqttQualityOfServiceLevel> _subscriptions = new Dictionary<string, MqttQualityOfServiceLevel>();
|
|
||||||
private readonly SemaphoreSlim _subscriptionsSemaphore = new SemaphoreSlim(1, 1);
|
|
||||||
private readonly List<string> _unsubscriptions = new List<string>();
|
|
||||||
|
|
||||||
private readonly IMqttClient _mqttClient;
|
|
||||||
private readonly IMqttNetLogger _logger;
|
|
||||||
|
|
||||||
private CancellationTokenSource _connectionCancellationToken;
|
|
||||||
private CancellationTokenSource _publishingCancellationToken;
|
|
||||||
|
|
||||||
private ManagedMqttClientStorageManager _storageManager;
|
|
||||||
private IManagedMqttClientOptions _options;
|
|
||||||
|
|
||||||
private bool _subscriptionsNotPushed;
|
|
||||||
|
|
||||||
public ManagedMqttClient(IMqttClient mqttClient, IMqttNetLogger logger)
|
|
||||||
{
|
|
||||||
_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 bool IsStarted => _connectionCancellationToken != null;
|
|
||||||
|
|
||||||
public event EventHandler<MqttClientConnectedEventArgs> Connected;
|
|
||||||
public event EventHandler<MqttClientDisconnectedEventArgs> Disconnected;
|
|
||||||
public event EventHandler<MqttApplicationMessageReceivedEventArgs> ApplicationMessageReceived;
|
|
||||||
public event EventHandler<ApplicationMessageProcessedEventArgs> ApplicationMessageProcessed;
|
|
||||||
public event EventHandler SynchronizingSubscriptionsFailed;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
if (_options.Storage != null)
|
|
||||||
{
|
|
||||||
_storageManager = new ManagedMqttClientStorageManager(_options.Storage);
|
|
||||||
await _storageManager.LoadQueuedMessagesAsync().ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
_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.Run(async () => await MaintainConnectionAsync(_connectionCancellationToken.Token).ConfigureAwait(false), _connectionCancellationToken.Token).ConfigureAwait(false);
|
|
||||||
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
|
||||||
|
|
||||||
_logger.Info<ManagedMqttClient>("Started");
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task StopAsync()
|
|
||||||
{
|
|
||||||
StopPublishing();
|
|
||||||
StopMaintainingConnection();
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
if (_storageManager != null)
|
|
||||||
{
|
|
||||||
await _storageManager.AddAsync(applicationMessage).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
_messageQueue.Add(applicationMessage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task SubscribeAsync(IEnumerable<TopicFilter> topicFilters)
|
|
||||||
{
|
|
||||||
if (topicFilters == null) throw new ArgumentNullException(nameof(topicFilters));
|
|
||||||
|
|
||||||
await _subscriptionsSemaphore.WaitAsync().ConfigureAwait(false);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
foreach (var topicFilter in topicFilters)
|
|
||||||
{
|
|
||||||
_subscriptions[topicFilter.Topic] = topicFilter.QualityOfServiceLevel;
|
|
||||||
_subscriptionsNotPushed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_subscriptionsSemaphore.Release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task UnsubscribeAsync(IEnumerable<string> topics)
|
|
||||||
{
|
|
||||||
await _subscriptionsSemaphore.WaitAsync().ConfigureAwait(false);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
foreach (var topic in topics)
|
|
||||||
{
|
|
||||||
if (_subscriptions.Remove(topic))
|
|
||||||
{
|
|
||||||
_unsubscriptions.Add(topic);
|
|
||||||
_subscriptionsNotPushed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_subscriptionsSemaphore.Release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_messageQueue?.Dispose();
|
|
||||||
_subscriptionsSemaphore?.Dispose();
|
|
||||||
_connectionCancellationToken?.Dispose();
|
|
||||||
_publishingCancellationToken?.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task MaintainConnectionAsync(CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
while (!cancellationToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
await TryMaintainConnectionAsync(cancellationToken).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (OperationCanceledException)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
_logger.Error<ManagedMqttClient>(exception, "Unhandled exception while maintaining connection.");
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
await _mqttClient.DisconnectAsync().ConfigureAwait(false);
|
|
||||||
_logger.Info<ManagedMqttClient>("Stopped");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task TryMaintainConnectionAsync(CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var connectionState = await ReconnectIfRequiredAsync().ConfigureAwait(false);
|
|
||||||
if (connectionState == ReconnectionResult.NotConnected)
|
|
||||||
{
|
|
||||||
StopPublishing();
|
|
||||||
await Task.Delay(_options.AutoReconnectDelay, cancellationToken).ConfigureAwait(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (connectionState == ReconnectionResult.Reconnected || _subscriptionsNotPushed)
|
|
||||||
{
|
|
||||||
await SynchronizeSubscriptionsAsync().ConfigureAwait(false);
|
|
||||||
|
|
||||||
StartPublishing();
|
|
||||||
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (connectionState == ReconnectionResult.StillConnected)
|
|
||||||
{
|
|
||||||
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (OperationCanceledException)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
catch (MqttCommunicationException exception)
|
|
||||||
{
|
|
||||||
_logger.Warning<ManagedMqttClient>(exception, "Communication exception while maintaining connection.");
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
_logger.Error<ManagedMqttClient>(exception, "Unhandled exception while maintaining connection.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (OperationCanceledException)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
_logger.Error<ManagedMqttClient>(exception, "Unhandled exception while publishing queued application messages.");
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_logger.Verbose<ManagedMqttClient>("Stopped publishing messages.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task TryPublishQueuedMessageAsync(MqttApplicationMessage message)
|
|
||||||
{
|
|
||||||
Exception transmitException = null;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await _mqttClient.PublishAsync(message).ConfigureAwait(false);
|
|
||||||
|
|
||||||
if (_storageManager != null)
|
|
||||||
{
|
|
||||||
await _storageManager.RemoveAsync(message).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (MqttCommunicationException exception)
|
|
||||||
{
|
|
||||||
transmitException = exception;
|
|
||||||
|
|
||||||
_logger.Warning<ManagedMqttClient>(exception, "Publishing application message failed.");
|
|
||||||
|
|
||||||
if (message.QualityOfServiceLevel > MqttQualityOfServiceLevel.AtMostOnce)
|
|
||||||
{
|
|
||||||
_messageQueue.Add(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
transmitException = exception;
|
|
||||||
_logger.Error<ManagedMqttClient>(exception, "Unhandled exception while publishing queued application message.");
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
ApplicationMessageProcessed?.Invoke(this, new ApplicationMessageProcessedEventArgs(message, transmitException));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task SynchronizeSubscriptionsAsync()
|
|
||||||
{
|
|
||||||
_logger.Info<ManagedMqttClient>(nameof(ManagedMqttClient), "Synchronizing subscriptions");
|
|
||||||
|
|
||||||
List<TopicFilter> subscriptions;
|
|
||||||
List<string> unsubscriptions;
|
|
||||||
|
|
||||||
await _subscriptionsSemaphore.WaitAsync().ConfigureAwait(false);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
subscriptions = _subscriptions.Select(i => new TopicFilter(i.Key, i.Value)).ToList();
|
|
||||||
|
|
||||||
unsubscriptions = new List<string>(_unsubscriptions);
|
|
||||||
_unsubscriptions.Clear();
|
|
||||||
|
|
||||||
_subscriptionsNotPushed = false;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_subscriptionsSemaphore.Release();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!subscriptions.Any() && !unsubscriptions.Any())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (subscriptions.Any())
|
|
||||||
{
|
|
||||||
await _mqttClient.SubscribeAsync(subscriptions).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (unsubscriptions.Any())
|
|
||||||
{
|
|
||||||
await _mqttClient.UnsubscribeAsync(unsubscriptions).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
_logger.Warning<ManagedMqttClient>(exception, "Synchronizing subscriptions failed.");
|
|
||||||
_subscriptionsNotPushed = true;
|
|
||||||
|
|
||||||
SynchronizingSubscriptionsFailed?.Invoke(this, EventArgs.Empty);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void StartPublishing()
|
|
||||||
{
|
|
||||||
if (_publishingCancellationToken != null)
|
|
||||||
{
|
|
||||||
StopPublishing();
|
|
||||||
}
|
|
||||||
|
|
||||||
var cts = new CancellationTokenSource();
|
|
||||||
|
|
||||||
_publishingCancellationToken = cts;
|
|
||||||
|
|
||||||
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
|
|
||||||
Task.Run(async () => await PublishQueuedMessagesAsync(cts.Token).ConfigureAwait(false), cts.Token).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 StopPublishing()
|
|
||||||
{
|
|
||||||
_publishingCancellationToken?.Cancel(false);
|
|
||||||
_publishingCancellationToken?.Dispose();
|
|
||||||
_publishingCancellationToken = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void StopMaintainingConnection()
|
|
||||||
{
|
|
||||||
_connectionCancellationToken?.Cancel(false);
|
|
||||||
_connectionCancellationToken?.Dispose();
|
|
||||||
_connectionCancellationToken = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using MQTTnet.Protocol;
|
|
||||||
|
|
||||||
namespace MQTTnet.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 SubscribeAsync(this IManagedMqttClient managedClient, string topic, MqttQualityOfServiceLevel qualityOfServiceLevel)
|
|
||||||
{
|
|
||||||
if (managedClient == null) throw new ArgumentNullException(nameof(managedClient));
|
|
||||||
if (topic == null) throw new ArgumentNullException(nameof(topic));
|
|
||||||
|
|
||||||
return managedClient.SubscribeAsync(new TopicFilterBuilder().WithTopic(topic).WithQualityOfServiceLevel(qualityOfServiceLevel).Build());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Task SubscribeAsync(this IManagedMqttClient managedClient, string topic)
|
|
||||||
{
|
|
||||||
if (managedClient == null) throw new ArgumentNullException(nameof(managedClient));
|
|
||||||
if (topic == null) throw new ArgumentNullException(nameof(topic));
|
|
||||||
|
|
||||||
return managedClient.SubscribeAsync(new TopicFilterBuilder().WithTopic(topic).Build());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Task UnsubscribeAsync(this IManagedMqttClient managedClient, params string[] topicFilters)
|
|
||||||
{
|
|
||||||
if (managedClient == null) throw new ArgumentNullException(nameof(managedClient));
|
|
||||||
|
|
||||||
return managedClient.UnsubscribeAsync(topicFilters);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
using System;
|
|
||||||
using MQTTnet.Client;
|
|
||||||
|
|
||||||
namespace MQTTnet.ManagedClient
|
|
||||||
{
|
|
||||||
public class ManagedMqttClientOptions : IManagedMqttClientOptions
|
|
||||||
{
|
|
||||||
public IMqttClientOptions ClientOptions { get; set; }
|
|
||||||
|
|
||||||
public TimeSpan AutoReconnectDelay { get; set; } = TimeSpan.FromSeconds(5);
|
|
||||||
|
|
||||||
public IManagedMqttClientStorage Storage { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace MQTTnet.ManagedClient
|
|
||||||
{
|
|
||||||
public class ManagedMqttClientStorageManager
|
|
||||||
{
|
|
||||||
private readonly List<MqttApplicationMessage> _applicationMessages = new List<MqttApplicationMessage>();
|
|
||||||
private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
|
|
||||||
private readonly IManagedMqttClientStorage _storage;
|
|
||||||
|
|
||||||
public ManagedMqttClientStorageManager(IManagedMqttClientStorage storage)
|
|
||||||
{
|
|
||||||
_storage = storage ?? throw new ArgumentNullException(nameof(storage));
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task LoadQueuedMessagesAsync()
|
|
||||||
{
|
|
||||||
var loadedMessages = await _storage.LoadQueuedMessagesAsync().ConfigureAwait(false);
|
|
||||||
foreach (var loadedMessage in loadedMessages)
|
|
||||||
{
|
|
||||||
_applicationMessages.Add(loadedMessage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task AddAsync(MqttApplicationMessage applicationMessage)
|
|
||||||
{
|
|
||||||
await _semaphore.WaitAsync().ConfigureAwait(false);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_applicationMessages.Add(applicationMessage);
|
|
||||||
await SaveAsync().ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_semaphore.Release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task RemoveAsync(MqttApplicationMessage applicationMessage)
|
|
||||||
{
|
|
||||||
await _semaphore.WaitAsync().ConfigureAwait(false);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
namespace MQTTnet.ManagedClient
|
|
||||||
{
|
|
||||||
public enum ReconnectionResult
|
|
||||||
{
|
|
||||||
StillConnected,
|
|
||||||
Reconnected,
|
|
||||||
NotConnected
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
using System;
|
|
||||||
using MQTTnet.Protocol;
|
|
||||||
|
|
||||||
namespace MQTTnet
|
|
||||||
{
|
|
||||||
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,114 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using MQTTnet.Exceptions;
|
|
||||||
using MQTTnet.Protocol;
|
|
||||||
|
|
||||||
namespace MQTTnet
|
|
||||||
{
|
|
||||||
public class MqttApplicationMessageBuilder
|
|
||||||
{
|
|
||||||
private MqttQualityOfServiceLevel _qualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce;
|
|
||||||
private string _topic;
|
|
||||||
private byte[] _payload;
|
|
||||||
private bool _retain;
|
|
||||||
|
|
||||||
public MqttApplicationMessageBuilder WithTopic(string topic)
|
|
||||||
{
|
|
||||||
_topic = topic;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MqttApplicationMessageBuilder WithPayload(IEnumerable<byte> payload)
|
|
||||||
{
|
|
||||||
if (payload == null)
|
|
||||||
{
|
|
||||||
_payload = null;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
_payload = payload.ToArray();
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MqttApplicationMessageBuilder WithPayload(MemoryStream payload)
|
|
||||||
{
|
|
||||||
if (payload == null)
|
|
||||||
{
|
|
||||||
_payload = null;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (payload.Length == 0)
|
|
||||||
{
|
|
||||||
_payload = new byte[0];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_payload = new byte[payload.Length - payload.Position];
|
|
||||||
payload.Read(_payload, 0, _payload.Length);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MqttApplicationMessageBuilder WithPayload(string payload)
|
|
||||||
{
|
|
||||||
if (payload == null)
|
|
||||||
{
|
|
||||||
_payload = null;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
_payload = string.IsNullOrEmpty(payload) ? new byte[0] : Encoding.UTF8.GetBytes(payload);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MqttApplicationMessageBuilder WithQualityOfServiceLevel(MqttQualityOfServiceLevel qualityOfServiceLevel)
|
|
||||||
{
|
|
||||||
_qualityOfServiceLevel = qualityOfServiceLevel;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MqttApplicationMessageBuilder WithRetainFlag(bool value = true)
|
|
||||||
{
|
|
||||||
_retain = value;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MqttApplicationMessageBuilder WithAtLeastOnceQoS()
|
|
||||||
{
|
|
||||||
_qualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MqttApplicationMessageBuilder WithAtMostOnceQoS()
|
|
||||||
{
|
|
||||||
_qualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MqttApplicationMessageBuilder WithExactlyOnceQoS()
|
|
||||||
{
|
|
||||||
_qualityOfServiceLevel = MqttQualityOfServiceLevel.ExactlyOnce;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MqttApplicationMessage Build()
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(_topic))
|
|
||||||
{
|
|
||||||
throw new MqttProtocolViolationException("Topic is not set.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return new MqttApplicationMessage
|
|
||||||
{
|
|
||||||
Topic = _topic,
|
|
||||||
Payload = _payload ?? new byte[0],
|
|
||||||
QualityOfServiceLevel = _qualityOfServiceLevel,
|
|
||||||
Retain = _retain
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace MQTTnet
|
|
||||||
{
|
|
||||||
public sealed class MqttApplicationMessageReceivedEventArgs : EventArgs
|
|
||||||
{
|
|
||||||
public MqttApplicationMessageReceivedEventArgs(string clientId, MqttApplicationMessage applicationMessage)
|
|
||||||
{
|
|
||||||
ClientId = clientId;
|
|
||||||
ApplicationMessage = applicationMessage ?? throw new ArgumentNullException(nameof(applicationMessage));
|
|
||||||
}
|
|
||||||
|
|
||||||
public string ClientId { get; }
|
|
||||||
|
|
||||||
public MqttApplicationMessage ApplicationMessage { get; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using MQTTnet.Adapter;
|
|
||||||
using MQTTnet.Client;
|
|
||||||
using MQTTnet.Diagnostics;
|
|
||||||
using MQTTnet.Implementations;
|
|
||||||
using MQTTnet.ManagedClient;
|
|
||||||
using MQTTnet.Server;
|
|
||||||
|
|
||||||
namespace MQTTnet
|
|
||||||
{
|
|
||||||
public class MqttFactory : IMqttClientFactory, IMqttServerFactory
|
|
||||||
{
|
|
||||||
public IMqttClient CreateMqttClient()
|
|
||||||
{
|
|
||||||
return CreateMqttClient(new MqttNetLogger());
|
|
||||||
}
|
|
||||||
|
|
||||||
public IMqttClient CreateMqttClient(IMqttNetLogger logger)
|
|
||||||
{
|
|
||||||
if (logger == null) throw new ArgumentNullException(nameof(logger));
|
|
||||||
|
|
||||||
return new MqttClient(new MqttClientAdapterFactory(), logger);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IManagedMqttClient CreateManagedMqttClient()
|
|
||||||
{
|
|
||||||
return new ManagedMqttClient(CreateMqttClient(), new MqttNetLogger());
|
|
||||||
}
|
|
||||||
|
|
||||||
public IManagedMqttClient CreateManagedMqttClient(IMqttNetLogger logger)
|
|
||||||
{
|
|
||||||
if (logger == null) throw new ArgumentNullException(nameof(logger));
|
|
||||||
|
|
||||||
return new ManagedMqttClient(CreateMqttClient(), logger);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IMqttServer CreateMqttServer()
|
|
||||||
{
|
|
||||||
var logger = new MqttNetLogger();
|
|
||||||
return CreateMqttServer(logger);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IMqttServer CreateMqttServer(IMqttNetLogger logger)
|
|
||||||
{
|
|
||||||
if (logger == null) throw new ArgumentNullException(nameof(logger));
|
|
||||||
|
|
||||||
return CreateMqttServer(new List<IMqttServerAdapter> { new MqttTcpServerAdapter(logger) }, logger);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IMqttServer CreateMqttServer(IEnumerable<IMqttServerAdapter> adapters, IMqttNetLogger logger)
|
|
||||||
{
|
|
||||||
if (adapters == null) throw new ArgumentNullException(nameof(adapters));
|
|
||||||
if (logger == null) throw new ArgumentNullException(nameof(logger));
|
|
||||||
|
|
||||||
return new MqttServer(adapters, logger);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
namespace MQTTnet.Packets
|
|
||||||
{
|
|
||||||
public interface IMqttPacketWithIdentifier
|
|
||||||
{
|
|
||||||
ushort? PacketIdentifier { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
namespace MQTTnet.Packets
|
|
||||||
{
|
|
||||||
public abstract class MqttBasePacket
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
namespace MQTTnet.Packets
|
|
||||||
{
|
|
||||||
public class MqttBasePublishPacket : MqttBasePacket, IMqttPacketWithIdentifier
|
|
||||||
{
|
|
||||||
public ushort? PacketIdentifier { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
using MQTTnet.Protocol;
|
|
||||||
|
|
||||||
namespace MQTTnet.Packets
|
|
||||||
{
|
|
||||||
public sealed class MqttConnAckPacket : MqttBasePacket
|
|
||||||
{
|
|
||||||
public bool IsSessionPresent { get; set; }
|
|
||||||
|
|
||||||
public MqttConnectReturnCode ConnectReturnCode { get; set; }
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return "ConnAck: [ConnectReturnCode=" + ConnectReturnCode + "] [IsSessionPresent=" + IsSessionPresent + "]";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
using MQTTnet.Serializer;
|
|
||||||
|
|
||||||
namespace MQTTnet.Packets
|
|
||||||
{
|
|
||||||
public sealed class MqttConnectPacket : MqttBasePacket
|
|
||||||
{
|
|
||||||
public MqttProtocolVersion ProtocolVersion { get; set; }
|
|
||||||
|
|
||||||
public string ClientId { get; set; }
|
|
||||||
|
|
||||||
public string Username { get; set; }
|
|
||||||
|
|
||||||
public string Password { get; set; }
|
|
||||||
|
|
||||||
public ushort KeepAlivePeriod { get; set; }
|
|
||||||
|
|
||||||
public bool CleanSession { get; set; }
|
|
||||||
|
|
||||||
public MqttApplicationMessage WillMessage { get; set; }
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return "Connect: [ClientId=" + ClientId + "] [Username=" + Username + "] [Password=" + Password + "] [KeepAlivePeriod=" + KeepAlivePeriod + "] [CleanSession=" + CleanSession + "]";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
namespace MQTTnet.Packets
|
|
||||||
{
|
|
||||||
public sealed class MqttDisconnectPacket : MqttBasePacket
|
|
||||||
{
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return "Disconnect";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
using MQTTnet.Protocol;
|
|
||||||
|
|
||||||
namespace MQTTnet.Packets
|
|
||||||
{
|
|
||||||
public class MqttPacketHeader
|
|
||||||
{
|
|
||||||
public MqttControlPacketType ControlPacketType { get; set; }
|
|
||||||
|
|
||||||
public byte FixedHeader { get; set; }
|
|
||||||
|
|
||||||
public int BodyLength { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
namespace MQTTnet.Packets
|
|
||||||
{
|
|
||||||
public sealed class MqttPingReqPacket : MqttBasePacket
|
|
||||||
{
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return "PingReq";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
namespace MQTTnet.Packets
|
|
||||||
{
|
|
||||||
public sealed class MqttPingRespPacket : MqttBasePacket
|
|
||||||
{
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return "PingResp";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
namespace MQTTnet.Packets
|
|
||||||
{
|
|
||||||
public sealed class MqttPubAckPacket : MqttBasePublishPacket
|
|
||||||
{
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return "PubAck";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
namespace MQTTnet.Packets
|
|
||||||
{
|
|
||||||
public sealed class MqttPubCompPacket : MqttBasePublishPacket
|
|
||||||
{
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return "PubComp";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
namespace MQTTnet.Packets
|
|
||||||
{
|
|
||||||
public sealed class MqttPubRecPacket : MqttBasePublishPacket
|
|
||||||
{
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return "PubRec";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user