Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
948c7c2b69 | ||
|
|
1cdc343e6d | ||
|
|
27771f0146 | ||
|
|
e7de2b913a | ||
|
|
3dd3279656 | ||
|
|
c5934bf18b | ||
|
|
7715bf37dc | ||
|
|
9dbe1e4b12 | ||
|
|
f13b3eb6bb | ||
|
|
8eb884fa5d | ||
|
|
9f81a2bb71 | ||
|
|
e2f8d0f0ac | ||
|
|
981ea02149 | ||
|
|
8566fd5395 | ||
|
|
8089c6bdbc | ||
|
|
ef5b6c9b32 | ||
|
|
4aa4a12e47 | ||
|
|
6bf2b4eaca | ||
|
|
6ef6b57064 | ||
|
|
7d4027958c | ||
|
|
bdb74642c2 | ||
|
|
63d85184e1 | ||
|
|
0506857f0d | ||
|
|
6417baa081 | ||
|
|
74bb68ad73 | ||
|
|
81a4e7cb86 | ||
|
|
2fdbb02dc8 | ||
|
|
c4eb05105b | ||
|
|
bda21f0fcd | ||
|
|
980a5d0a6d | ||
|
|
e18a91a4b5 | ||
|
|
b95d352236 | ||
|
|
c5f4cf1f9b | ||
|
|
8650002523 | ||
|
|
25c1da6a21 | ||
|
|
4e3ede42c6 | ||
|
|
918a3c8d8b | ||
|
|
290419b3b6 | ||
|
|
32b6146532 | ||
|
|
3a73ec21f7 | ||
|
|
41d5b70344 | ||
|
|
b4769778af | ||
|
|
473f9c31b1 | ||
|
|
ae4f168066 | ||
|
|
c5578a93e8 | ||
|
|
f4b2a10616 | ||
|
|
ed63aa4910 | ||
|
|
4481869d42 | ||
|
|
abbf14b1ec | ||
|
|
4f9e54ba1d | ||
|
|
d80188167a | ||
|
|
4ab956abd1 | ||
|
|
1aa91e3828 | ||
|
|
2410d7b4c1 | ||
|
|
b7328e5fd2 | ||
|
|
7f5c437c46 | ||
|
|
ae5e038548 | ||
|
|
67e0fb8b0b | ||
|
|
b763542329 | ||
|
|
d061da6077 | ||
|
|
82a235aef4 | ||
|
|
fd55982d98 | ||
|
|
aabf180b23 | ||
|
|
1245d8299a | ||
|
|
1ffffaff47 | ||
|
|
c204f6d8b2 | ||
|
|
b775fb256b | ||
|
|
f82d382f23 | ||
|
|
4214af283a | ||
|
|
b32a5c9fa0 | ||
|
|
e7e13a631f | ||
|
|
c12096705f | ||
|
|
eb25500f50 | ||
|
|
4df06980a1 | ||
|
|
f3499013f4 | ||
|
|
158a7fca46 | ||
|
|
09ab30fb0f | ||
|
|
ae84aa43d2 | ||
|
|
b431a5d3fe | ||
|
|
b9683c2943 | ||
|
|
1ba8f74555 | ||
|
|
ec637f7946 | ||
|
|
2599b3dfdc | ||
|
|
d22caa7b70 | ||
|
|
01c90badf9 | ||
|
|
e4f605fbd5 | ||
|
|
6fdee9e0db | ||
|
|
cae7206733 | ||
|
|
e2e7aacc09 | ||
|
|
0d3dd96f2a | ||
|
|
e0660ba887 | ||
|
|
5965ded488 | ||
|
|
fc31c3077c | ||
|
|
e604345e65 | ||
|
|
111a0d69cf | ||
|
|
1a08ec17ba | ||
|
|
c537d290c3 | ||
|
|
2180f59725 | ||
|
|
b1dc8607af | ||
|
|
101898695e | ||
|
|
9295626503 | ||
|
|
18614d4e81 | ||
|
|
560e80455f | ||
|
|
a97e88be29 | ||
|
|
3b5667c882 | ||
|
|
0790e54794 | ||
|
|
94f9fac2bd | ||
|
|
8ad86928f1 | ||
|
|
d8e5936d45 | ||
|
|
caa717544b | ||
|
|
66b77f8d6d | ||
|
|
ab5264c8ca | ||
|
|
db12309bae | ||
|
|
2953e0dcc5 | ||
|
|
1b18ac9fe3 | ||
|
|
2cdad6622a | ||
|
|
7601d11a7a | ||
|
|
0bb4c6ef09 | ||
|
|
449624fd72 | ||
|
|
99f4f46062 | ||
|
|
b08d11c069 | ||
|
|
ac41e838ec | ||
|
|
836ddf3bbc | ||
|
|
0e815c626c | ||
|
|
4b7d035e9e | ||
|
|
cb3c44f48d | ||
|
|
8b3b9d33de | ||
|
|
59d99509c8 | ||
|
|
2933b76237 | ||
|
|
36c6d8974e | ||
|
|
d6cab76728 | ||
|
|
b4f740f338 | ||
|
|
a004de25ac | ||
|
|
d0584413ca | ||
|
|
ba57537108 | ||
|
|
6dcb110737 | ||
|
|
b599a832da | ||
|
|
9cbd5dd778 | ||
|
|
ed31c51237 | ||
|
|
e01707d25e | ||
|
|
73f118fe1c | ||
|
|
0234918f30 | ||
|
|
c90ba7e061 | ||
|
|
2aa4219838 | ||
|
|
b98417ab39 | ||
|
|
ead58dd606 | ||
|
|
a93e935585 | ||
|
|
aeb07e880e | ||
|
|
e0ce1f309e | ||
|
|
d74c81c3d1 | ||
|
|
2fa7200fb3 | ||
|
|
9f7d15e1b1 | ||
|
|
fdf00f1b18 | ||
|
|
abe7bbda15 | ||
|
|
795139d78f | ||
|
|
c7390ef0d7 | ||
|
|
8a926a3db3 | ||
|
|
7d5e49b44c | ||
|
|
555c3eac7a | ||
|
|
f79649d71b | ||
|
|
c9c27b9f01 | ||
|
|
0bc47cf853 | ||
|
|
5649328bc5 | ||
|
|
726dd15c5a | ||
|
|
d513c6f85c | ||
|
|
c1c17ba162 | ||
|
|
f8aa686557 | ||
|
|
d7eb4bbe6e | ||
|
|
c17a350796 | ||
|
|
bb106f8c6e | ||
|
|
09e47fb5eb | ||
|
|
099209fff2 | ||
|
|
d572490a67 | ||
|
|
180e7f79d4 | ||
|
|
d9da7cc92e | ||
|
|
c2ce2044cc | ||
|
|
e400bfb79e | ||
|
|
3d32faa8f9 | ||
|
|
862d76471b | ||
|
|
27f2474876 | ||
|
|
01a5aeeb58 | ||
|
|
47df131e7f | ||
|
|
56750adfe4 | ||
|
|
11e64ef018 | ||
|
|
9b20e0a430 | ||
|
|
41df2ec5da | ||
|
|
e45b207375 | ||
|
|
c37866e534 | ||
|
|
bda4ab7025 | ||
|
|
32cc1ff7b1 | ||
|
|
b5952a590a | ||
|
|
581a024ceb | ||
|
|
c45cb8b04e | ||
|
|
a5e1a4d6dd | ||
|
|
e88b280eb0 | ||
|
|
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 |
@@ -1,3 +0,0 @@
|
||||
component_depth: 2
|
||||
languages:
|
||||
- csharp
|
||||
@@ -0,0 +1,83 @@
|
||||
[*.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_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 = true
|
||||
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 @@
|
||||
###############################################################################
|
||||
# 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
|
||||
* text=auto
|
||||
@@ -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,123 @@
|
||||
name: CI
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
env:
|
||||
VERSION: "5.0.1.${{github.run_number}}"
|
||||
PACKAGE_SUFFIX: ""
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Setup .NET SDK
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: |
|
||||
8.0.x
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Build package
|
||||
run: dotnet build MQTTnet.sln --configuration Release /p:FileVersion=${{ env.VERSION }} /p:AssemblyVersion=${{ env.VERSION }} /p:PackageVersion=${{ env.VERSION }}${{ env.PACKAGE_SUFFIX }}
|
||||
|
||||
- name: Upload nuget packages
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: nugets
|
||||
path: |
|
||||
${{ github.workspace }}/Source/**/*.nupkg
|
||||
${{ github.workspace }}/Source/**/*.snupkg
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Setup .NET SDK
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: |
|
||||
8.0.x
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Execute tests
|
||||
run: dotnet test --framework net8.0 Source/MQTTnet.Tests/MQTTnet.Tests.csproj
|
||||
|
||||
sign:
|
||||
needs: build
|
||||
runs-on: windows-latest # Code signing must run on a Windows agent for Authenticode signing (dll/exe)
|
||||
if: github.repository == 'dotnet/MQTTnet'
|
||||
steps:
|
||||
- name: Setup .NET SDK
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: |
|
||||
6.0.x
|
||||
|
||||
- name: Download nuget packages
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: nugets
|
||||
path: nugets
|
||||
|
||||
- name: Install sign CLI tool
|
||||
run: dotnet tool install --tool-path . sign --version 0.9.0-beta.23127.3
|
||||
|
||||
- name: Sign nugets
|
||||
shell: pwsh
|
||||
run: >
|
||||
./sign code azure-key-vault `
|
||||
"**/*.nupkg" `
|
||||
--base-directory "${{ github.workspace }}\nugets" `
|
||||
--publisher-name "MQTTnet" `
|
||||
--description "MQTTnet" `
|
||||
--description-url "https://github.com/dotnet/MQTTnet" `
|
||||
--azure-key-vault-tenant-id "${{ secrets.AZURE_TENANT_ID }}" `
|
||||
--azure-key-vault-client-id "${{ secrets.AZURE_CLIENT_ID }}" `
|
||||
--azure-key-vault-client-secret "${{ secrets.AZURE_CLIENT_SECRET }}" `
|
||||
--azure-key-vault-certificate "${{ secrets.KEY_VAULT_CERTIFICATE_ID }}" `
|
||||
--azure-key-vault-url "${{ secrets.KEY_VAULT_URL }}"
|
||||
|
||||
- name: Upload signed nuget packages
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: signed-nugets
|
||||
path: |
|
||||
${{ github.workspace }}\nugets\**\*.nupkg
|
||||
${{ github.workspace }}\nugets\**\*.snupkg
|
||||
|
||||
publish-myget:
|
||||
if: ${{ github.event_name == 'push' }}
|
||||
needs: sign
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Download signed nuget packages
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: signed-nugets
|
||||
path: nugets
|
||||
|
||||
- name: Publish myget.com nugets
|
||||
run: dotnet nuget push ${{ github.workspace }}/nugets/**/*.nupkg -k ${{ secrets.MYGET_API_KEY }} -s https://www.myget.org/F/mqttnet/api/v3/index.json --skip-duplicate
|
||||
|
||||
publish-nuget:
|
||||
# Tag schema is 'v5.0.0-rc' etc.
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
needs: [sign, test]
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Download signed nuget packages
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: signed-nugets
|
||||
path: nugets
|
||||
|
||||
- name: Publish nuget.org nugets
|
||||
run: dotnet nuget push ${{ github.workspace }}/nugets/**/*.nupkg -k ${{ secrets.NUGET_API_KEY }} -s https://api.nuget.org/v3/index.json --skip-duplicate
|
||||
@@ -292,3 +292,7 @@ __pycache__/
|
||||
*.map
|
||||
|
||||
/Tests/MQTTnet.TestApp.NetCore/RetainedMessages.json
|
||||
|
||||
Build/NuGet/
|
||||
|
||||
Build/nuget.exe
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<package >
|
||||
<metadata>
|
||||
<id>MQTTnet.AspNetCore</id>
|
||||
<version>0.0.0</version>
|
||||
<authors>Christian Kratky</authors>
|
||||
<owners>Christian Kratky</owners>
|
||||
<license type="file">LICENSE</license>
|
||||
<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>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</releaseNotes>
|
||||
<copyright>Copyright Christian Kratky 2016-2019</copyright>
|
||||
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin</tags>
|
||||
<dependencies>
|
||||
<dependency id="MQTTnet" version="$nugetVersion" />
|
||||
|
||||
<dependency id="Microsoft.AspNetCore.Connections.Abstractions" version="2.1.3" />
|
||||
<dependency id="Microsoft.AspNetCore.Http.Connections" version="1.0.3" />
|
||||
<dependency id="Microsoft.AspNetCore.WebSockets" version="2.1.1" />
|
||||
<dependency id="Microsoft.Extensions.Hosting.Abstractions" version="2.1.1" />
|
||||
</dependencies>
|
||||
</metadata>
|
||||
|
||||
<files>
|
||||
<!-- License -->
|
||||
<file src="..\LICENSE" />
|
||||
|
||||
<!-- .NET Standard 2.0 -->
|
||||
<file src="..\Source\MQTTnet.AspNetCore\bin\Release\netstandard2.0\MQTTnet.AspNetCore.*" target="lib\netstandard2.0\"/>
|
||||
</files>
|
||||
</package>
|
||||
@@ -1,42 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<package >
|
||||
<metadata>
|
||||
<id>MQTTnet.Extensions.ManagedClient</id>
|
||||
<version>0.0.0</version>
|
||||
<authors>Christian Kratky</authors>
|
||||
<owners>Christian Kratky</owners>
|
||||
<license type="file">LICENSE</license>
|
||||
<projectUrl>https://github.com/chkr1011/MQTTnet</projectUrl>
|
||||
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<description>This is an extension library which provides a managed MQTT client with additional features using MQTTnet.</description>
|
||||
<releaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</releaseNotes>
|
||||
<copyright>Copyright Christian Kratky 2016-2019</copyright>
|
||||
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin</tags>
|
||||
<dependencies>
|
||||
<dependency id="MQTTnet" version="$nugetVersion" />
|
||||
</dependencies>
|
||||
</metadata>
|
||||
|
||||
<files>
|
||||
<!-- License -->
|
||||
<file src="..\LICENSE" />
|
||||
|
||||
<!-- .NET Standard 1.3 -->
|
||||
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\netstandard1.3\MQTTnet.Extensions.ManagedClient.*" target="lib\netstandard1.3\"/>
|
||||
|
||||
<!-- .NET Standard 2.0 -->
|
||||
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\netstandard2.0\MQTTnet.Extensions.ManagedClient.*" target="lib\netstandard2.0\"/>
|
||||
|
||||
<!-- Universal Windows -->
|
||||
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\uap10.0\MQTTnet.Extensions.ManagedClient.*" target="lib\uap10.0\"/>
|
||||
|
||||
<!-- .NET Framework -->
|
||||
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\net452\MQTTnet.Extensions.ManagedClient.*" target="lib\net452\"/>
|
||||
|
||||
<!-- .NET Framework 4.6.0 will use binaries from 4.5.2. -->
|
||||
<file src="..\Source\MQTTnet.Extensions.ManagedClient\bin\Release\net461\MQTTnet.Extensions.Rpc.*" target="lib\net461\"/>
|
||||
|
||||
<!-- Everything above .NET Framework 4.6.1 will use the binaries from 4.6.1. -->
|
||||
</files>
|
||||
</package>
|
||||
@@ -1,42 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<package >
|
||||
<metadata>
|
||||
<id>MQTTnet.Extensions.Rpc</id>
|
||||
<version>0.0.0</version>
|
||||
<authors>Christian Kratky</authors>
|
||||
<owners>Christian Kratky</owners>
|
||||
<license type="file">LICENSE</license>
|
||||
<projectUrl>https://github.com/chkr1011/MQTTnet</projectUrl>
|
||||
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<description>This is an extension library which allows executing synchronous device calls including a response using MQTTnet.</description>
|
||||
<releaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</releaseNotes>
|
||||
<copyright>Copyright Christian Kratky 2016-2019</copyright>
|
||||
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin</tags>
|
||||
<dependencies>
|
||||
<dependency id="MQTTnet" version="$nugetVersion" />
|
||||
</dependencies>
|
||||
</metadata>
|
||||
|
||||
<files>
|
||||
<!-- License -->
|
||||
<file src="..\LICENSE" />
|
||||
|
||||
<!-- .NET Standard 1.3 -->
|
||||
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\netstandard1.3\MQTTnet.Extensions.Rpc.*" target="lib\netstandard1.3\"/>
|
||||
|
||||
<!-- .NET Standard 2.0 -->
|
||||
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\netstandard2.0\MQTTnet.Extensions.Rpc.*" target="lib\netstandard2.0\"/>
|
||||
|
||||
<!-- Universal Windows -->
|
||||
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\uap10.0\MQTTnet.Extensions.Rpc.*" target="lib\uap10.0\"/>
|
||||
|
||||
<!-- .NET Framework -->
|
||||
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\net452\MQTTnet.Extensions.Rpc.*" target="lib\net452\"/>
|
||||
|
||||
<!-- .NET Framework 4.6.0 will use binaries from 4.5.2. -->
|
||||
<file src="..\Source\MQTTnet.Extensions.Rpc\bin\Release\net461\MQTTnet.Extensions.Rpc.*" target="lib\net461\"/>
|
||||
|
||||
<!-- Everything above .NET Framework 4.6.1 will use the binaries from 4.6.1. -->
|
||||
</files>
|
||||
</package>
|
||||
@@ -1,42 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<package >
|
||||
<metadata>
|
||||
<id>MQTTnet.Extensions.WebSocket4Net</id>
|
||||
<version>0.0.0</version>
|
||||
<authors>Christian Kratky</authors>
|
||||
<owners>Christian Kratky</owners>
|
||||
<license type="file">LICENSE</license>
|
||||
<projectUrl>https://github.com/chkr1011/MQTTnet</projectUrl>
|
||||
<iconUrl>https://raw.githubusercontent.com/chkr1011/MQTTnet/master/Images/Logo_128x128.png</iconUrl>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<description>This is an extension library which allows using _WebSocket4Net_ as transport for MQTTnet clients.</description>
|
||||
<releaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</releaseNotes>
|
||||
<copyright>Copyright Christian Kratky 2016-2019</copyright>
|
||||
<tags>MQTT Message Queue Telemetry Transport MQTTClient MQTTServer Server MQTTBroker Broker NETStandard IoT InternetOfThings Messaging Hardware Arduino Sensor Actuator M2M ESP Smart Home Cities Automation Xamarin</tags>
|
||||
<dependencies>
|
||||
<dependency id="MQTTnet" version="$nugetVersion" />
|
||||
</dependencies>
|
||||
</metadata>
|
||||
|
||||
<files>
|
||||
<!-- License -->
|
||||
<file src="..\LICENSE" />
|
||||
|
||||
<!-- .NET Standard 1.3 -->
|
||||
<file src="..\Source\MQTTnet.Extensions.WebSocket4Net\bin\Release\netstandard1.3\MQTTnet.Extensions.WebSocket4Net.*" target="lib\netstandard1.3\"/>
|
||||
|
||||
<!-- .NET Standard 2.0 -->
|
||||
<file src="..\Source\MQTTnet.Extensions.WebSocket4Net\bin\Release\netstandard2.0\MQTTnet.Extensions.WebSocket4Net.*" target="lib\netstandard2.0\"/>
|
||||
|
||||
<!-- Universal Windows -->
|
||||
<file src="..\Source\MQTTnet.Extensions.WebSocket4Net\bin\Release\uap10.0\MQTTnet.Extensions.WebSocket4Net.*" target="lib\uap10.0\"/>
|
||||
|
||||
<!-- .NET Framework -->
|
||||
<file src="..\Source\MQTTnet.Extensions.WebSocket4Net\bin\Release\net452\MQTTnet.Extensions.WebSocket4Net.*" target="lib\net452\"/>
|
||||
|
||||
<!-- .NET Framework 4.6.0 will use binaries from 4.5.2. -->
|
||||
<file src="..\Source\MQTTnet.Extensions.WebSocket4Net\bin\Release\net461\MQTTnet.Extensions.WebSocket4Net.*" target="lib\net461\"/>
|
||||
|
||||
<!-- Everything above .NET Framework 4.6.1 will use the binaries from 4.6.1. -->
|
||||
</files>
|
||||
</package>
|
||||
@@ -1,42 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<package >
|
||||
<metadata>
|
||||
<id>MQTTnet.NETStandard</id>
|
||||
<version>0.0.0</version>
|
||||
<authors>Christian Kratky</authors>
|
||||
<owners>Christian Kratky</owners>
|
||||
<license type="file">LICENSE</license>
|
||||
<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 package contains the .NET Standard version of MQTTnet only.</description>
|
||||
<releaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</releaseNotes>
|
||||
<copyright>Copyright Christian Kratky 2016-2019</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.2" />
|
||||
</group>
|
||||
<group targetFramework="netstandard2.0">
|
||||
<dependency id="NETStandard.Library" version="2.0.0" />
|
||||
<dependency id="System.Net.Security" version="4.3.2" />
|
||||
<dependency id="System.Net.WebSockets" version="4.3.0" />
|
||||
<dependency id="System.Net.WebSockets.Client" version="4.3.2" />
|
||||
</group>
|
||||
</dependencies>
|
||||
</metadata>
|
||||
|
||||
<files>
|
||||
<!-- License -->
|
||||
<file src="..\LICENSE" />
|
||||
|
||||
<!-- .NET Standard 1.3 -->
|
||||
<file src="..\Source\MQTTnet\bin\Release\netstandard1.3\MQTTnet.*" target="lib\netstandard1.3\"/>
|
||||
|
||||
<!-- .NET Standard 2.0 -->
|
||||
<file src="..\Source\MQTTnet\bin\Release\netstandard2.0\MQTTnet.*" target="lib\netstandard2.0\"/>
|
||||
</files>
|
||||
</package>
|
||||
@@ -1,59 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<package >
|
||||
<metadata>
|
||||
<id>MQTTnet</id>
|
||||
<version>0.0.0</version>
|
||||
<authors>Christian Kratky</authors>
|
||||
<owners>Christian Kratky</owners>
|
||||
<license type="file">LICENSE</license>
|
||||
<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) and supports v3.1.0, v3.1.1 and v5.0.0 of the MQTT protocol.</description>
|
||||
<releaseNotes>
|
||||
* [Core] Fixed wrong versions in nuget packages.
|
||||
* [Server] The TCP address is now reused when starting which should prevent "port in used" error when restarting.
|
||||
</releaseNotes>
|
||||
<copyright>Copyright Christian Kratky 2016-2019</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.2" />
|
||||
</group>
|
||||
<group targetFramework="netstandard2.0">
|
||||
<dependency id="NETStandard.Library" version="2.0.0" />
|
||||
<dependency id="System.Net.Security" version="4.3.2" />
|
||||
<dependency id="System.Net.WebSockets" version="4.3.0" />
|
||||
<dependency id="System.Net.WebSockets.Client" version="4.3.2" />
|
||||
</group>
|
||||
<group targetFramework="uap10.0">
|
||||
<dependency id="Microsoft.NETCore.UniversalWindowsPlatform" version="6.2.8" />
|
||||
</group>
|
||||
</dependencies>
|
||||
</metadata>
|
||||
|
||||
<files>
|
||||
<!-- License -->
|
||||
<file src="..\LICENSE" />
|
||||
|
||||
<!-- .NET Standard 1.3 -->
|
||||
<file src="..\Source\MQTTnet\bin\Release\netstandard1.3\MQTTnet.*" target="lib\netstandard1.3\"/>
|
||||
|
||||
<!-- .NET Standard 2.0 -->
|
||||
<file src="..\Source\MQTTnet\bin\Release\netstandard2.0\MQTTnet.*" target="lib\netstandard2.0\"/>
|
||||
|
||||
<!-- Universal Windows -->
|
||||
<file src="..\Source\MQTTnet\bin\Release\uap10.0\MQTTnet.*" target="lib\uap10.0\"/>
|
||||
|
||||
<!-- .NET Framework -->
|
||||
<file src="..\Source\MQTTnet\bin\Release\net452\MQTTnet.*" target="lib\net452\"/>
|
||||
|
||||
<!-- .NET Framework 4.6.0 will use binaries from 4.5.2. -->
|
||||
<file src="..\Source\MQTTnet\bin\Release\net461\MQTTnet.*" target="lib\net461\"/>
|
||||
|
||||
<!-- Everything above .NET Framework 4.6.1 will use the binaries from 4.6.1. -->
|
||||
</files>
|
||||
</package>
|
||||
@@ -1,120 +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'
|
||||
$msbuild = &$vswhere -products * -requires Microsoft.Component.MSBuild -latest -find MSBuild\**\Bin\MSBuild.exe
|
||||
|
||||
Write-Host
|
||||
Write-Host "Assembly version = $assemblyVersion"
|
||||
Write-Host "Nuget version = $nugetVersion"
|
||||
Write-Host "MSBuild path = $msbuild"
|
||||
Write-Host
|
||||
|
||||
# Build the core library
|
||||
&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net452" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net461" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard1.3" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
&$msbuild ..\Source\MQTTnet\MQTTnet.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="uap10.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
|
||||
# Build the ASP.NET Core 2.0 extension
|
||||
&$msbuild ..\Source\MQTTnet.AspNetCore\MQTTnet.AspNetCore.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
|
||||
# Build the RPC extension
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net452" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net461" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard1.3" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="uap10.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
|
||||
# Build the Managed Client extension
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net452" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net461" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard1.3" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="uap10.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
|
||||
# Build the WebSocket4Net extension
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net452" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="net461" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard1.3" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.csproj /t:Build /p:Configuration="Release" /p:TargetFramework="netstandard2.0" /p:FileVersion=$assemblyVersion /p:AssemblyVersion=$assemblyVersion /verbosity:m /p:SignAssembly=true /p:AssemblyOriginatorKeyFile=".\..\..\Build\codeSigningKey.pfx"
|
||||
&$msbuild ..\Source\MQTTnet.Extensions.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.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"
|
||||
|
||||
# Create NuGet packages.
|
||||
|
||||
Invoke-WebRequest -Uri "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe" -OutFile "nuget.exe"
|
||||
|
||||
Remove-Item .\NuGet -Force -Recurse -ErrorAction SilentlyContinue
|
||||
|
||||
Copy-Item MQTTnet.AspNetCore.nuspec -Destination MQTTnet.AspNetCore.nuspec.old -Force
|
||||
(Get-Content MQTTnet.AspNetCore.nuspec) -replace '\$nugetVersion', $nugetVersion | Set-Content MQTTnet.AspNetCore.nuspec
|
||||
Copy-Item MQTTnet.Extensions.Rpc.nuspec -Destination MQTTnet.Extensions.Rpc.nuspec.old -Force
|
||||
(Get-Content MQTTnet.Extensions.Rpc.nuspec) -replace '\$nugetVersion', $nugetVersion | Set-Content MQTTnet.Extensions.Rpc.nuspec
|
||||
Copy-Item MQTTnet.Extensions.ManagedClient.nuspec -Destination MQTTnet.Extensions.ManagedClient.nuspec.old -Force
|
||||
(Get-Content MQTTnet.Extensions.ManagedClient.nuspec) -replace '\$nugetVersion', $nugetVersion | Set-Content MQTTnet.Extensions.ManagedClient.nuspec
|
||||
Copy-Item MQTTnet.Extensions.WebSocket4Net.nuspec -Destination MQTTnet.Extensions.WebSocket4Net.nuspec.old -Force
|
||||
(Get-Content MQTTnet.Extensions.WebSocket4Net.nuspec) -replace '\$nugetVersion', $nugetVersion | Set-Content MQTTnet.Extensions.WebSocket4Net.nuspec
|
||||
|
||||
New-Item -ItemType Directory -Force -Path .\NuGet
|
||||
.\nuget.exe pack MQTTnet.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $nugetVersion
|
||||
.\nuget.exe pack MQTTnet.NETStandard.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $nugetVersion
|
||||
.\nuget.exe pack MQTTnet.AspNetCore.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $nugetVersion
|
||||
.\nuget.exe pack MQTTnet.Extensions.Rpc.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $nugetVersion
|
||||
.\nuget.exe pack MQTTnet.Extensions.ManagedClient.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $nugetVersion
|
||||
.\nuget.exe pack MQTTnet.Extensions.WebSocket4Net.nuspec -Verbosity detailed -Symbols -OutputDir "NuGet" -Version $nugetVersion
|
||||
|
||||
Move-Item MQTTnet.AspNetCore.nuspec.old -Destination MQTTnet.AspNetCore.nuspec -Force
|
||||
Move-Item MQTTnet.Extensions.Rpc.nuspec.old -Destination MQTTnet.Extensions.Rpc.nuspec -Force
|
||||
Move-Item MQTTnet.Extensions.ManagedClient.nuspec.old -Destination MQTTnet.Extensions.ManagedClient.nuspec -Force
|
||||
Move-Item MQTTnet.Extensions.WebSocket4Net.nuspec.old -Destination MQTTnet.Extensions.WebSocket4Net.nuspec -Force
|
||||
|
||||
Remove-Item "nuget.exe" -Force -Recurse -ErrorAction SilentlyContinue
|
||||
|
||||
####################################################################
|
||||
|
||||
# Build MQTTnet.Server Portable
|
||||
&dotnet publish ..\Source\MQTTnet.Server\MQTTnet.Server.csproj --configuration Release /p:FileVersion=$assemblyVersion /p:Version=$nugetVersion
|
||||
|
||||
$source = "..\Source\MQTTnet.Server\bin\Release\netcoreapp2.2\publish"
|
||||
$destination = "..\Source\MQTTnet.Server\bin\MQTTnet.Server-Portable-v$nugetVersion.zip"
|
||||
If(Test-path $destination) {Remove-item $destination}
|
||||
Add-Type -assembly "system.io.compression.filesystem"
|
||||
[io.compression.zipfile]::CreateFromDirectory($source, $destination)
|
||||
|
||||
####################################################################
|
||||
|
||||
# Build MQTTnet.Server Linux-x64
|
||||
&dotnet publish ..\Source\MQTTnet.Server\MQTTnet.Server.csproj --configuration Release /p:FileVersion=$assemblyVersion /p:Version=$nugetVersion --self-contained --runtime linux-x64
|
||||
|
||||
$source = "..\Source\MQTTnet.Server\bin\Release\netcoreapp2.2\linux-x64\publish"
|
||||
$destination = "..\Source\MQTTnet.Server\bin\MQTTnet.Server-Linux-x64-v$nugetVersion.zip"
|
||||
If(Test-path $destination) {Remove-item $destination}
|
||||
Add-Type -assembly "system.io.compression.filesystem"
|
||||
[io.compression.zipfile]::CreateFromDirectory($source, $destination)
|
||||
|
||||
####################################################################
|
||||
|
||||
# Build MQTTnet.Server Linux-ARM
|
||||
&dotnet publish ..\Source\MQTTnet.Server\MQTTnet.Server.csproj --configuration Release /p:FileVersion=$assemblyVersion /p:Version=$nugetVersion --self-contained --runtime linux-arm
|
||||
|
||||
$source = "..\Source\MQTTnet.Server\bin\Release\netcoreapp2.2\linux-ARM\publish"
|
||||
$destination = "..\Source\MQTTnet.Server\bin\MQTTnet.Server-Linux-ARM-v$nugetVersion.zip"
|
||||
If(Test-path $destination) {Remove-item $destination}
|
||||
Add-Type -assembly "system.io.compression.filesystem"
|
||||
[io.compression.zipfile]::CreateFromDirectory($source, $destination)
|
||||
|
||||
####################################################################
|
||||
|
||||
# Build MQTTnet.Server Windows-x64
|
||||
&dotnet publish ..\Source\MQTTnet.Server\MQTTnet.Server.csproj --configuration Release /p:FileVersion=$assemblyVersion /p:Version=$nugetVersion --self-contained --runtime win-x64
|
||||
|
||||
$source = "..\Source\MQTTnet.Server\bin\Release\netcoreapp2.2\win-x64\publish"
|
||||
$destination = "..\Source\MQTTnet.Server\bin\MQTTnet.Server-Windows-x64-v$nugetVersion.zip"
|
||||
If(Test-path $destination) {Remove-item $destination}
|
||||
Add-Type -assembly "system.io.compression.filesystem"
|
||||
[io.compression.zipfile]::CreateFromDirectory($source, $destination)
|
||||
|
||||
####################################################################
|
||||
@@ -1,13 +0,0 @@
|
||||
param([string]$apiKey)
|
||||
|
||||
Invoke-WebRequest -Uri "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe" -OutFile "nuget.exe"
|
||||
|
||||
$files = Get-ChildItem -Path ".\NuGet" -Filter "*.nupkg"
|
||||
foreach ($file in $files)
|
||||
{
|
||||
Write-Host "Uploading: " $file
|
||||
|
||||
.\nuget.exe push $file.Fullname $apiKey -NoSymbols -Source https://api.nuget.org/v3/index.json
|
||||
}
|
||||
|
||||
Remove-Item "nuget.exe" -Force -Recurse -ErrorAction SilentlyContinue
|
||||
@@ -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
|
||||
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 4.0 KiB |
@@ -0,0 +1,239 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="210mm"
|
||||
height="297mm"
|
||||
viewBox="0 0 210 297"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
|
||||
sodipodi:docname="MQTT.svg">
|
||||
<defs
|
||||
id="defs2">
|
||||
<filter
|
||||
inkscape:label="Blur Double"
|
||||
inkscape:menu="Blurs"
|
||||
inkscape:menu-tooltip="Overlays two copies with different blur amounts and modifiable blend and composite"
|
||||
style="color-interpolation-filters:sRGB;"
|
||||
id="filter3801">
|
||||
<feGaussianBlur
|
||||
stdDeviation="5"
|
||||
result="fbSourceGraphic"
|
||||
id="feGaussianBlur3793" />
|
||||
<feGaussianBlur
|
||||
stdDeviation="0.01"
|
||||
in="SourceGraphic"
|
||||
result="result1"
|
||||
id="feGaussianBlur3795" />
|
||||
<feComposite
|
||||
in2="result1"
|
||||
operator="arithmetic"
|
||||
in="fbSourceGraphic"
|
||||
k2="0.5"
|
||||
k3="0.5"
|
||||
result="result2"
|
||||
id="feComposite3797" />
|
||||
<feBlend
|
||||
in2="fbSourceGraphic"
|
||||
mode="normal"
|
||||
result="result3"
|
||||
id="feBlend3799" />
|
||||
</filter>
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="0.24748737"
|
||||
inkscape:cx="955.72999"
|
||||
inkscape:cy="295.38958"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="3840"
|
||||
inkscape:window-height="2066"
|
||||
inkscape:window-x="2989"
|
||||
inkscape:window-y="-11"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<rect
|
||||
ry="50"
|
||||
rx="50"
|
||||
style="fill:#000000;stroke-width:0.26458332;filter:url(#filter3801)"
|
||||
y="92.501205"
|
||||
x="56.367012"
|
||||
height="512"
|
||||
width="512"
|
||||
id="rect3791"
|
||||
inkscape:export-xdpi="2.6500001"
|
||||
inkscape:export-ydpi="2.6500001" />
|
||||
<rect
|
||||
id="rect3713"
|
||||
width="512"
|
||||
height="512"
|
||||
x="53.721176"
|
||||
y="88.797035"
|
||||
style="fill:#aa0000;stroke-width:0.26458332"
|
||||
rx="50"
|
||||
ry="50"
|
||||
inkscape:export-xdpi="2.6500001"
|
||||
inkscape:export-ydpi="2.6500001" />
|
||||
<rect
|
||||
style="fill:#ce0000;fill-opacity:0.60591131;stroke-width:0.28100395"
|
||||
id="rect3806"
|
||||
width="143.25298"
|
||||
height="9.0714283"
|
||||
x="270.81995"
|
||||
y="554.02386"
|
||||
inkscape:export-xdpi="2.6500001"
|
||||
inkscape:export-ydpi="2.6500001" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4638"
|
||||
d="m 494.14104,142.00092 -5.35736,41.28822 67.77684,374.38977 a 263.46279,229.17651 36.835702 0 1 4.98278,-0.4605 263.46279,229.17651 36.835702 0 1 7.77403,0.0471 z"
|
||||
style="fill:#ce0000;fill-opacity:0.60591131;stroke-width:0.59952348"
|
||||
inkscape:transform-center-x="-28.818481"
|
||||
inkscape:transform-center-y="146.97441"
|
||||
inkscape:export-xdpi="2.6500001"
|
||||
inkscape:export-ydpi="2.6500001" />
|
||||
<path
|
||||
style="fill:#d40000;stroke-width:1.76103246"
|
||||
d="m 2138.1582,1411.377 a 995.76488,866.17897 0 0 0 -842.3984,854.3418 995.76488,866.17897 0 0 0 0.2988,5.0097 h 653.123 c 104.693,0 188.9766,-84.2836 188.9766,-188.9765 z"
|
||||
transform="scale(0.26458333)"
|
||||
id="path3715-5-8"
|
||||
inkscape:connector-curvature="0"
|
||||
inkscape:export-xdpi="2.6500001"
|
||||
inkscape:export-ydpi="2.6500001" />
|
||||
<ellipse
|
||||
style="fill:#d40000;stroke-width:0.06901014"
|
||||
id="path3715-5"
|
||||
cx="492.58557"
|
||||
cy="170.58017"
|
||||
rx="39.021355"
|
||||
ry="33.94323"
|
||||
inkscape:export-xdpi="2.6500001"
|
||||
inkscape:export-ydpi="2.6500001" />
|
||||
<path
|
||||
style="fill:#ce0000;fill-opacity:0.60591131;stroke-width:2.26591563"
|
||||
d="M 464.66602,532.89453 542.01562,669.92969 1595.373,1648.873 a 995.76487,866.17896 0 0 1 14.0293,-12.6835 995.76487,866.17896 0 0 1 23.6231,-17.4727 z"
|
||||
id="rect3806-2"
|
||||
inkscape:connector-curvature="0"
|
||||
transform="scale(0.26458333)"
|
||||
inkscape:export-xdpi="2.6500001"
|
||||
inkscape:export-ydpi="2.6500001" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:10.58333302px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
|
||||
x="102.34985"
|
||||
y="320.75201"
|
||||
id="text3774"
|
||||
inkscape:export-xdpi="2.6500001"
|
||||
inkscape:export-ydpi="2.6500001"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan3772"
|
||||
x="102.34985"
|
||||
y="320.75201"
|
||||
style="font-size:141.1111145px;fill:#ffffff;stroke-width:0.26458332">MQTT</tspan></text>
|
||||
<ellipse
|
||||
ry="33.94323"
|
||||
rx="39.021355"
|
||||
cy="148.56271"
|
||||
cx="124.47787"
|
||||
id="ellipse3768"
|
||||
style="fill:#d40000;stroke-width:0.06901014"
|
||||
inkscape:export-xdpi="2.6500001"
|
||||
inkscape:export-ydpi="2.6500001" />
|
||||
<ellipse
|
||||
style="fill:#d40000;stroke-width:0.06901014"
|
||||
id="ellipse3770"
|
||||
cx="234.9953"
|
||||
cy="556.91571"
|
||||
rx="39.021355"
|
||||
ry="33.94323"
|
||||
inkscape:export-xdpi="2.6500001"
|
||||
inkscape:export-ydpi="2.6500001" />
|
||||
<text
|
||||
id="text3778"
|
||||
y="474.73837"
|
||||
x="194.57605"
|
||||
style="font-style:normal;font-weight:normal;font-size:10.58333302px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
|
||||
xml:space="preserve"
|
||||
inkscape:export-xdpi="2.6500001"
|
||||
inkscape:export-ydpi="2.6500001"><tspan
|
||||
style="font-size:141.1111145px;fill:#ffffff;stroke-width:0.26458332"
|
||||
y="474.73837"
|
||||
x="194.57605"
|
||||
id="tspan3776"
|
||||
sodipodi:role="line">net</tspan></text>
|
||||
<rect
|
||||
ry="50"
|
||||
rx="50"
|
||||
style="fill:#aa0000;stroke-width:0.26458332"
|
||||
y="97.349655"
|
||||
x="-672.18286"
|
||||
height="512"
|
||||
width="512"
|
||||
id="rect4642"
|
||||
inkscape:export-xdpi="6.3499999"
|
||||
inkscape:export-ydpi="6.3499999" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4648"
|
||||
d="m -160.18302,381.97945 a 263.46279,229.17652 0 0 0 -222.88457,226.0446 263.46279,229.17652 0 0 0 0.0791,1.32549 h 172.80546 c 27.70002,0 50.00005,-22.30004 50.00005,-50.00004 z"
|
||||
style="fill:#d40000;stroke-width:0.46593982"
|
||||
inkscape:export-xdpi="6.3499999"
|
||||
inkscape:export-ydpi="6.3499999" />
|
||||
<text
|
||||
id="text4656"
|
||||
y="329.30466"
|
||||
x="-639.42944"
|
||||
style="font-style:normal;font-weight:normal;font-size:10.58333302px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
|
||||
xml:space="preserve"
|
||||
inkscape:export-xdpi="6.3499999"
|
||||
inkscape:export-ydpi="6.3499999"><tspan
|
||||
style="font-weight:bold;font-size:141.1111145px;fill:#ffffff;stroke-width:0.26458332"
|
||||
y="329.30466"
|
||||
x="-639.42944"
|
||||
id="tspan4654"
|
||||
sodipodi:role="line">MQTT</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:10.58333302px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
|
||||
x="-550.90747"
|
||||
y="482.23267"
|
||||
id="text4664"
|
||||
inkscape:export-xdpi="6.3499999"
|
||||
inkscape:export-ydpi="6.3499999"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4662"
|
||||
x="-550.90747"
|
||||
y="482.23267"
|
||||
style="font-weight:bold;font-size:141.1111145px;fill:#ffffff;stroke-width:0.26458332">net</tspan></text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.4 KiB |
|
After Width: | Height: | Size: 7.8 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
@@ -1,6 +1,7 @@
|
||||
MIT License
|
||||
The MIT License (MIT)
|
||||
|
||||
MQTTnet Copyright (c) 2016-2019 Christian Kratky
|
||||
Copyright (c) .NET Foundation and Contributors
|
||||
All Rights Reserved
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,260 +0,0 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.27004.2010
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{9248C2E1-B9D6-40BF-81EC-86004D7765B4}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Source", "Source", "{32A630A7-2598-41D7-B625-204CD906F5FB}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet", "Source\MQTTnet\MQTTnet.csproj", "{3587E506-55A2-4EB3-99C7-DC01E42D25D2}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{67C28AC1-BC3A-420A-BE9C-FA2401431CF9}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
Build\build.ps1 = Build\build.ps1
|
||||
Build\MQTTnet.AspNetCore.nuspec = Build\MQTTnet.AspNetCore.nuspec
|
||||
Build\MQTTnet.Extensions.ManagedClient.nuspec = Build\MQTTnet.Extensions.ManagedClient.nuspec
|
||||
Build\MQTTnet.Extensions.Rpc.nuspec = Build\MQTTnet.Extensions.Rpc.nuspec
|
||||
Build\MQTTnet.nuspec = Build\MQTTnet.nuspec
|
||||
Build\upload.ps1 = Build\upload.ps1
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{B3F60ECB-45BA-4C66-8903-8BB89CA67998}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
.bettercodehub.yml = .bettercodehub.yml
|
||||
appveyor.yml = appveyor.yml
|
||||
LICENSE = LICENSE
|
||||
README.md = README.md
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.TestApp.NetCore", "Tests\MQTTnet.TestApp.NetCore\MQTTnet.TestApp.NetCore.csproj", "{3D283AAD-AAA8-4339-8394-52F80B6304DB}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.TestApp.AspNetCore2", "Tests\MQTTnet.TestApp.AspNetCore2\MQTTnet.TestApp.AspNetCore2.csproj", "{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.AspNetCore", "Source\MQTTnet.AspnetCore\MQTTnet.AspNetCore.csproj", "{F10C4060-F7EE-4A83-919F-FF723E72F94A}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Extensions", "Extensions", "{12816BCC-AF9E-44A9-9AE5-C246AF2A0587}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Extensions.Rpc", "Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj", "{C444E9C8-95FA-430E-9126-274129DE16CD}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Benchmarks", "Tests\MQTTnet.Benchmarks\MQTTnet.Benchmarks.csproj", "{998D04DD-7CB0-45F5-A393-E2495C16399E}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Extensions.ManagedClient", "Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj", "{C400533A-8EBA-4F0B-BF4D-295C3708604B}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.AspNetCore.Tests", "Tests\MQTTnet.AspNetCore.Tests\MQTTnet.AspNetCore.Tests.csproj", "{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Tests", "Tests\MQTTnet.Core.Tests\MQTTnet.Tests.csproj", "{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Server", "Source\MQTTnet.Server\MQTTnet.Server.csproj", "{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Extensions.WebSocket4Net", "Source\MQTTnet.Extensions.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.csproj", "{9AE2F8DA-4178-470B-B8C4-88BE1DAE7D5F}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Debug|ARM = Debug|ARM
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Release|ARM = Release|ARM
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Release|x64.Build.0 = Release|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Release|x86.Build.0 = Release|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|x64.Build.0 = Release|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|x86.Build.0 = Release|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|x64.Build.0 = Release|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|x86.Build.0 = Release|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|x64.Build.0 = Release|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|x86.Build.0 = Release|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Release|x64.Build.0 = Release|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Release|x86.Build.0 = Release|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|x64.Build.0 = Release|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|x86.Build.0 = Release|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|x64.Build.0 = Release|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|x86.Build.0 = Release|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|x64.Build.0 = Release|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|x86.Build.0 = Release|Any CPU
|
||||
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Release|x64.Build.0 = Release|Any CPU
|
||||
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41}.Release|x86.Build.0 = Release|Any CPU
|
||||
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Release|x64.Build.0 = Release|Any CPU
|
||||
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7}.Release|x86.Build.0 = Release|Any CPU
|
||||
{9AE2F8DA-4178-470B-B8C4-88BE1DAE7D5F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9AE2F8DA-4178-470B-B8C4-88BE1DAE7D5F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9AE2F8DA-4178-470B-B8C4-88BE1DAE7D5F}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{9AE2F8DA-4178-470B-B8C4-88BE1DAE7D5F}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{9AE2F8DA-4178-470B-B8C4-88BE1DAE7D5F}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{9AE2F8DA-4178-470B-B8C4-88BE1DAE7D5F}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{9AE2F8DA-4178-470B-B8C4-88BE1DAE7D5F}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{9AE2F8DA-4178-470B-B8C4-88BE1DAE7D5F}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{9AE2F8DA-4178-470B-B8C4-88BE1DAE7D5F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9AE2F8DA-4178-470B-B8C4-88BE1DAE7D5F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{9AE2F8DA-4178-470B-B8C4-88BE1DAE7D5F}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{9AE2F8DA-4178-470B-B8C4-88BE1DAE7D5F}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{9AE2F8DA-4178-470B-B8C4-88BE1DAE7D5F}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{9AE2F8DA-4178-470B-B8C4-88BE1DAE7D5F}.Release|x64.Build.0 = Release|Any CPU
|
||||
{9AE2F8DA-4178-470B-B8C4-88BE1DAE7D5F}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{9AE2F8DA-4178-470B-B8C4-88BE1DAE7D5F}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2} = {32A630A7-2598-41D7-B625-204CD906F5FB}
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A} = {32A630A7-2598-41D7-B625-204CD906F5FB}
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD} = {12816BCC-AF9E-44A9-9AE5-C246AF2A0587}
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B} = {12816BCC-AF9E-44A9-9AE5-C246AF2A0587}
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
||||
{9C7106CA-96B8-4ABE-B3B4-9357AB8ACB41} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
||||
{DADEA836-5CC3-474C-A2D8-4D0F31118CD7} = {32A630A7-2598-41D7-B625-204CD906F5FB}
|
||||
{9AE2F8DA-4178-470B-B8C4-88BE1DAE7D5F} = {12816BCC-AF9E-44A9-9AE5-C246AF2A0587}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {07536672-5CBC-4BE3-ACE0-708A431A7894}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -1,288 +1,90 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.29009.5
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.0.31919.166
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Tests", "Tests\MQTTnet.Core.Tests\MQTTnet.Tests.csproj", "{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{9248C2E1-B9D6-40BF-81EC-86004D7765B4}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Source", "Source", "{32A630A7-2598-41D7-B625-204CD906F5FB}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.TestApp.UniversalWindows", "Tests\MQTTnet.TestApp.UniversalWindows\MQTTnet.TestApp.UniversalWindows.csproj", "{FF1F72D6-9524-4422-9497-3CC0002216ED}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet", "Source\MQTTnet\MQTTnet.csproj", "{3587E506-55A2-4EB3-99C7-DC01E42D25D2}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{67C28AC1-BC3A-420A-BE9C-FA2401431CF9}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
Build\build.ps1 = Build\build.ps1
|
||||
Build\MQTTnet.AspNetCore.nuspec = Build\MQTTnet.AspNetCore.nuspec
|
||||
Build\MQTTnet.Extensions.ManagedClient.nuspec = Build\MQTTnet.Extensions.ManagedClient.nuspec
|
||||
Build\MQTTnet.Extensions.Rpc.nuspec = Build\MQTTnet.Extensions.Rpc.nuspec
|
||||
Build\MQTTnet.Extensions.WebSocket4Net.nuspec = Build\MQTTnet.Extensions.WebSocket4Net.nuspec
|
||||
Build\MQTTnet.NETStandard.nuspec = Build\MQTTnet.NETStandard.nuspec
|
||||
Build\MQTTnet.nuspec = Build\MQTTnet.nuspec
|
||||
Build\upload.ps1 = Build\upload.ps1
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{B3F60ECB-45BA-4C66-8903-8BB89CA67998}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
.bettercodehub.yml = .bettercodehub.yml
|
||||
appveyor.yml = appveyor.yml
|
||||
CODE-OF-CONDUCT.md = CODE-OF-CONDUCT.md
|
||||
LICENSE = LICENSE
|
||||
README.md = README.md
|
||||
Source\ReleaseNotes.md = Source\ReleaseNotes.md
|
||||
.github\workflows\ci.yml = .github\workflows\ci.yml
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.TestApp.NetCore", "Tests\MQTTnet.TestApp.NetCore\MQTTnet.TestApp.NetCore.csproj", "{3D283AAD-AAA8-4339-8394-52F80B6304DB}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.TestApp.AspNetCore2", "Tests\MQTTnet.TestApp.AspNetCore2\MQTTnet.TestApp.AspNetCore2.csproj", "{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.AspNetCore", "Source\MQTTnet.AspnetCore\MQTTnet.AspNetCore.csproj", "{F10C4060-F7EE-4A83-919F-FF723E72F94A}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Extensions", "Extensions", "{12816BCC-AF9E-44A9-9AE5-C246AF2A0587}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Extensions.Rpc", "Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj", "{C444E9C8-95FA-430E-9126-274129DE16CD}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Benchmarks", "Tests\MQTTnet.Benchmarks\MQTTnet.Benchmarks.csproj", "{998D04DD-7CB0-45F5-A393-E2495C16399E}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Samples", "Samples\MQTTnet.Samples.csproj", "{71CF35F5-3327-4A91-AAF4-5340F6701771}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Extensions.ManagedClient", "Source\MQTTnet.Extensions.ManagedClient\MQTTnet.Extensions.ManagedClient.csproj", "{C400533A-8EBA-4F0B-BF4D-295C3708604B}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Tests", "Source\MQTTnet.Tests\MQTTnet.Tests.csproj", "{B270F32A-9F3E-42EE-A989-813E35E29ADB}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.AspNetCore.Tests", "Tests\MQTTnet.AspNetCore.Tests\MQTTnet.AspNetCore.Tests.csproj", "{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Benchmarks", "Source\MQTTnet.Benchmarks\MQTTnet.Benchmarks.csproj", "{2F516E76-AAC4-4219-B7D1-34CDD3CFF381}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Server", "Source\MQTTnet.Server\MQTTnet.Server.csproj", "{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.TestApp", "Source\MQTTnet.TestApp\MQTTnet.TestApp.csproj", "{175D5340-CC5B-4542-939D-4E7D15A0BC8D}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Extensions.WebSocket4Net", "Source\MQTTnet.Extensions.WebSocket4Net\MQTTnet.Extensions.WebSocket4Net.csproj", "{2BD01D53-4CA5-4142-BE8D-313876395E3E}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.AspTestApp", "Source\MQTTnet.AspTestApp\MQTTnet.AspTestApp.csproj", "{72867E4C-4E15-4E8E-8FAB-AE9253286BBC}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MQTTnet.Extensions.TopicTemplate", "Source\MQTTnet.Extensions.TopicTemplate\MQTTnet.Extensions.TopicTemplate.csproj", "{374D5861-F7A6-42A2-8CBE-040846000EF9}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MQTTnet.Server", "Source\MQTTnet.Server\MQTTnet.Server.csproj", "{C876EFFD-C5AD-4E26-BD9F-4C48252C02BC}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Debug|ARM = Debug|ARM
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Release|ARM = Release|ARM
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|x64.Build.0 = Release|Any CPU
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC}.Release|x86.Build.0 = Release|Any CPU
|
||||
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
|
||||
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|ARM.ActiveCfg = Debug|ARM
|
||||
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|ARM.Build.0 = Debug|ARM
|
||||
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|ARM.Deploy.0 = Debug|ARM
|
||||
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|x64.Build.0 = Debug|x64
|
||||
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|x64.Deploy.0 = Debug|x64
|
||||
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|x86.Build.0 = Debug|x86
|
||||
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Debug|x86.Deploy.0 = Debug|x86
|
||||
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Release|ARM.ActiveCfg = Release|ARM
|
||||
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Release|ARM.Build.0 = Release|ARM
|
||||
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Release|ARM.Deploy.0 = Release|ARM
|
||||
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Release|x64.ActiveCfg = Release|x64
|
||||
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Release|x64.Build.0 = Release|x64
|
||||
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Release|x64.Deploy.0 = Release|x64
|
||||
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Release|x86.ActiveCfg = Release|x86
|
||||
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Release|x86.Build.0 = Release|x86
|
||||
{FF1F72D6-9524-4422-9497-3CC0002216ED}.Release|x86.Deploy.0 = Release|x86
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Release|x64.Build.0 = Release|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2}.Release|x86.Build.0 = Release|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|x64.Build.0 = Release|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB}.Release|x86.Build.0 = Release|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|x64.Build.0 = Release|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9}.Release|x86.Build.0 = Release|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|x64.Build.0 = Release|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A}.Release|x86.Build.0 = Release|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Release|x64.Build.0 = Release|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD}.Release|x86.Build.0 = Release|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|x64.Build.0 = Release|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E}.Release|x86.Build.0 = Release|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|x64.Build.0 = Release|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B}.Release|x86.Build.0 = Release|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|x64.Build.0 = Release|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E}.Release|x86.Build.0 = Release|Any CPU
|
||||
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Release|x64.Build.0 = Release|Any CPU
|
||||
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B}.Release|x86.Build.0 = Release|Any CPU
|
||||
{2BD01D53-4CA5-4142-BE8D-313876395E3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2BD01D53-4CA5-4142-BE8D-313876395E3E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2BD01D53-4CA5-4142-BE8D-313876395E3E}.Debug|ARM.ActiveCfg = Debug|Any CPU
|
||||
{2BD01D53-4CA5-4142-BE8D-313876395E3E}.Debug|ARM.Build.0 = Debug|Any CPU
|
||||
{2BD01D53-4CA5-4142-BE8D-313876395E3E}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{2BD01D53-4CA5-4142-BE8D-313876395E3E}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{2BD01D53-4CA5-4142-BE8D-313876395E3E}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{2BD01D53-4CA5-4142-BE8D-313876395E3E}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{2BD01D53-4CA5-4142-BE8D-313876395E3E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{2BD01D53-4CA5-4142-BE8D-313876395E3E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{2BD01D53-4CA5-4142-BE8D-313876395E3E}.Release|ARM.ActiveCfg = Release|Any CPU
|
||||
{2BD01D53-4CA5-4142-BE8D-313876395E3E}.Release|ARM.Build.0 = Release|Any CPU
|
||||
{2BD01D53-4CA5-4142-BE8D-313876395E3E}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{2BD01D53-4CA5-4142-BE8D-313876395E3E}.Release|x64.Build.0 = Release|Any CPU
|
||||
{2BD01D53-4CA5-4142-BE8D-313876395E3E}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{2BD01D53-4CA5-4142-BE8D-313876395E3E}.Release|x86.Build.0 = Release|Any CPU
|
||||
{71CF35F5-3327-4A91-AAF4-5340F6701771}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{71CF35F5-3327-4A91-AAF4-5340F6701771}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{71CF35F5-3327-4A91-AAF4-5340F6701771}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{71CF35F5-3327-4A91-AAF4-5340F6701771}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B270F32A-9F3E-42EE-A989-813E35E29ADB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B270F32A-9F3E-42EE-A989-813E35E29ADB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B270F32A-9F3E-42EE-A989-813E35E29ADB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B270F32A-9F3E-42EE-A989-813E35E29ADB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{2F516E76-AAC4-4219-B7D1-34CDD3CFF381}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2F516E76-AAC4-4219-B7D1-34CDD3CFF381}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2F516E76-AAC4-4219-B7D1-34CDD3CFF381}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{2F516E76-AAC4-4219-B7D1-34CDD3CFF381}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{175D5340-CC5B-4542-939D-4E7D15A0BC8D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{175D5340-CC5B-4542-939D-4E7D15A0BC8D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{175D5340-CC5B-4542-939D-4E7D15A0BC8D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{175D5340-CC5B-4542-939D-4E7D15A0BC8D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{72867E4C-4E15-4E8E-8FAB-AE9253286BBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{72867E4C-4E15-4E8E-8FAB-AE9253286BBC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{72867E4C-4E15-4E8E-8FAB-AE9253286BBC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{72867E4C-4E15-4E8E-8FAB-AE9253286BBC}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{374D5861-F7A6-42A2-8CBE-040846000EF9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{374D5861-F7A6-42A2-8CBE-040846000EF9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{374D5861-F7A6-42A2-8CBE-040846000EF9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{374D5861-F7A6-42A2-8CBE-040846000EF9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C876EFFD-C5AD-4E26-BD9F-4C48252C02BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C876EFFD-C5AD-4E26-BD9F-4C48252C02BC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C876EFFD-C5AD-4E26-BD9F-4C48252C02BC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C876EFFD-C5AD-4E26-BD9F-4C48252C02BC}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{A7FF0C91-25DE-4BA6-B39E-F54E8DADF1CC} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
||||
{FF1F72D6-9524-4422-9497-3CC0002216ED} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
||||
{3587E506-55A2-4EB3-99C7-DC01E42D25D2} = {32A630A7-2598-41D7-B625-204CD906F5FB}
|
||||
{3D283AAD-AAA8-4339-8394-52F80B6304DB} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
||||
{C6FF8AEA-0855-41EC-A1F3-AC262225BAB9} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
||||
{F10C4060-F7EE-4A83-919F-FF723E72F94A} = {32A630A7-2598-41D7-B625-204CD906F5FB}
|
||||
{C444E9C8-95FA-430E-9126-274129DE16CD} = {12816BCC-AF9E-44A9-9AE5-C246AF2A0587}
|
||||
{998D04DD-7CB0-45F5-A393-E2495C16399E} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
||||
{C400533A-8EBA-4F0B-BF4D-295C3708604B} = {12816BCC-AF9E-44A9-9AE5-C246AF2A0587}
|
||||
{61B62223-F5D0-48E4-BBD6-2CBA9353CB5E} = {9248C2E1-B9D6-40BF-81EC-86004D7765B4}
|
||||
{5699FB8C-838C-4AB0-80A5-9CA809F9B65B} = {32A630A7-2598-41D7-B625-204CD906F5FB}
|
||||
{2BD01D53-4CA5-4142-BE8D-313876395E3E} = {12816BCC-AF9E-44A9-9AE5-C246AF2A0587}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {07536672-5CBC-4BE3-ACE0-708A431A7894}
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
||||
@@ -1,14 +1,256 @@
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_FOR/@EntryValue">Required</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_FOREACH/@EntryValue">Required</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_IFELSE/@EntryValue">Required</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_WHILE/@EntryValue">Required</s:String>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_EXISTING_EMBEDDED_ARRANGEMENT/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_EXISTING_EXPR_MEMBER_ARRANGEMENT/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_EXISTING_INITIALIZER_ARRANGEMENT/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_EXISTING_INVOCATION_PARENS_ARRANGEMENT/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_EXISTING_PROPERTY_PATTERNS_ARRANGEMENT/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/KEEP_USER_LINEBREAKS/@EntryValue">False</s:Boolean>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_ACCESSORHOLDER_ATTRIBUTE_ON_SAME_LINE_EX/@EntryValue">NEVER</s:String>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_SIMPLE_ANONYMOUSMETHOD_ON_SINGLE_LINE/@EntryValue">False</s:Boolean>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_SIMPLE_EMBEDDED_STATEMENT_ON_SAME_LINE/@EntryValue">NEVER</s:String>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_SIMPLE_INITIALIZER_ON_SINGLE_LINE/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_SIMPLE_PROPERTY_PATTERN_ON_SINGLE_LINE/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_WITHIN_METHOD_CALL_PARENTHESES/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_WITHIN_METHOD_PARENTHESES/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/SPACE_WITHIN_PARENTHESES/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_AFTER_DECLARATION_LPAR/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_AFTER_INVOCATION_LPAR/@EntryValue">True</s:Boolean>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_ARGUMENTS_STYLE/@EntryValue">CHOP_IF_LONG</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_ARRAY_INITIALIZER_STYLE/@EntryValue">CHOP_IF_LONG</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_CHAINED_METHOD_CALLS/@EntryValue">CHOP_IF_LONG</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_EXTENDS_LIST_STYLE/@EntryValue">CHOP_IF_LONG</s:String>
|
||||
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_LIMIT/@EntryValue">180</s:Int64>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_PARAMETERS_STYLE/@EntryValue">CHOP_IF_LONG</s:String>
|
||||
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/XmlDocFormatter/KeepUserLineBreaks/@EntryValue">True</s:Boolean>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/XmlDocFormatter/ProcessingInstructionAttributesFormat/@EntryValue">OnDifferentLines</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CodeFormatting/XmlDocFormatter/TagAttributesFormat/@EntryValue">OnDifferentLines</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/CSharpFileLayoutPatterns/Pattern/@EntryValue"><?xml version="1.0" encoding="utf-16"?>
|
||||
<Patterns xmlns="urn:schemas-jetbrains-com:member-reordering-patterns">
|
||||
<TypePattern DisplayName="Non-reorderable types">
|
||||
<TypePattern.Match>
|
||||
<Or>
|
||||
<And>
|
||||
<Kind Is="Interface" />
|
||||
<Or>
|
||||
<HasAttribute Name="System.Runtime.InteropServices.InterfaceTypeAttribute" />
|
||||
<HasAttribute Name="System.Runtime.InteropServices.ComImport" />
|
||||
</Or>
|
||||
</And>
|
||||
<Kind Is="Struct" />
|
||||
<HasAttribute Name="JetBrains.Annotations.NoReorderAttribute" />
|
||||
<HasAttribute Name="JetBrains.Annotations.NoReorder" />
|
||||
</Or>
|
||||
</TypePattern.Match>
|
||||
</TypePattern>
|
||||
<TypePattern DisplayName="xUnit.net Test Classes" RemoveRegions="All">
|
||||
<TypePattern.Match>
|
||||
<And>
|
||||
<Kind Is="Class" />
|
||||
<HasMember>
|
||||
<And>
|
||||
<Kind Is="Method" />
|
||||
<HasAttribute Name="Xunit.FactAttribute" Inherited="True" />
|
||||
<HasAttribute Name="Xunit.TheoryAttribute" Inherited="True" />
|
||||
</And>
|
||||
</HasMember>
|
||||
</And>
|
||||
</TypePattern.Match>
|
||||
<Entry DisplayName="Setup/Teardown Methods">
|
||||
<Entry.Match>
|
||||
<Or>
|
||||
<Kind Is="Constructor" />
|
||||
<And>
|
||||
<Kind Is="Method" />
|
||||
<ImplementsInterface Name="System.IDisposable" />
|
||||
</And>
|
||||
</Or>
|
||||
</Entry.Match>
|
||||
<Entry.SortBy>
|
||||
<Kind Order="Constructor" />
|
||||
</Entry.SortBy>
|
||||
</Entry>
|
||||
<Entry DisplayName="All other members" />
|
||||
<Entry Priority="100" DisplayName="Test Methods">
|
||||
<Entry.Match>
|
||||
<And>
|
||||
<Kind Is="Method" />
|
||||
<HasAttribute Name="Xunit.FactAttribute" />
|
||||
<HasAttribute Name="Xunit.TheoryAttribute" />
|
||||
</And>
|
||||
</Entry.Match>
|
||||
<Entry.SortBy>
|
||||
<Name />
|
||||
</Entry.SortBy>
|
||||
</Entry>
|
||||
</TypePattern>
|
||||
<TypePattern DisplayName="NUnit Test Fixtures" RemoveRegions="All">
|
||||
<TypePattern.Match>
|
||||
<And>
|
||||
<Kind Is="Class" />
|
||||
<Or>
|
||||
<HasAttribute Name="NUnit.Framework.TestFixtureAttribute" Inherited="True" />
|
||||
<HasAttribute Name="NUnit.Framework.TestFixtureSourceAttribute" Inherited="True" />
|
||||
<HasMember>
|
||||
<And>
|
||||
<Kind Is="Method" />
|
||||
<HasAttribute Name="NUnit.Framework.TestAttribute" />
|
||||
<HasAttribute Name="NUnit.Framework.TestCaseAttribute" />
|
||||
<HasAttribute Name="NUnit.Framework.TestCaseSourceAttribute" />
|
||||
</And>
|
||||
</HasMember>
|
||||
</Or>
|
||||
</And>
|
||||
</TypePattern.Match>
|
||||
<Entry DisplayName="Setup/Teardown Methods">
|
||||
<Entry.Match>
|
||||
<And>
|
||||
<Kind Is="Method" />
|
||||
<Or>
|
||||
<HasAttribute Name="NUnit.Framework.SetUpAttribute" Inherited="True" />
|
||||
<HasAttribute Name="NUnit.Framework.TearDownAttribute" Inherited="True" />
|
||||
<HasAttribute Name="NUnit.Framework.TestFixtureSetUpAttribute" Inherited="True" />
|
||||
<HasAttribute Name="NUnit.Framework.TestFixtureTearDownAttribute" Inherited="True" />
|
||||
<HasAttribute Name="NUnit.Framework.OneTimeSetUpAttribute" Inherited="True" />
|
||||
<HasAttribute Name="NUnit.Framework.OneTimeTearDownAttribute" Inherited="True" />
|
||||
</Or>
|
||||
</And>
|
||||
</Entry.Match>
|
||||
</Entry>
|
||||
<Entry DisplayName="All other members" />
|
||||
<Entry Priority="100" DisplayName="Test Methods">
|
||||
<Entry.Match>
|
||||
<And>
|
||||
<Kind Is="Method" />
|
||||
<HasAttribute Name="NUnit.Framework.TestAttribute" />
|
||||
<HasAttribute Name="NUnit.Framework.TestCaseAttribute" />
|
||||
<HasAttribute Name="NUnit.Framework.TestCaseSourceAttribute" />
|
||||
</And>
|
||||
</Entry.Match>
|
||||
<Entry.SortBy>
|
||||
<Name />
|
||||
</Entry.SortBy>
|
||||
</Entry>
|
||||
</TypePattern>
|
||||
<TypePattern DisplayName="Default Pattern">
|
||||
<Entry Priority="100" DisplayName="Public Delegates">
|
||||
<Entry.Match>
|
||||
<And>
|
||||
<Access Is="Public" />
|
||||
<Kind Is="Delegate" />
|
||||
</And>
|
||||
</Entry.Match>
|
||||
<Entry.SortBy>
|
||||
<Name />
|
||||
</Entry.SortBy>
|
||||
</Entry>
|
||||
<Entry Priority="100" DisplayName="Public Enums">
|
||||
<Entry.Match>
|
||||
<And>
|
||||
<Access Is="Public" />
|
||||
<Kind Is="Enum" />
|
||||
</And>
|
||||
</Entry.Match>
|
||||
<Entry.SortBy>
|
||||
<Name />
|
||||
</Entry.SortBy>
|
||||
</Entry>
|
||||
<Entry DisplayName="Static Fields and Constants">
|
||||
<Entry.Match>
|
||||
<Or>
|
||||
<Kind Is="Constant" />
|
||||
<And>
|
||||
<Kind Is="Field" />
|
||||
<Static />
|
||||
</And>
|
||||
</Or>
|
||||
</Entry.Match>
|
||||
<Entry.SortBy>
|
||||
<Kind Order="Constant Field" />
|
||||
</Entry.SortBy>
|
||||
</Entry>
|
||||
<Entry DisplayName="Fields">
|
||||
<Entry.Match>
|
||||
<And>
|
||||
<Kind Is="Field" />
|
||||
<Not>
|
||||
<Static />
|
||||
</Not>
|
||||
</And>
|
||||
</Entry.Match>
|
||||
<Entry.SortBy>
|
||||
<Readonly />
|
||||
<Name />
|
||||
</Entry.SortBy>
|
||||
</Entry>
|
||||
<Entry DisplayName="Constructors">
|
||||
<Entry.Match>
|
||||
<Kind Is="Constructor" />
|
||||
</Entry.Match>
|
||||
<Entry.SortBy>
|
||||
<Static />
|
||||
</Entry.SortBy>
|
||||
</Entry>
|
||||
<Entry DisplayName="Events">
|
||||
<Entry.Match>
|
||||
<Kind Is="Event" />
|
||||
</Entry.Match>
|
||||
<Entry.SortBy>
|
||||
<Name />
|
||||
</Entry.SortBy>
|
||||
</Entry>
|
||||
<Entry DisplayName="Properties, Indexers">
|
||||
<Entry.Match>
|
||||
<Or>
|
||||
<Kind Is="Property" />
|
||||
<Kind Is="Indexer" />
|
||||
</Or>
|
||||
</Entry.Match>
|
||||
<Entry.SortBy>
|
||||
<Access Order="Public Internal Protected Private" />
|
||||
<Name />
|
||||
</Entry.SortBy>
|
||||
</Entry>
|
||||
<Entry DisplayName="All other members">
|
||||
<Entry.SortBy>
|
||||
<Access Order="Public Internal Protected Private" />
|
||||
<Name />
|
||||
</Entry.SortBy>
|
||||
</Entry>
|
||||
<Entry DisplayName="Nested Types">
|
||||
<Entry.Match>
|
||||
<Kind Is="Type" />
|
||||
</Entry.Match>
|
||||
</Entry>
|
||||
</TypePattern>
|
||||
</Patterns></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/FileHeader/FileHeaderText/@EntryValue">Licensed to the .NET Foundation under one or more agreements.
|
||||
The .NET Foundation licenses this file to you under the MIT license.
|
||||
See the LICENSE file in the project root for more information.</s:String>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EFeature_002EServices_002ECodeCleanup_002EFileHeader_002EFileHeaderSettingsMigrate/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpAttributeForSingleLineMethodUpgrade/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpRenamePlacementToArrangementMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002EMemberReordering_002EMigrations_002ECSharpFileLayoutPatternRemoveIsAttributeUpgrade/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAddAccessorOwnerDeclarationBracesMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAlwaysTreatStructAsNotReorderableMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002ECSharpPlaceAttributeOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=catched/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=CONNACK/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=PINGREQ/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=PINGRESP/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=PUBACK/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=PUBCOMP/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=PUBREC/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=PUBREL/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=SUBACK/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Tnet/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=unsub/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=UNSUBACK/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||
@@ -1,42 +1,44 @@
|
||||
<p align="center">
|
||||
<img src="https://github.com/chkr1011/MQTTnet/blob/master/Images/Logo_128x128.png?raw=true" width="128">
|
||||
<br/>
|
||||
<img src="https://github.com/dotnet/MQTTnet/blob/master/Images/icon_det_256.png?raw=true" width="196">
|
||||
<br/>
|
||||
</p>
|
||||
|
||||
[](https://www.nuget.org/packages/MQTTnet)
|
||||
[](https://ci.appveyor.com/project/chkr1011/mqttnet)
|
||||
[](https://bettercodehub.com/)
|
||||
[](https://opencollective.com/mqttnet)
|
||||
[](https://opencollective.com/mqttnet) [](https://gitter.im/MQTTnet/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
[](https://raw.githubusercontent.com/chkr1011/MQTTnet/master/LICENSE)
|
||||
[](https://github.com/dotnet/MQTTnet/actions/workflows/ci.yml)
|
||||
[](https://www.myget.org/feed/mqttnet/package/nuget/MQTTnet)
|
||||

|
||||
[](https://gitter.im/MQTTnet/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
[](https://raw.githubusercontent.com/dotnet/MQTTnet/master/LICENSE)
|
||||
|
||||
# MQTTnet
|
||||
|
||||
MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker). The implementation is based on the documentation from <http://mqtt.org/>.
|
||||
MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (
|
||||
broker) and supports the MQTT protocol up to version 5. It is compatible with mostly any supported .NET Framework
|
||||
version and CPU architecture.
|
||||
|
||||
## Features
|
||||
|
||||
### General
|
||||
|
||||
* Async support
|
||||
* TLS 1.2 support for client and server (but not UWP servers)
|
||||
* TLS support for client and server (but not UWP servers)
|
||||
* Extensible communication channels (e.g. In-Memory, TCP, TCP+TLS, WS)
|
||||
* Lightweight (only the low level implementation of MQTT, no overhead)
|
||||
* Performance optimized (processing ~70.000 messages / second)*
|
||||
* Performance optimized (processing ~150.000 messages / second)*
|
||||
* Uniform API across all supported versions of the MQTT protocol
|
||||
* Interfaces included for mocking and testing
|
||||
* Access to internal trace messages
|
||||
* Unit tested (~210 tests)
|
||||
* Unit tested (~636 tests)
|
||||
* No external dependencies
|
||||
|
||||
\* Tested on local machine (Intel i7 8700K) with MQTTnet client and server running in the same process using the TCP channel. The app for verification is part of this repository and stored in _/Tests/MQTTnet.TestApp.NetCore_.
|
||||
\* Tested on local machine (Intel i7 8700K) with MQTTnet client and server running in the same process using the TCP
|
||||
channel. The app for verification is part of this repository and stored in _/Tests/MQTTnet.TestApp.NetCore_.
|
||||
|
||||
### Client
|
||||
|
||||
* Communication via TCP (+TLS) or WS (WebSocket) supported
|
||||
* Included core _MqttClient_ with low level functionality
|
||||
* Also included _ManagedMqttClient_ which maintains the connection and subscriptions automatically. Also application messages are queued and re-scheduled for higher QoS levels automatically.
|
||||
* Included core _LowLevelMqttClient_ with low level functionality
|
||||
* Also included _ManagedMqttClient_ which maintains the connection and subscriptions automatically. Also application
|
||||
messages are queued and re-scheduled for higher QoS levels automatically.
|
||||
* Rx support (via another project)
|
||||
* Compatible with Microsoft Azure IoT Hub
|
||||
|
||||
@@ -51,84 +53,31 @@ MQTTnet is a high performance .NET library for MQTT based communication. It prov
|
||||
* WebSockets supported (via ASP.NET Core 2.0, separate nuget)
|
||||
* A custom message interceptor can be added which allows transforming or extending every received application message
|
||||
* Validate subscriptions and deny subscribing of certain topics depending on requesting clients
|
||||
* Connect clients with different protocol versions at the same time.
|
||||
|
||||
## MQTTnet Server
|
||||
## Getting Started
|
||||
|
||||
_MQTTnet Server_ is a standalone cross platform MQTT server (like mosquitto) basing on this library. It has the following features.
|
||||
* Running portable (no installation required)
|
||||
* Runs und Windows, Linux, macOS, Raspberry Pi
|
||||
* Python scripting support for manipulating messages, validation of clients, building business logic etc.
|
||||
* Supports WebSocket and TCP (with and without TLS) connections
|
||||
* Provides a HTTP based API (including Swagger endpoint)
|
||||
* Extensive configuration parameters and customization supported
|
||||
MQTTnet is delivered via <b>NuGet</b> package manager. You can find the packages
|
||||
here: https://www.nuget.org/packages/MQTTnet/
|
||||
|
||||
## Supported frameworks
|
||||
Use these command in the Package Manager console to install MQTTnet manually:
|
||||
|
||||
* .NET Standard 1.3+
|
||||
* .NET Core 1.1+
|
||||
* .NET Core App 1.1+
|
||||
* .NET Framework 4.5.2+ (x86, x64, AnyCPU)
|
||||
* Mono 5.2+
|
||||
* Universal Windows Platform (UWP) 10.0.10240+ (x86, x64, ARM, AnyCPU, Windows 10 IoT Core)
|
||||
* Xamarin.Android 7.5+
|
||||
* Xamarin.iOS 10.14+
|
||||
```
|
||||
Install-Package MQTTnet
|
||||
```
|
||||
|
||||
## Supported MQTT versions
|
||||
Samples for using MQTTnet are part of this repository. For starters these samples are recommended:
|
||||
|
||||
* 5.0.0
|
||||
* 3.1.1
|
||||
* 3.1.0
|
||||
- [Connect with a broker](https://github.com/dotnet/MQTTnet/blob/master/Samples/Client/Client_Connection_Samples.cs)
|
||||
- [Subscribing to data](https://github.com/dotnet/MQTTnet/blob/master/Samples/Client/Client_Subscribe_Samples.cs)
|
||||
- [Publishing data](https://github.com/dotnet/MQTTnet/blob/master/Samples/Client/Client_Publish_Samples.cs)
|
||||
- [Host own broker](https://github.com/dotnet/MQTTnet/blob/master/Samples/Server/Server_Simple_Samples.cs)
|
||||
|
||||
## Nuget
|
||||
## Code of Conduct
|
||||
|
||||
This library is available as a nuget package: <https://www.nuget.org/packages/MQTTnet/>
|
||||
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).
|
||||
|
||||
## Examples
|
||||
## .NET Foundation
|
||||
|
||||
Please find examples and the documentation at the Wiki of this repository (<https://github.com/chkr1011/MQTTnet/wiki>).
|
||||
|
||||
## Contributions
|
||||
|
||||
If you want to contribute to this project just create a pull request. But only pull requests which are matching the code style of this library will be accepted. Before creating a pull request please have a look at the library to get an overview of the required style.
|
||||
Also additions and updates in the Wiki are welcome.
|
||||
|
||||
**This project, like all projects requires time and money. If you feel compelled to support the development team, you can do so through Open Collective (https://opencollective.com/mqttnet)**.
|
||||
|
||||
## References
|
||||
|
||||
This library is used in the following projects:
|
||||
|
||||
* Azure Functions MQTT Bindings, [CaseOnline.Azure.WebJobs.Extensions.Mqtt](https://github.com/keesschollaart81/CaseOnline.Azure.WebJobs.Extensions.Mqtt/)
|
||||
* HA4IoT (Open Source Home Automation system for .NET, <https://github.com/chkr1011/HA4IoT>)
|
||||
* MQTT Client Rx (Wrapper for Reactive Extensions, <https://github.com/1iveowl/MQTTClient.rx>)
|
||||
* MQTT Tester (MQTT client test app for [Android](https://play.google.com/store/apps/details?id=com.liveowl.mqtttester) and [iOS](https://itunes.apple.com/us/app/mqtt-tester/id1278621826?mt=8))
|
||||
* Wirehome.Core (Open Source Home Automation system for .NET Core, <https://github.com/chkr1011/Wirehome.Core>)
|
||||
|
||||
Further projects using this project can be found under https://github.com/chkr1011/MQTTnet/network/dependents.
|
||||
|
||||
If you use this library and want to see your project here please create a pull request.
|
||||
|
||||
## License
|
||||
|
||||
MIT License
|
||||
|
||||
MQTTnet Copyright (c) 2016-2019 Christian Kratky
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
This project is supported by the [.NET Foundation](https://dotnetfoundation.org).
|
||||
|
||||
@@ -0,0 +1,522 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
// ReSharper disable UnusedType.Global
|
||||
// ReSharper disable UnusedMember.Global
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
using System.Security.Authentication;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Samples.Helpers;
|
||||
using MQTTnet.Server;
|
||||
using MQTTnet.Server.EnhancedAuthentication;
|
||||
|
||||
namespace MQTTnet.Samples.Client;
|
||||
|
||||
public static class Client_Connection_Samples
|
||||
{
|
||||
const string mosquitto_org = @"
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEAzCCAuugAwIBAgIUBY1hlCGvdj4NhBXkZ/uLUZNILAwwDQYJKoZIhvcNAQEL
|
||||
BQAwgZAxCzAJBgNVBAYTAkdCMRcwFQYDVQQIDA5Vbml0ZWQgS2luZ2RvbTEOMAwG
|
||||
A1UEBwwFRGVyYnkxEjAQBgNVBAoMCU1vc3F1aXR0bzELMAkGA1UECwwCQ0ExFjAU
|
||||
BgNVBAMMDW1vc3F1aXR0by5vcmcxHzAdBgkqhkiG9w0BCQEWEHJvZ2VyQGF0Y2hv
|
||||
by5vcmcwHhcNMjAwNjA5MTEwNjM5WhcNMzAwNjA3MTEwNjM5WjCBkDELMAkGA1UE
|
||||
BhMCR0IxFzAVBgNVBAgMDlVuaXRlZCBLaW5nZG9tMQ4wDAYDVQQHDAVEZXJieTES
|
||||
MBAGA1UECgwJTW9zcXVpdHRvMQswCQYDVQQLDAJDQTEWMBQGA1UEAwwNbW9zcXVp
|
||||
dHRvLm9yZzEfMB0GCSqGSIb3DQEJARYQcm9nZXJAYXRjaG9vLm9yZzCCASIwDQYJ
|
||||
KoZIhvcNAQEBBQADggEPADCCAQoCggEBAME0HKmIzfTOwkKLT3THHe+ObdizamPg
|
||||
UZmD64Tf3zJdNeYGYn4CEXbyP6fy3tWc8S2boW6dzrH8SdFf9uo320GJA9B7U1FW
|
||||
Te3xda/Lm3JFfaHjkWw7jBwcauQZjpGINHapHRlpiCZsquAthOgxW9SgDgYlGzEA
|
||||
s06pkEFiMw+qDfLo/sxFKB6vQlFekMeCymjLCbNwPJyqyhFmPWwio/PDMruBTzPH
|
||||
3cioBnrJWKXc3OjXdLGFJOfj7pP0j/dr2LH72eSvv3PQQFl90CZPFhrCUcRHSSxo
|
||||
E6yjGOdnz7f6PveLIB574kQORwt8ePn0yidrTC1ictikED3nHYhMUOUCAwEAAaNT
|
||||
MFEwHQYDVR0OBBYEFPVV6xBUFPiGKDyo5V3+Hbh4N9YSMB8GA1UdIwQYMBaAFPVV
|
||||
6xBUFPiGKDyo5V3+Hbh4N9YSMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL
|
||||
BQADggEBAGa9kS21N70ThM6/Hj9D7mbVxKLBjVWe2TPsGfbl3rEDfZ+OKRZ2j6AC
|
||||
6r7jb4TZO3dzF2p6dgbrlU71Y/4K0TdzIjRj3cQ3KSm41JvUQ0hZ/c04iGDg/xWf
|
||||
+pp58nfPAYwuerruPNWmlStWAXf0UTqRtg4hQDWBuUFDJTuWuuBvEXudz74eh/wK
|
||||
sMwfu1HFvjy5Z0iMDU8PUDepjVolOCue9ashlS4EB5IECdSR2TItnAIiIwimx839
|
||||
LdUdRudafMu5T5Xma182OC0/u/xRlEm+tvKGGmfFcN0piqVl8OrSPBgIlb+1IKJE
|
||||
m/XriWr/Cq4h/JfB7NTsezVslgkBaoU=
|
||||
-----END CERTIFICATE-----
|
||||
";
|
||||
|
||||
public static async Task Clean_Disconnect()
|
||||
{
|
||||
/*
|
||||
* This sample disconnects in a clean way. This will send a MQTT DISCONNECT packet
|
||||
* to the server and close the connection afterward.
|
||||
*
|
||||
* See sample _Connect_Client_ for more details.
|
||||
*/
|
||||
|
||||
var mqttFactory = new MqttClientFactory();
|
||||
|
||||
using var mqttClient = mqttFactory.CreateMqttClient();
|
||||
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
|
||||
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
||||
|
||||
// This will send the DISCONNECT packet. Calling _Dispose_ without DisconnectAsync the
|
||||
// connection is closed in a "not clean" way. See MQTT specification for more details.
|
||||
await mqttClient.DisconnectAsync(new MqttClientDisconnectOptionsBuilder().WithReason(MqttClientDisconnectOptionsReason.NormalDisconnection).Build());
|
||||
}
|
||||
|
||||
public static async Task Connect()
|
||||
{
|
||||
/*
|
||||
* This sample creates a simple MQTT client and connects to a public broker.
|
||||
*
|
||||
* Always dispose the client when it is no longer used.
|
||||
* The default version of MQTT is 3.1.1.
|
||||
*/
|
||||
|
||||
var mqttFactory = new MqttClientFactory();
|
||||
|
||||
using var mqttClient = mqttFactory.CreateMqttClient();
|
||||
// Use builder classes where possible in this project.
|
||||
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
|
||||
|
||||
// This will throw an exception if the server is not available.
|
||||
// The result from this message returns additional data which was sent
|
||||
// from the server. Please refer to the MQTT protocol specification for details.
|
||||
var response = await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
||||
|
||||
Console.WriteLine("The MQTT client is connected.");
|
||||
|
||||
response.DumpToConsole();
|
||||
|
||||
// Send a clean disconnect to the server by calling _DisconnectAsync_. Without this the TCP connection
|
||||
// gets dropped and the server will handle this as a non clean disconnect (see MQTT spec for details).
|
||||
var mqttClientDisconnectOptions = mqttFactory.CreateClientDisconnectOptionsBuilder().Build();
|
||||
|
||||
await mqttClient.DisconnectAsync(mqttClientDisconnectOptions, CancellationToken.None);
|
||||
}
|
||||
|
||||
public static async Task Connect_Using_Enhanced_Authentication()
|
||||
{
|
||||
/*
|
||||
* This sample uses enhanced authentication (Kerberos) when creating the connection.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Server part...
|
||||
*/
|
||||
|
||||
var mqttServerFactory = new MqttServerFactory();
|
||||
|
||||
var serverOptions = mqttServerFactory.CreateServerOptionsBuilder().WithDefaultEndpoint().Build();
|
||||
var server = mqttServerFactory.CreateMqttServer(serverOptions);
|
||||
|
||||
server.ValidatingConnectionAsync += async args =>
|
||||
{
|
||||
if (args.AuthenticationMethod == "GS2-KRB5")
|
||||
{
|
||||
var result = await args.ExchangeEnhancedAuthenticationAsync(new ExchangeEnhancedAuthenticationOptions(), args.CancellationToken);
|
||||
|
||||
Console.WriteLine($"Received AUTH data from client: {Encoding.UTF8.GetString(result.AuthenticationData)}");
|
||||
|
||||
var authOptions = mqttServerFactory.CreateExchangeExtendedAuthenticationOptionsBuilder().WithAuthenticationData("reply context token").Build();
|
||||
|
||||
result = await args.ExchangeEnhancedAuthenticationAsync(authOptions, args.CancellationToken);
|
||||
|
||||
Console.WriteLine($"Received AUTH data from client: {Encoding.UTF8.GetString(result.AuthenticationData)}");
|
||||
|
||||
args.ResponseAuthenticationData = "outcome of authentication"u8.ToArray();
|
||||
|
||||
// Authentication DONE!
|
||||
args.ReasonCode = MqttConnectReasonCode.Success; // Also the default!
|
||||
}
|
||||
else
|
||||
{
|
||||
args.ReasonCode = MqttConnectReasonCode.BadAuthenticationMethod;
|
||||
}
|
||||
};
|
||||
|
||||
await server.StartAsync();
|
||||
|
||||
/*
|
||||
* Client part...
|
||||
*/
|
||||
|
||||
var mqttClientFactory = new MqttClientFactory();
|
||||
|
||||
// Use Kerberos sample from the MQTT RFC.
|
||||
var kerberosAuthenticationHandler = new SampleClientKerberosAuthenticationHandler();
|
||||
|
||||
var clientOptions = mqttClientFactory.CreateClientOptionsBuilder()
|
||||
.WithTcpServer("localhost")
|
||||
.WithProtocolVersion(MqttProtocolVersion.V500)
|
||||
.WithEnhancedAuthenticationHandler(kerberosAuthenticationHandler)
|
||||
.WithEnhancedAuthentication("GS2-KRB5")
|
||||
.Build();
|
||||
|
||||
var client = mqttClientFactory.CreateMqttClient();
|
||||
|
||||
var result = await client.ConnectAsync(clientOptions);
|
||||
|
||||
Console.WriteLine($"Client connect result: {result.ResultCode}");
|
||||
}
|
||||
|
||||
public static async Task Connect_Using_MQTTv5()
|
||||
{
|
||||
/*
|
||||
* This sample creates a simple MQTT client and connects to a public broker using MQTTv5.
|
||||
*
|
||||
* This is a modified version of the sample _Connect_Client_! See other sample for more details.
|
||||
*/
|
||||
|
||||
var mqttFactory = new MqttClientFactory();
|
||||
|
||||
using var mqttClient = mqttFactory.CreateMqttClient();
|
||||
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").WithProtocolVersion(MqttProtocolVersion.V500).Build();
|
||||
|
||||
// In MQTTv5 the response contains much more information.
|
||||
var response = await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
||||
|
||||
Console.WriteLine("The MQTT client is connected.");
|
||||
|
||||
response.DumpToConsole();
|
||||
}
|
||||
|
||||
public static async Task Connect_Using_TLS_1_2()
|
||||
{
|
||||
/*
|
||||
* This sample creates a simple MQTT client and connects to a public broker using TLS 1.2 encryption.
|
||||
*
|
||||
* This is a modified version of the sample _Connect_Client_! See other sample for more details.
|
||||
*/
|
||||
|
||||
var mqttFactory = new MqttClientFactory();
|
||||
|
||||
using var mqttClient = mqttFactory.CreateMqttClient();
|
||||
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("mqtt.fluux.io")
|
||||
.WithTlsOptions(
|
||||
o =>
|
||||
{
|
||||
// The used public broker sometimes has invalid certificates. This sample accepts all
|
||||
// certificates. This should not be used in live environments.
|
||||
o.WithCertificateValidationHandler(_ => true);
|
||||
|
||||
// The default value is determined by the OS. Set manually to force version.
|
||||
o.WithSslProtocols(SslProtocols.Tls12);
|
||||
})
|
||||
.Build();
|
||||
|
||||
using var timeout = new CancellationTokenSource(5000);
|
||||
await mqttClient.ConnectAsync(mqttClientOptions, timeout.Token);
|
||||
|
||||
Console.WriteLine("The MQTT client is connected.");
|
||||
}
|
||||
|
||||
public static async Task Connect_Using_TLS_Encryption()
|
||||
{
|
||||
/*
|
||||
* This sample creates a simple MQTT client and connects to a public broker with enabled TLS encryption.
|
||||
*
|
||||
* This is a modified version of the sample _Connect_Client_! See other sample for more details.
|
||||
*/
|
||||
|
||||
var mqttFactory = new MqttClientFactory();
|
||||
|
||||
using var mqttClient = mqttFactory.CreateMqttClient();
|
||||
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("test.mosquitto.org", 8883)
|
||||
.WithTlsOptions(
|
||||
o => o.WithCertificateValidationHandler(
|
||||
// The used public broker sometimes has invalid certificates. This sample accepts all
|
||||
// certificates. This should not be used in live environments.
|
||||
_ => true))
|
||||
.Build();
|
||||
|
||||
// In MQTTv5 the response contains much more information.
|
||||
using var timeout = new CancellationTokenSource(5000);
|
||||
var response = await mqttClient.ConnectAsync(mqttClientOptions, timeout.Token);
|
||||
|
||||
Console.WriteLine("The MQTT client is connected.");
|
||||
|
||||
response.DumpToConsole();
|
||||
}
|
||||
|
||||
public static async Task Connect_Using_TLS_With_CA_File()
|
||||
{
|
||||
var mqttFactory = new MqttClientFactory();
|
||||
|
||||
var caChain = new X509Certificate2Collection();
|
||||
caChain.ImportFromPem(mosquitto_org); // from https://test.mosquitto.org/ssl/mosquitto.org.crt
|
||||
|
||||
using var mqttClient = mqttFactory.CreateMqttClient();
|
||||
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("test.mosquitto.org", 8883)
|
||||
.WithTlsOptions(new MqttClientTlsOptionsBuilder().WithTrustChain(caChain).Build())
|
||||
.Build();
|
||||
|
||||
var connAck = await mqttClient.ConnectAsync(mqttClientOptions);
|
||||
Console.WriteLine("Connected to test.moquitto.org:8883 with CaFile mosquitto.org.crt: " + connAck.ResultCode);
|
||||
}
|
||||
|
||||
public static async Task Connect_Using_WebSockets()
|
||||
{
|
||||
/*
|
||||
* This sample creates a simple MQTT client and connects to a public broker using a WebSocket connection.
|
||||
*
|
||||
* This is a modified version of the sample _Connect_Client_! See other sample for more details.
|
||||
*/
|
||||
|
||||
var mqttFactory = new MqttClientFactory();
|
||||
|
||||
using var mqttClient = mqttFactory.CreateMqttClient();
|
||||
var mqttClientOptions = new MqttClientOptionsBuilder().WithWebSocketServer(o => o.WithUri("broker.hivemq.com:8000/mqtt")).Build();
|
||||
|
||||
var response = await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
||||
|
||||
Console.WriteLine("The MQTT client is connected.");
|
||||
|
||||
response.DumpToConsole();
|
||||
}
|
||||
|
||||
public static async Task Connect_With_Amazon_AWS()
|
||||
{
|
||||
/*
|
||||
* This sample creates a simple MQTT client and connects to an Amazon Web Services broker.
|
||||
*
|
||||
* The broker requires special settings which are set here.
|
||||
*/
|
||||
|
||||
var mqttFactory = new MqttClientFactory();
|
||||
|
||||
using var mqttClient = mqttFactory.CreateMqttClient();
|
||||
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("amazon.web.services.broker")
|
||||
// Disabling packet fragmentation is very important!
|
||||
.WithoutPacketFragmentation()
|
||||
.Build();
|
||||
|
||||
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
||||
|
||||
Console.WriteLine("The MQTT client is connected.");
|
||||
|
||||
await mqttClient.DisconnectAsync();
|
||||
}
|
||||
|
||||
public static async Task Disconnect_Clean()
|
||||
{
|
||||
/*
|
||||
* This sample disconnects from the server with sending a DISCONNECT packet.
|
||||
* This way of disconnecting is treated as a clean disconnect which will not
|
||||
* trigger sending the last will etc.
|
||||
*/
|
||||
|
||||
var mqttFactory = new MqttClientFactory();
|
||||
|
||||
using var mqttClient = mqttFactory.CreateMqttClient();
|
||||
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
|
||||
|
||||
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
||||
|
||||
// Calling _DisconnectAsync_ will send a DISCONNECT packet before closing the connection.
|
||||
// Using a reason code requires MQTT version 5.0.0!
|
||||
await mqttClient.DisconnectAsync(MqttClientDisconnectOptionsReason.ImplementationSpecificError);
|
||||
}
|
||||
|
||||
public static async Task Disconnect_Non_Clean()
|
||||
{
|
||||
/*
|
||||
* This sample disconnects from the server without sending a DISCONNECT packet.
|
||||
* This way of disconnecting is treated as a non-clean disconnect which will
|
||||
* trigger sending the last will etc.
|
||||
*/
|
||||
|
||||
var mqttFactory = new MqttClientFactory();
|
||||
|
||||
var mqttClient = mqttFactory.CreateMqttClient();
|
||||
|
||||
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
|
||||
|
||||
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
||||
|
||||
// Calling _Dispose_ or use of a _using_ statement will close the transport connection
|
||||
// without sending a DISCONNECT packet to the server.
|
||||
mqttClient.Dispose();
|
||||
}
|
||||
|
||||
public static async Task Inspect_Certificate_Validation_Errors()
|
||||
{
|
||||
/*
|
||||
* This sample prints the certificate information while connection. This data can be used to decide whether a connection is secure or not
|
||||
* including the reason for that status.
|
||||
*/
|
||||
|
||||
var mqttFactory = new MqttClientFactory();
|
||||
|
||||
using var mqttClient = mqttFactory.CreateMqttClient();
|
||||
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("mqtt.fluux.io", 8883)
|
||||
.WithTlsOptions(
|
||||
o =>
|
||||
{
|
||||
o.WithCertificateValidationHandler(
|
||||
eventArgs =>
|
||||
{
|
||||
eventArgs.Certificate.Subject.DumpToConsole();
|
||||
eventArgs.Certificate.GetExpirationDateString().DumpToConsole();
|
||||
eventArgs.Chain.ChainPolicy.RevocationMode.DumpToConsole();
|
||||
eventArgs.Chain.ChainStatus.DumpToConsole();
|
||||
eventArgs.SslPolicyErrors.DumpToConsole();
|
||||
return true;
|
||||
});
|
||||
})
|
||||
.Build();
|
||||
|
||||
// In MQTTv5 the response contains much more information.
|
||||
using var timeout = new CancellationTokenSource(5000);
|
||||
await mqttClient.ConnectAsync(mqttClientOptions, timeout.Token);
|
||||
}
|
||||
|
||||
public static async Task Ping_Server()
|
||||
{
|
||||
/*
|
||||
* This sample sends a PINGREQ packet to the server and waits for a reply.
|
||||
*
|
||||
* This is only supported in MQTTv5.0.0+.
|
||||
*/
|
||||
|
||||
var mqttFactory = new MqttClientFactory();
|
||||
|
||||
using var mqttClient = mqttFactory.CreateMqttClient();
|
||||
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
|
||||
|
||||
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
||||
|
||||
// This will throw an exception if the server does not reply.
|
||||
await mqttClient.PingAsync(CancellationToken.None);
|
||||
|
||||
Console.WriteLine("The MQTT server replied to the ping request.");
|
||||
}
|
||||
|
||||
public static async Task Reconnect_Using_Event()
|
||||
{
|
||||
/*
|
||||
* This sample shows how to reconnect when the connection was dropped.
|
||||
* This approach uses one of the events from the client.
|
||||
* This approach has a risk of deadlocks! Consider using the timer approach (see sample).
|
||||
*/
|
||||
|
||||
var mqttFactory = new MqttClientFactory();
|
||||
|
||||
using var mqttClient = mqttFactory.CreateMqttClient();
|
||||
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
|
||||
|
||||
mqttClient.DisconnectedAsync += async e =>
|
||||
{
|
||||
if (e.ClientWasConnected)
|
||||
{
|
||||
// Use the current options as the new options.
|
||||
await mqttClient.ConnectAsync(mqttClient.Options);
|
||||
}
|
||||
};
|
||||
|
||||
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
||||
}
|
||||
|
||||
public static void Reconnect_Using_Timer()
|
||||
{
|
||||
/*
|
||||
* This sample shows how to reconnect when the connection was dropped.
|
||||
* This approach uses a custom Task/Thread which will monitor the connection status.
|
||||
* This is the recommended way but requires more custom code!
|
||||
*/
|
||||
|
||||
var mqttFactory = new MqttClientFactory();
|
||||
|
||||
using var mqttClient = mqttFactory.CreateMqttClient();
|
||||
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
|
||||
|
||||
_ = Task.Run(
|
||||
async () =>
|
||||
{
|
||||
// User proper cancellation and no while(true).
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
// This code will also do the very first connect! So no call to _ConnectAsync_ is required in the first place.
|
||||
if (!await mqttClient.TryPingAsync())
|
||||
{
|
||||
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
||||
|
||||
// Subscribe to topics when session is clean etc.
|
||||
Console.WriteLine("The MQTT client is connected.");
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Handle the exception properly (logging etc.).
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Check the connection state every 5 seconds and perform a reconnect if required.
|
||||
await Task.Delay(TimeSpan.FromSeconds(5));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Console.WriteLine("Press <Enter> to exit");
|
||||
Console.ReadLine();
|
||||
}
|
||||
|
||||
public static async Task Timeout()
|
||||
{
|
||||
/*
|
||||
* This sample creates a simple MQTT client and connects to an invalid broker using a timeout.
|
||||
*
|
||||
* This is a modified version of the sample _Connect_Client_! See other sample for more details.
|
||||
*/
|
||||
|
||||
var mqttFactory = new MqttClientFactory();
|
||||
|
||||
using var mqttClient = mqttFactory.CreateMqttClient();
|
||||
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("127.0.0.1").Build();
|
||||
|
||||
try
|
||||
{
|
||||
using var timeoutToken = new CancellationTokenSource(TimeSpan.FromSeconds(1));
|
||||
await mqttClient.ConnectAsync(mqttClientOptions, timeoutToken.Token);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
Console.WriteLine("Timeout while connecting.");
|
||||
}
|
||||
}
|
||||
|
||||
class SampleClientKerberosAuthenticationHandler : IMqttEnhancedAuthenticationHandler
|
||||
{
|
||||
public async Task HandleEnhancedAuthenticationAsync(MqttEnhancedAuthenticationEventArgs eventArgs)
|
||||
{
|
||||
if (eventArgs.AuthenticationMethod != "GS2-KRB5")
|
||||
{
|
||||
throw new InvalidOperationException("Wrong authentication method");
|
||||
}
|
||||
|
||||
var sendOptions = new SendMqttEnhancedAuthenticationDataOptions
|
||||
{
|
||||
Data = "initial context token"u8.ToArray()
|
||||
};
|
||||
|
||||
await eventArgs.SendAsync(sendOptions);
|
||||
|
||||
var response = await eventArgs.ReceiveAsync(CancellationToken.None);
|
||||
|
||||
Console.WriteLine($"Received AUTH data from server: {Encoding.UTF8.GetString(response.AuthenticationData)}");
|
||||
|
||||
// No further data is required, but we have to fulfil the exchange.
|
||||
sendOptions = new SendMqttEnhancedAuthenticationDataOptions
|
||||
{
|
||||
Data = []
|
||||
};
|
||||
|
||||
await eventArgs.SendAsync(sendOptions, CancellationToken.None);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
// ReSharper disable UnusedType.Global
|
||||
// ReSharper disable UnusedMember.Global
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
namespace MQTTnet.Samples.Client;
|
||||
|
||||
public static class Client_Publish_Samples
|
||||
{
|
||||
public static async Task Publish_Application_Message()
|
||||
{
|
||||
/*
|
||||
* This sample pushes a simple application message including a topic and a payload.
|
||||
*
|
||||
* Always use builders where they exist. Builders (in this project) are designed to be
|
||||
* backward compatible. Creating an _MqttApplicationMessage_ via its constructor is also
|
||||
* supported but the class might change often in future releases where the builder does not
|
||||
* or at least provides backward compatibility where possible.
|
||||
*/
|
||||
|
||||
var mqttFactory = new MqttClientFactory();
|
||||
|
||||
using (var mqttClient = mqttFactory.CreateMqttClient())
|
||||
{
|
||||
var mqttClientOptions = new MqttClientOptionsBuilder()
|
||||
.WithTcpServer("broker.hivemq.com")
|
||||
.Build();
|
||||
|
||||
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
||||
|
||||
var applicationMessage = new MqttApplicationMessageBuilder()
|
||||
.WithTopic("samples/temperature/living_room")
|
||||
.WithPayload("19.5")
|
||||
.Build();
|
||||
|
||||
await mqttClient.PublishAsync(applicationMessage, CancellationToken.None);
|
||||
|
||||
await mqttClient.DisconnectAsync();
|
||||
|
||||
Console.WriteLine("MQTT application message is published.");
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task Publish_Multiple_Application_Messages()
|
||||
{
|
||||
/*
|
||||
* This sample pushes multiple simple application message including a topic and a payload.
|
||||
*
|
||||
* See sample _Publish_Application_Message_ for more details.
|
||||
*/
|
||||
|
||||
var mqttFactory = new MqttClientFactory();
|
||||
|
||||
using (var mqttClient = mqttFactory.CreateMqttClient())
|
||||
{
|
||||
var mqttClientOptions = new MqttClientOptionsBuilder()
|
||||
.WithTcpServer("broker.hivemq.com")
|
||||
.Build();
|
||||
|
||||
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
||||
|
||||
var applicationMessage = new MqttApplicationMessageBuilder()
|
||||
.WithTopic("samples/temperature/living_room")
|
||||
.WithPayload("19.5")
|
||||
.Build();
|
||||
|
||||
await mqttClient.PublishAsync(applicationMessage, CancellationToken.None);
|
||||
|
||||
applicationMessage = new MqttApplicationMessageBuilder()
|
||||
.WithTopic("samples/temperature/living_room")
|
||||
.WithPayload("20.0")
|
||||
.Build();
|
||||
|
||||
await mqttClient.PublishAsync(applicationMessage, CancellationToken.None);
|
||||
|
||||
applicationMessage = new MqttApplicationMessageBuilder()
|
||||
.WithTopic("samples/temperature/living_room")
|
||||
.WithPayload("21.0")
|
||||
.Build();
|
||||
|
||||
await mqttClient.PublishAsync(applicationMessage, CancellationToken.None);
|
||||
|
||||
await mqttClient.DisconnectAsync();
|
||||
|
||||
Console.WriteLine("MQTT application message is published.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
// ReSharper disable UnusedType.Global
|
||||
// ReSharper disable UnusedMember.Global
|
||||
// ReSharper disable InconsistentNaming
|
||||
// ReSharper disable UnusedMember.Local
|
||||
|
||||
using MQTTnet.Extensions.TopicTemplate;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Samples.Helpers;
|
||||
|
||||
namespace MQTTnet.Samples.Client;
|
||||
|
||||
public static class Client_Subscribe_Samples
|
||||
{
|
||||
static readonly MqttTopicTemplate sampleTemplate = new("mqttnet/samples/topic/{id}");
|
||||
|
||||
public static async Task Handle_Received_Application_Message()
|
||||
{
|
||||
/*
|
||||
* This sample subscribes to a topic and processes the received message.
|
||||
*/
|
||||
|
||||
var mqttFactory = new MqttClientFactory();
|
||||
|
||||
using (var mqttClient = mqttFactory.CreateMqttClient())
|
||||
{
|
||||
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
|
||||
|
||||
// Setup message handling before connecting so that queued messages
|
||||
// are also handled properly. When there is no event handler attached all
|
||||
// received messages get lost.
|
||||
mqttClient.ApplicationMessageReceivedAsync += e =>
|
||||
{
|
||||
Console.WriteLine("Received application message.");
|
||||
e.DumpToConsole();
|
||||
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
||||
|
||||
var mqttSubscribeOptions = mqttFactory.CreateSubscribeOptionsBuilder().WithTopicTemplate(sampleTemplate.WithParameter("id", "2")).Build();
|
||||
|
||||
await mqttClient.SubscribeAsync(mqttSubscribeOptions, CancellationToken.None);
|
||||
|
||||
Console.WriteLine("MQTT client subscribed to topic.");
|
||||
|
||||
Console.WriteLine("Press enter to exit.");
|
||||
Console.ReadLine();
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task Send_Responses()
|
||||
{
|
||||
/*
|
||||
* This sample subscribes to a topic and sends a response to the broker. This requires at least QoS level 1 to work!
|
||||
*/
|
||||
|
||||
var mqttFactory = new MqttClientFactory();
|
||||
|
||||
using (var mqttClient = mqttFactory.CreateMqttClient())
|
||||
{
|
||||
mqttClient.ApplicationMessageReceivedAsync += delegate(MqttApplicationMessageReceivedEventArgs args)
|
||||
{
|
||||
// Do some work with the message...
|
||||
|
||||
// Now respond to the broker with a reason code other than success.
|
||||
args.ReasonCode = MqttApplicationMessageReceivedReasonCode.ImplementationSpecificError;
|
||||
args.ResponseReasonString = "That did not work!";
|
||||
|
||||
// User properties require MQTT v5!
|
||||
args.ResponseUserProperties.Add(new MqttUserProperty("My", "Data"));
|
||||
|
||||
// Now the broker will resend the message again.
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
|
||||
|
||||
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
||||
|
||||
var mqttSubscribeOptions = mqttFactory.CreateSubscribeOptionsBuilder().WithTopicTemplate(sampleTemplate.WithParameter("id", "1")).Build();
|
||||
|
||||
var response = await mqttClient.SubscribeAsync(mqttSubscribeOptions, CancellationToken.None);
|
||||
|
||||
Console.WriteLine("MQTT client subscribed to topic.");
|
||||
|
||||
// The response contains additional data sent by the server after subscribing.
|
||||
response.DumpToConsole();
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task Subscribe_Multiple_Topics()
|
||||
{
|
||||
/*
|
||||
* This sample subscribes to several topics in a single request.
|
||||
*/
|
||||
|
||||
var mqttFactory = new MqttClientFactory();
|
||||
|
||||
using (var mqttClient = mqttFactory.CreateMqttClient())
|
||||
{
|
||||
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
|
||||
|
||||
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
||||
|
||||
// Create the subscribe options including several topics with different options.
|
||||
// It is also possible to all of these topics using a dedicated call of _SubscribeAsync_ per topic.
|
||||
var mqttSubscribeOptions = mqttFactory.CreateSubscribeOptionsBuilder()
|
||||
.WithTopicTemplate(sampleTemplate.WithParameter("id", "1"))
|
||||
.WithTopicTemplate(sampleTemplate.WithParameter("id", "2"), noLocal: true)
|
||||
.WithTopicTemplate(sampleTemplate.WithParameter("id", "3"), retainHandling: MqttRetainHandling.SendAtSubscribe)
|
||||
.Build();
|
||||
|
||||
var response = await mqttClient.SubscribeAsync(mqttSubscribeOptions, CancellationToken.None);
|
||||
|
||||
Console.WriteLine("MQTT client subscribed to topics.");
|
||||
|
||||
// The response contains additional data sent by the server after subscribing.
|
||||
response.DumpToConsole();
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task Subscribe_Topic()
|
||||
{
|
||||
/*
|
||||
* This sample subscribes to a topic.
|
||||
*/
|
||||
|
||||
var mqttFactory = new MqttClientFactory();
|
||||
|
||||
using (var mqttClient = mqttFactory.CreateMqttClient())
|
||||
{
|
||||
var mqttClientOptions = new MqttClientOptionsBuilder().WithTcpServer("broker.hivemq.com").Build();
|
||||
|
||||
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
||||
|
||||
var mqttSubscribeOptions = mqttFactory.CreateSubscribeOptionsBuilder().WithTopicTemplate(sampleTemplate.WithParameter("id", "1")).Build();
|
||||
|
||||
var response = await mqttClient.SubscribeAsync(mqttSubscribeOptions, CancellationToken.None);
|
||||
|
||||
Console.WriteLine("MQTT client subscribed to topic.");
|
||||
|
||||
// The response contains additional data sent by the server after subscribing.
|
||||
response.DumpToConsole();
|
||||
}
|
||||
}
|
||||
|
||||
static void ConcurrentProcessingDisableAutoAcknowledge(CancellationToken shutdownToken, IMqttClient mqttClient)
|
||||
{
|
||||
/*
|
||||
* This sample shows how to achieve concurrent processing and not have message AutoAcknowledged
|
||||
* This to have a proper QoS1 (at-least-once) experience for what at least MQTT specification can provide
|
||||
*/
|
||||
mqttClient.ApplicationMessageReceivedAsync += ea =>
|
||||
{
|
||||
ea.AutoAcknowledge = false;
|
||||
|
||||
async Task ProcessAsync()
|
||||
{
|
||||
// DO YOUR WORK HERE!
|
||||
await Task.Delay(1000, shutdownToken);
|
||||
await ea.AcknowledgeAsync(shutdownToken);
|
||||
// WARNING: If process failures are not transient the message will be retried on every restart of the client
|
||||
// A failed message will not be dispatched again to the client as MQTT does not have a NACK packet to let
|
||||
// the broker know processing failed
|
||||
//
|
||||
// Optionally: Use a framework like Polly to create a retry policy: https://github.com/App-vNext/Polly#retry
|
||||
}
|
||||
|
||||
_ = Task.Run(ProcessAsync, shutdownToken);
|
||||
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
}
|
||||
|
||||
static void ConcurrentProcessingWithLimit(CancellationToken shutdownToken, IMqttClient mqttClient)
|
||||
{
|
||||
/*
|
||||
* This sample shows how to achieve concurrent processing, with:
|
||||
* - a maximum concurrency limit based on Environment.ProcessorCount
|
||||
*/
|
||||
|
||||
var concurrent = new SemaphoreSlim(Environment.ProcessorCount);
|
||||
|
||||
mqttClient.ApplicationMessageReceivedAsync += async ea =>
|
||||
{
|
||||
await concurrent.WaitAsync(shutdownToken).ConfigureAwait(false);
|
||||
|
||||
async Task ProcessAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
// DO YOUR WORK HERE!
|
||||
await Task.Delay(1000, shutdownToken);
|
||||
}
|
||||
finally
|
||||
{
|
||||
concurrent.Release();
|
||||
}
|
||||
}
|
||||
|
||||
_ = Task.Run(ProcessAsync, shutdownToken);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
// ReSharper disable UnusedType.Global
|
||||
// ReSharper disable UnusedMember.Global
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
using System.Text;
|
||||
using MQTTnet.Diagnostics.Logger;
|
||||
|
||||
namespace MQTTnet.Samples.Diagnostics;
|
||||
|
||||
public static class Logger_Samples
|
||||
{
|
||||
public static async Task Create_Custom_Logger()
|
||||
{
|
||||
/*
|
||||
* This sample covers the creation of a custom logger which can be used to forward MQTTnet log messages
|
||||
* to other loggers like Microsoft logger or Serilog or log4net etc.
|
||||
*/
|
||||
|
||||
var mqttFactory = new MqttClientFactory(new MyLogger());
|
||||
|
||||
var mqttClientOptions = mqttFactory.CreateClientOptionsBuilder()
|
||||
.WithTcpServer("broker.hivemq.com")
|
||||
.Build();
|
||||
|
||||
using (var mqttClient = mqttFactory.CreateMqttClient())
|
||||
{
|
||||
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
||||
|
||||
Console.WriteLine("MQTT client is connected.");
|
||||
|
||||
var mqttClientDisconnectOptions = mqttFactory.CreateClientDisconnectOptionsBuilder()
|
||||
.Build();
|
||||
|
||||
await mqttClient.DisconnectAsync(mqttClientDisconnectOptions, CancellationToken.None);
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task Use_Event_Logger()
|
||||
{
|
||||
/*
|
||||
* This sample shows how to get logs from the library.
|
||||
*
|
||||
* ATTENTION: Only use the logger for debugging etc. The performance is heavily decreased when a logger is used.
|
||||
*/
|
||||
|
||||
// The logger ID is optional but can be set do distinguish different logger instances.
|
||||
var mqttEventLogger = new MqttNetEventLogger("MyCustomLogger");
|
||||
|
||||
mqttEventLogger.LogMessagePublished += (sender, args) =>
|
||||
{
|
||||
var output = new StringBuilder();
|
||||
output.AppendLine($">> [{args.LogMessage.Timestamp:O}] [{args.LogMessage.ThreadId}] [{args.LogMessage.Source}] [{args.LogMessage.Level}]: {args.LogMessage.Message}");
|
||||
if (args.LogMessage.Exception != null)
|
||||
{
|
||||
output.AppendLine(args.LogMessage.Exception.ToString());
|
||||
}
|
||||
|
||||
Console.Write(output);
|
||||
};
|
||||
|
||||
var mqttFactory = new MqttClientFactory(mqttEventLogger);
|
||||
|
||||
var mqttClientOptions = mqttFactory.CreateClientOptionsBuilder()
|
||||
.WithTcpServer("broker.hivemq.com")
|
||||
.Build();
|
||||
|
||||
using (var mqttClient = mqttFactory.CreateMqttClient())
|
||||
{
|
||||
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
||||
|
||||
Console.WriteLine("MQTT client is connected.");
|
||||
|
||||
var mqttClientDisconnectOptions = mqttFactory.CreateClientDisconnectOptionsBuilder()
|
||||
.Build();
|
||||
|
||||
await mqttClient.DisconnectAsync(mqttClientDisconnectOptions, CancellationToken.None);
|
||||
}
|
||||
}
|
||||
|
||||
sealed class MyLogger : IMqttNetLogger
|
||||
{
|
||||
public bool IsEnabled { get; set; } = true;
|
||||
|
||||
public void Publish(MqttNetLogLevel logLevel, string source, string message, object[] parameters, Exception exception)
|
||||
{
|
||||
// Forward the log message to other loggers.
|
||||
// 1. Convert log level to matching log level in target logger.
|
||||
// 2. Call target logger and pass data accordingly.
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
// ReSharper disable UnusedType.Global
|
||||
// ReSharper disable UnusedMember.Global
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
using MQTTnet.Diagnostics.PacketInspection;
|
||||
|
||||
namespace MQTTnet.Samples.Diagnostics;
|
||||
|
||||
public static class PackageInspection_Samples
|
||||
{
|
||||
public static async Task Inspect_Outgoing_Package()
|
||||
{
|
||||
/*
|
||||
* This sample covers the inspection of outgoing packages from the client.
|
||||
*/
|
||||
|
||||
var mqttFactory = new MqttClientFactory();
|
||||
|
||||
using (var mqttClient = mqttFactory.CreateMqttClient())
|
||||
{
|
||||
var mqttClientOptions = mqttFactory.CreateClientOptionsBuilder()
|
||||
.WithTcpServer("broker.hivemq.com")
|
||||
.Build();
|
||||
|
||||
mqttClient.InspectPacketAsync += OnInspectPacket;
|
||||
|
||||
await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
|
||||
|
||||
Console.WriteLine("MQTT client is connected.");
|
||||
|
||||
var mqttClientDisconnectOptions = mqttFactory.CreateClientDisconnectOptionsBuilder()
|
||||
.Build();
|
||||
|
||||
await mqttClient.DisconnectAsync(mqttClientDisconnectOptions, CancellationToken.None);
|
||||
}
|
||||
}
|
||||
|
||||
static Task OnInspectPacket(InspectMqttPacketEventArgs eventArgs)
|
||||
{
|
||||
if (eventArgs.Direction == MqttPacketFlowDirection.Inbound)
|
||||
{
|
||||
Console.WriteLine($"IN: {Convert.ToBase64String(eventArgs.Buffer)}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"OUT: {Convert.ToBase64String(eventArgs.Buffer)}");
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Text.Json;
|
||||
|
||||
namespace MQTTnet.Samples.Helpers;
|
||||
|
||||
internal static class ObjectExtensions
|
||||
{
|
||||
public static TObject DumpToConsole<TObject>(this TObject @object)
|
||||
{
|
||||
var output = "NULL";
|
||||
if (@object != null)
|
||||
{
|
||||
output = JsonSerializer.Serialize(@object, new JsonSerializerOptions
|
||||
{
|
||||
WriteIndented = true
|
||||
});
|
||||
}
|
||||
|
||||
Console.WriteLine($"[{@object?.GetType().Name}]:\r\n{output}");
|
||||
return @object;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<IsPackable>false</IsPackable>
|
||||
<DisableImplicitAspNetCoreAnalyzers>true</DisableImplicitAspNetCoreAnalyzers>
|
||||
<EnableNETAnalyzers>false</EnableNETAnalyzers>
|
||||
<EnforceCodeStyleInBuild>false</EnforceCodeStyleInBuild>
|
||||
<NoWarn>1591;NETSDK1138;NU1803;NU1901;NU1902</NoWarn>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<NuGetAuditMode>all</NuGetAuditMode>
|
||||
<NuGetAudit>true</NuGetAudit>
|
||||
<NuGetAuditLevel>low</NuGetAuditLevel>
|
||||
<AnalysisLevel>latest-Recommended</AnalysisLevel>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
<ProjectReference Include="..\Source\MQTTnet.AspnetCore\MQTTnet.AspNetCore.csproj"/>
|
||||
<ProjectReference Include="..\Source\MQTTnet.Extensions.Rpc\MQTTnet.Extensions.Rpc.csproj"/>
|
||||
<ProjectReference Include="..\Source\MQTTnet.Extensions.TopicTemplate\MQTTnet.Extensions.TopicTemplate.csproj" />
|
||||
<ProjectReference Include="..\Source\MQTTnet.Server\MQTTnet.Server.csproj"/>
|
||||
<ProjectReference Include="..\Source\MQTTnet\MQTTnet.csproj"/>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,71 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Reflection;
|
||||
|
||||
Console.BackgroundColor = ConsoleColor.White;
|
||||
Console.BackgroundColor = ConsoleColor.Red;
|
||||
Console.Title = "Samples - MQTTnet";
|
||||
Console.WriteLine("----------------------------------------------");
|
||||
Console.WriteLine("- Welcome to MQTTnet samples! -");
|
||||
Console.WriteLine("----------------------------------------------");
|
||||
Console.ResetColor();
|
||||
Console.WriteLine();
|
||||
|
||||
var sampleClasses = Assembly.GetExecutingAssembly().GetExportedTypes().OrderBy(c => c.Name).ToList();
|
||||
|
||||
var index = 0;
|
||||
foreach (var sampleClass in sampleClasses)
|
||||
{
|
||||
Console.WriteLine($"{index} = {sampleClass.Name}");
|
||||
index++;
|
||||
}
|
||||
|
||||
Console.WriteLine();
|
||||
Console.ForegroundColor = ConsoleColor.Green;
|
||||
Console.Write("Please choose sample class (press Enter to continue): ");
|
||||
Console.ResetColor();
|
||||
|
||||
var input = Console.ReadLine();
|
||||
var selectedIndex = int.Parse(input ?? "0");
|
||||
var selectedSampleClass = sampleClasses[selectedIndex];
|
||||
var sampleMethods = selectedSampleClass.GetMethods(BindingFlags.Static | BindingFlags.Public).OrderBy(m => m.Name).ToList();
|
||||
|
||||
index = 0;
|
||||
foreach (var sampleMethod in sampleMethods)
|
||||
{
|
||||
Console.WriteLine($"{index} = {sampleMethod.Name}");
|
||||
index++;
|
||||
}
|
||||
|
||||
Console.WriteLine();
|
||||
Console.ForegroundColor = ConsoleColor.Green;
|
||||
Console.Write("Please choose sample (press Enter to continue): ");
|
||||
Console.ResetColor();
|
||||
|
||||
input = Console.ReadLine();
|
||||
selectedIndex = int.Parse(input ?? "0");
|
||||
var selectedSampleMethod = sampleMethods[selectedIndex];
|
||||
|
||||
Console.WriteLine();
|
||||
Console.ForegroundColor = ConsoleColor.White;
|
||||
Console.WriteLine("Executing sample...");
|
||||
Console.ResetColor();
|
||||
Console.WriteLine();
|
||||
|
||||
try
|
||||
{
|
||||
if (selectedSampleMethod.Invoke(null, null) is Task task)
|
||||
{
|
||||
await task;
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Console.WriteLine(exception.ToString());
|
||||
}
|
||||
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Press Enter to exit.");
|
||||
Console.ReadLine();
|
||||
@@ -0,0 +1,87 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
// ReSharper disable UnusedType.Global
|
||||
// ReSharper disable UnusedMember.Global
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
using MQTTnet.Extensions.Rpc;
|
||||
using MQTTnet.Protocol;
|
||||
|
||||
namespace MQTTnet.Samples.RpcClient;
|
||||
|
||||
public static class RpcClient_Samples
|
||||
{
|
||||
/*
|
||||
* The extension MQTTnet.Extensions.Rpc (available as nuget) allows sending a request and waiting for the matching reply.
|
||||
* This is done via defining a pattern which uses the topic to correlate the request and the response.
|
||||
* From client usage it is possible to define a timeout.
|
||||
*/
|
||||
|
||||
public static async Task Send_Request()
|
||||
{
|
||||
var mqttFactory = new MqttClientFactory();
|
||||
|
||||
// The RPC client is an addon for the existing client. So we need a regular client
|
||||
// which is wrapped later.
|
||||
|
||||
using (var mqttClient = mqttFactory.CreateMqttClient())
|
||||
{
|
||||
var mqttClientOptions = new MqttClientOptionsBuilder()
|
||||
.WithTcpServer("broker.hivemq.com")
|
||||
.Build();
|
||||
|
||||
await mqttClient.ConnectAsync(mqttClientOptions);
|
||||
|
||||
using (var mqttRpcClient = mqttFactory.CreateMqttRpcClient(mqttClient))
|
||||
{
|
||||
// Access to a fully featured application message is not supported for RPC calls!
|
||||
// The method will throw an exception when the response was not received in time.
|
||||
await mqttRpcClient.ExecuteAsync(TimeSpan.FromSeconds(2), "ping", "", MqttQualityOfServiceLevel.AtMostOnce);
|
||||
}
|
||||
|
||||
Console.WriteLine("The RPC call was successful.");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The device must respond to the request using the correct topic. The following C code shows how a
|
||||
* smart device like an ESP8266 must respond to the above sample.
|
||||
*
|
||||
// If using the MQTT client PubSubClient it must be ensured
|
||||
// that the request topic for each method is subscribed like the following.
|
||||
mqttClient.subscribe("MQTTnet.RPC/+/ping");
|
||||
mqttClient.subscribe("MQTTnet.RPC/+/do_something");
|
||||
|
||||
// It is not allowed to change the structure of the topic.
|
||||
// Otherwise RPC will not work.
|
||||
// So method names can be separated using an _ or . but no +, # or /.
|
||||
// If it is required to distinguish between devices
|
||||
// own rules can be defined like the following:
|
||||
mqttClient.subscribe("MQTTnet.RPC/+/deviceA.ping");
|
||||
mqttClient.subscribe("MQTTnet.RPC/+/deviceB.ping");
|
||||
mqttClient.subscribe("MQTTnet.RPC/+/deviceC.getTemperature");
|
||||
|
||||
// Within the callback of the MQTT client the topic must be checked
|
||||
// if it belongs to MQTTnet RPC. The following code shows one
|
||||
// possible way of doing this.
|
||||
void mqtt_Callback(char *topic, byte *payload, unsigned int payloadLength)
|
||||
{
|
||||
String topicString = String(topic);
|
||||
|
||||
if (topicString.startsWith("MQTTnet.RPC/")) {
|
||||
String responseTopic = topicString + String("/response");
|
||||
|
||||
if (topicString.endsWith("/deviceA.ping")) {
|
||||
mqtt_publish(responseTopic, "pong", false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Important notes:
|
||||
// ! Do not send response message with the _retain_ flag set to true.
|
||||
// ! All required data for a RPC call and the result must be placed into the payload.
|
||||
*/
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
// ReSharper disable UnusedType.Global
|
||||
// ReSharper disable UnusedMember.Global
|
||||
// ReSharper disable InconsistentNaming
|
||||
// ReSharper disable EmptyConstructor
|
||||
// ReSharper disable MemberCanBeMadeStatic.Local
|
||||
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using MQTTnet.AspNetCore;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.Samples.Server;
|
||||
|
||||
public static class Server_ASP_NET_Samples
|
||||
{
|
||||
public static Task Start_Server_With_WebSockets_Support()
|
||||
{
|
||||
/*
|
||||
* This sample starts a minimal ASP.NET Webserver including a hosted MQTT server.
|
||||
*/
|
||||
var host = Host.CreateDefaultBuilder(Array.Empty<string>())
|
||||
.ConfigureWebHostDefaults(
|
||||
webBuilder =>
|
||||
{
|
||||
webBuilder.UseKestrel(
|
||||
o =>
|
||||
{
|
||||
// This will allow MQTT connections based on TCP port 1883.
|
||||
o.ListenAnyIP(1883, l => l.UseMqtt());
|
||||
|
||||
// This will allow MQTT connections based on HTTP WebSockets with URI "localhost:5000/mqtt"
|
||||
// See code below for URI configuration.
|
||||
o.ListenAnyIP(5000); // Default HTTP pipeline
|
||||
});
|
||||
|
||||
webBuilder.UseStartup<Startup>();
|
||||
});
|
||||
|
||||
return host.RunConsoleAsync();
|
||||
}
|
||||
|
||||
sealed class MqttController
|
||||
{
|
||||
public MqttController()
|
||||
{
|
||||
// Inject other services via constructor.
|
||||
}
|
||||
|
||||
public Task OnClientConnected(ClientConnectedEventArgs eventArgs)
|
||||
{
|
||||
Console.WriteLine($"Client '{eventArgs.ClientId}' connected.");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
|
||||
public Task ValidateConnection(ValidatingConnectionEventArgs eventArgs)
|
||||
{
|
||||
Console.WriteLine($"Client '{eventArgs.ClientId}' wants to connect. Accepting!");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
sealed class Startup
|
||||
{
|
||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment environment, MqttController mqttController)
|
||||
{
|
||||
app.UseRouting();
|
||||
|
||||
app.UseEndpoints(
|
||||
endpoints =>
|
||||
{
|
||||
endpoints.MapConnectionHandler<MqttConnectionHandler>(
|
||||
"/mqtt",
|
||||
httpConnectionDispatcherOptions => httpConnectionDispatcherOptions.WebSockets.SubProtocolSelector =
|
||||
protocolList => protocolList.FirstOrDefault() ?? string.Empty);
|
||||
});
|
||||
|
||||
app.UseMqttServer(
|
||||
server =>
|
||||
{
|
||||
/*
|
||||
* Attach event handlers etc. if required.
|
||||
*/
|
||||
|
||||
server.ValidatingConnectionAsync += mqttController.ValidateConnection;
|
||||
server.ClientConnectedAsync += mqttController.OnClientConnected;
|
||||
});
|
||||
}
|
||||
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.AddHostedMqttServer(
|
||||
optionsBuilder =>
|
||||
{
|
||||
optionsBuilder.WithDefaultEndpoint();
|
||||
});
|
||||
|
||||
services.AddMqttConnectionHandler();
|
||||
services.AddConnections();
|
||||
|
||||
services.AddSingleton<MqttController>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.Samples.Server;
|
||||
|
||||
// ReSharper disable UnusedType.Global
|
||||
// ReSharper disable UnusedMember.Global
|
||||
// ReSharper disable InconsistentNaming
|
||||
// ReSharper disable EmptyConstructor
|
||||
// ReSharper disable MemberCanBeMadeStatic.Local
|
||||
public static class Server_Diagnostics_Samples
|
||||
{
|
||||
public static async Task Get_Notified_When_Client_Received_Message()
|
||||
{
|
||||
/*
|
||||
* This sample starts a MQTT server and attaches an event which gets fired whenever
|
||||
* a client received and acknowledged a PUBLISH packet.
|
||||
*/
|
||||
|
||||
var mqttServerFactory = new MqttServerFactory();
|
||||
var mqttServerOptions = new MqttServerOptionsBuilder().WithDefaultEndpoint().Build();
|
||||
|
||||
using (var mqttServer = mqttServerFactory.CreateMqttServer(mqttServerOptions))
|
||||
{
|
||||
// Attach the event handler.
|
||||
mqttServer.ClientAcknowledgedPublishPacketAsync += e =>
|
||||
{
|
||||
Console.WriteLine($"Client '{e.ClientId}' acknowledged packet {e.PublishPacket.PacketIdentifier} with topic '{e.PublishPacket.Topic}'");
|
||||
|
||||
// It is also possible to read additional data from the client response. This requires casting the response packet.
|
||||
var qos1AcknowledgePacket = e.AcknowledgePacket as MqttPubAckPacket;
|
||||
Console.WriteLine($"QoS 1 reason code: {qos1AcknowledgePacket?.ReasonCode}");
|
||||
|
||||
var qos2AcknowledgePacket = e.AcknowledgePacket as MqttPubCompPacket;
|
||||
Console.WriteLine($"QoS 2 reason code: {qos1AcknowledgePacket?.ReasonCode}");
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
await mqttServer.StartAsync();
|
||||
|
||||
Console.WriteLine("Press Enter to exit.");
|
||||
Console.ReadLine();
|
||||
|
||||
await mqttServer.StopAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
// ReSharper disable UnusedType.Global
|
||||
// ReSharper disable UnusedMember.Global
|
||||
// ReSharper disable InconsistentNaming
|
||||
// ReSharper disable EmptyConstructor
|
||||
// ReSharper disable MemberCanBeMadeStatic.Local
|
||||
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.Samples.Server;
|
||||
|
||||
public static class Server_Intercepting_Samples
|
||||
{
|
||||
public static async Task Intercept_Application_Messages()
|
||||
{
|
||||
/*
|
||||
* This sample starts a simple MQTT server which manipulate all processed application messages.
|
||||
* Please see _Server_Simple_Samples_ for more details on how to start a server.
|
||||
*/
|
||||
|
||||
var mqttServerFactory = new MqttServerFactory();
|
||||
var mqttServerOptions = new MqttServerOptionsBuilder().WithDefaultEndpoint().Build();
|
||||
|
||||
using (var mqttServer = mqttServerFactory.CreateMqttServer(mqttServerOptions))
|
||||
{
|
||||
mqttServer.InterceptingPublishAsync += args =>
|
||||
{
|
||||
// Here we only change the topic of the received application message.
|
||||
// but also changing the payload etc. is required. Changing the QoS after
|
||||
// transmitting is not supported and makes no sense at all.
|
||||
args.ApplicationMessage.Topic += "/manipulated";
|
||||
|
||||
return CompletedTask.Instance;
|
||||
};
|
||||
|
||||
await mqttServer.StartAsync();
|
||||
|
||||
Console.WriteLine("Press Enter to exit.");
|
||||
Console.ReadLine();
|
||||
await mqttServer.StopAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
// ReSharper disable UnusedType.Global
|
||||
// ReSharper disable UnusedMember.Global
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
using System.Buffers;
|
||||
using System.Text.Json;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.Samples.Server;
|
||||
|
||||
public static class Server_Retained_Messages_Samples
|
||||
{
|
||||
public static async Task Persist_Retained_Messages()
|
||||
{
|
||||
/*
|
||||
* This sample starts a simple MQTT server which will store all retained messages in a file.
|
||||
*/
|
||||
|
||||
var storePath = Path.Combine(Path.GetTempPath(), "RetainedMessages.json");
|
||||
|
||||
var mqttServerFactory = new MqttServerFactory();
|
||||
|
||||
// Due to security reasons the "default" endpoint (which is unencrypted) is not enabled by default!
|
||||
var mqttServerOptions = mqttServerFactory.CreateServerOptionsBuilder().WithDefaultEndpoint().Build();
|
||||
|
||||
using (var server = mqttServerFactory.CreateMqttServer(mqttServerOptions))
|
||||
{
|
||||
// Make sure that the server will load the retained messages.
|
||||
server.LoadingRetainedMessageAsync += async eventArgs =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var models = await JsonSerializer.DeserializeAsync<List<MqttRetainedMessageModel>>(File.OpenRead(storePath)) ?? new List<MqttRetainedMessageModel>();
|
||||
var retainedMessages = models.Select(m => m.ToApplicationMessage()).ToList();
|
||||
|
||||
eventArgs.LoadedRetainedMessages = retainedMessages;
|
||||
Console.WriteLine("Retained messages loaded.");
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
// Ignore because nothing is stored yet.
|
||||
Console.WriteLine("No retained messages stored yet.");
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Console.WriteLine(exception);
|
||||
}
|
||||
};
|
||||
|
||||
// Make sure to persist the changed retained messages.
|
||||
server.RetainedMessageChangedAsync += async eventArgs =>
|
||||
{
|
||||
try
|
||||
{
|
||||
// This sample uses the property _StoredRetainedMessages_ which will contain all(!) retained messages.
|
||||
// The event args also contain the affected retained message (property ChangedRetainedMessage). This can be
|
||||
// used to write all retained messages to dedicated files etc. Then all files must be loaded and a full list
|
||||
// of retained messages must be provided in the loaded event.
|
||||
|
||||
var models = eventArgs.StoredRetainedMessages.Select(MqttRetainedMessageModel.Create);
|
||||
|
||||
var buffer = JsonSerializer.SerializeToUtf8Bytes(models);
|
||||
await File.WriteAllBytesAsync(storePath, buffer);
|
||||
Console.WriteLine("Retained messages saved.");
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Console.WriteLine(exception);
|
||||
}
|
||||
};
|
||||
|
||||
// Make sure to clear the retained messages when they are all deleted via API.
|
||||
server.RetainedMessagesClearedAsync += _ =>
|
||||
{
|
||||
File.Delete(storePath);
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
await server.StartAsync();
|
||||
|
||||
Console.WriteLine("Press Enter to exit.");
|
||||
Console.ReadLine();
|
||||
}
|
||||
}
|
||||
|
||||
sealed class MqttRetainedMessageModel
|
||||
{
|
||||
public string? ContentType { get; set; }
|
||||
public byte[]? CorrelationData { get; set; }
|
||||
public byte[]? Payload { get; set; }
|
||||
public MqttPayloadFormatIndicator PayloadFormatIndicator { get; set; }
|
||||
public MqttQualityOfServiceLevel QualityOfServiceLevel { get; set; }
|
||||
public string? ResponseTopic { get; set; }
|
||||
public string? Topic { get; set; }
|
||||
public List<MqttUserProperty>? UserProperties { get; set; }
|
||||
|
||||
public static MqttRetainedMessageModel Create(MqttApplicationMessage message)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(message);
|
||||
|
||||
return new MqttRetainedMessageModel
|
||||
{
|
||||
Topic = message.Topic,
|
||||
|
||||
// Create a copy of the buffer from the payload segment because
|
||||
// it cannot be serialized and deserialized with the JSON serializer.
|
||||
Payload = message.Payload.ToArray(),
|
||||
UserProperties = message.UserProperties,
|
||||
ResponseTopic = message.ResponseTopic,
|
||||
CorrelationData = message.CorrelationData,
|
||||
ContentType = message.ContentType,
|
||||
PayloadFormatIndicator = message.PayloadFormatIndicator,
|
||||
QualityOfServiceLevel = message.QualityOfServiceLevel
|
||||
|
||||
// Other properties like "Retain" are not if interest in the storage.
|
||||
// That's why a custom model makes sense.
|
||||
};
|
||||
}
|
||||
|
||||
public MqttApplicationMessage ToApplicationMessage()
|
||||
{
|
||||
return new MqttApplicationMessage
|
||||
{
|
||||
Topic = Topic,
|
||||
PayloadSegment = new ArraySegment<byte>(Payload ?? Array.Empty<byte>()),
|
||||
PayloadFormatIndicator = PayloadFormatIndicator,
|
||||
ResponseTopic = ResponseTopic,
|
||||
CorrelationData = CorrelationData,
|
||||
ContentType = ContentType,
|
||||
UserProperties = UserProperties,
|
||||
QualityOfServiceLevel = QualityOfServiceLevel,
|
||||
Dup = false,
|
||||
Retain = true
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
// ReSharper disable UnusedType.Global
|
||||
// ReSharper disable UnusedMember.Global
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
using MQTTnet.Diagnostics.Logger;
|
||||
using MQTTnet.Protocol;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.Samples.Server;
|
||||
|
||||
public static class Server_Simple_Samples
|
||||
{
|
||||
public static async Task Force_Disconnecting_Client()
|
||||
{
|
||||
/*
|
||||
* This sample will disconnect a client.
|
||||
*
|
||||
* See _Run_Minimal_Server_ for more information.
|
||||
*/
|
||||
|
||||
using (var mqttServer = await StartMqttServer())
|
||||
{
|
||||
// Let the client connect.
|
||||
await Task.Delay(TimeSpan.FromSeconds(5));
|
||||
|
||||
// Now disconnect the client (if connected).
|
||||
var affectedClient = (await mqttServer.GetClientsAsync()).FirstOrDefault(c => c.Id == "MyClient");
|
||||
if (affectedClient != null)
|
||||
{
|
||||
await affectedClient.DisconnectAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task Publish_Message_From_Broker()
|
||||
{
|
||||
/*
|
||||
* This sample will publish a message directly at the broker.
|
||||
*
|
||||
* See _Run_Minimal_Server_ for more information.
|
||||
*/
|
||||
|
||||
using (var mqttServer = await StartMqttServer())
|
||||
{
|
||||
// Create a new message using the builder as usual.
|
||||
var message = new MqttApplicationMessageBuilder().WithTopic("HelloWorld").WithPayload("Test").Build();
|
||||
|
||||
// Now inject the new message at the broker.
|
||||
await mqttServer.InjectApplicationMessage(
|
||||
new InjectedMqttApplicationMessage(message)
|
||||
{
|
||||
SenderClientId = "SenderClientId"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task Run_Minimal_Server()
|
||||
{
|
||||
/*
|
||||
* This sample starts a simple MQTT server which will accept any TCP connection.
|
||||
*/
|
||||
|
||||
var mqttServerFactory = new MqttServerFactory();
|
||||
|
||||
// The port for the default endpoint is 1883.
|
||||
// The default endpoint is NOT encrypted!
|
||||
// Use the builder classes where possible.
|
||||
var mqttServerOptions = new MqttServerOptionsBuilder().WithDefaultEndpoint().Build();
|
||||
|
||||
// The port can be changed using the following API (not used in this example).
|
||||
// new MqttServerOptionsBuilder()
|
||||
// .WithDefaultEndpoint()
|
||||
// .WithDefaultEndpointPort(1234)
|
||||
// .Build();
|
||||
|
||||
using (var mqttServer = mqttServerFactory.CreateMqttServer(mqttServerOptions))
|
||||
{
|
||||
await mqttServer.StartAsync();
|
||||
|
||||
Console.WriteLine("Press Enter to exit.");
|
||||
Console.ReadLine();
|
||||
|
||||
// Stop and dispose the MQTT server if it is no longer needed!
|
||||
await mqttServer.StopAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task Run_Server_With_Logging()
|
||||
{
|
||||
/*
|
||||
* This sample starts a simple MQTT server and prints the logs to the output.
|
||||
*
|
||||
* IMPORTANT! Do not enable logging in live environment. It will decrease performance.
|
||||
*
|
||||
* See sample "Run_Minimal_Server" for more details.
|
||||
*/
|
||||
|
||||
var mqttServerFactory = new MqttServerFactory(new ConsoleLogger());
|
||||
|
||||
var mqttServerOptions = new MqttServerOptionsBuilder().WithDefaultEndpoint().Build();
|
||||
|
||||
using (var mqttServer = mqttServerFactory.CreateMqttServer(mqttServerOptions))
|
||||
{
|
||||
await mqttServer.StartAsync();
|
||||
|
||||
Console.WriteLine("Press Enter to exit.");
|
||||
Console.ReadLine();
|
||||
|
||||
// Stop and dispose the MQTT server if it is no longer needed!
|
||||
await mqttServer.StopAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task Validating_Connections()
|
||||
{
|
||||
/*
|
||||
* This sample starts a simple MQTT server which will check for valid credentials and client ID.
|
||||
*
|
||||
* See _Run_Minimal_Server_ for more information.
|
||||
*/
|
||||
|
||||
var mqttServerFactory = new MqttServerFactory();
|
||||
|
||||
var mqttServerOptions = new MqttServerOptionsBuilder().WithDefaultEndpoint().Build();
|
||||
|
||||
using (var mqttServer = mqttServerFactory.CreateMqttServer(mqttServerOptions))
|
||||
{
|
||||
// Setup connection validation before starting the server so that there is
|
||||
// no change to connect without valid credentials.
|
||||
mqttServer.ValidatingConnectionAsync += e =>
|
||||
{
|
||||
if (e.ClientId != "ValidClientId")
|
||||
{
|
||||
e.ReasonCode = MqttConnectReasonCode.ClientIdentifierNotValid;
|
||||
}
|
||||
|
||||
if (e.UserName != "ValidUser")
|
||||
{
|
||||
e.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword;
|
||||
}
|
||||
|
||||
if (e.Password != "SecretPassword")
|
||||
{
|
||||
e.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword;
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
await mqttServer.StartAsync();
|
||||
|
||||
Console.WriteLine("Press Enter to exit.");
|
||||
Console.ReadLine();
|
||||
|
||||
await mqttServer.StopAsync();
|
||||
}
|
||||
}
|
||||
|
||||
static async Task<MqttServer> StartMqttServer()
|
||||
{
|
||||
var mqttServerFactory = new MqttServerFactory();
|
||||
|
||||
// Due to security reasons the "default" endpoint (which is unencrypted) is not enabled by default!
|
||||
var mqttServerOptions = mqttServerFactory.CreateServerOptionsBuilder().WithDefaultEndpoint().Build();
|
||||
var server = mqttServerFactory.CreateMqttServer(mqttServerOptions);
|
||||
await server.StartAsync();
|
||||
return server;
|
||||
}
|
||||
|
||||
class ConsoleLogger : IMqttNetLogger
|
||||
{
|
||||
readonly object _consoleSyncRoot = new();
|
||||
|
||||
public bool IsEnabled => true;
|
||||
|
||||
public void Publish(MqttNetLogLevel logLevel, string source, string message, object[]? parameters, Exception? exception)
|
||||
{
|
||||
var foregroundColor = ConsoleColor.White;
|
||||
switch (logLevel)
|
||||
{
|
||||
case MqttNetLogLevel.Verbose:
|
||||
foregroundColor = ConsoleColor.White;
|
||||
break;
|
||||
|
||||
case MqttNetLogLevel.Info:
|
||||
foregroundColor = ConsoleColor.Green;
|
||||
break;
|
||||
|
||||
case MqttNetLogLevel.Warning:
|
||||
foregroundColor = ConsoleColor.DarkYellow;
|
||||
break;
|
||||
|
||||
case MqttNetLogLevel.Error:
|
||||
foregroundColor = ConsoleColor.Red;
|
||||
break;
|
||||
}
|
||||
|
||||
if (parameters?.Length > 0)
|
||||
{
|
||||
message = string.Format(message, parameters);
|
||||
}
|
||||
|
||||
lock (_consoleSyncRoot)
|
||||
{
|
||||
Console.ForegroundColor = foregroundColor;
|
||||
Console.WriteLine(message);
|
||||
|
||||
if (exception != null)
|
||||
{
|
||||
Console.WriteLine(exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
// ReSharper disable UnusedType.Global
|
||||
// ReSharper disable UnusedMember.Global
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
using System.Net;
|
||||
using System.Security.Cryptography;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.Samples.Server;
|
||||
|
||||
public static class Server_TLS_Samples
|
||||
{
|
||||
public static async Task Run_Server_With_Self_Signed_Certificate()
|
||||
{
|
||||
/*
|
||||
* This sample starts a simple MQTT server which will accept any TCP connection.
|
||||
* It also has an encrypted connection using a self signed TLS certificate.
|
||||
*
|
||||
* See sample "Run_Minimal_Server" for more details.
|
||||
*/
|
||||
|
||||
var mqttServerFactory = new MqttServerFactory();
|
||||
|
||||
// This certificate is self signed so that
|
||||
var certificate = CreateSelfSignedCertificate("1.3.6.1.5.5.7.3.1");
|
||||
|
||||
var mqttServerOptions = new MqttServerOptionsBuilder().WithEncryptionCertificate(certificate).WithEncryptedEndpoint().Build();
|
||||
|
||||
using (var mqttServer = mqttServerFactory.CreateMqttServer(mqttServerOptions))
|
||||
{
|
||||
await mqttServer.StartAsync();
|
||||
|
||||
Console.WriteLine("Press Enter to exit.");
|
||||
Console.ReadLine();
|
||||
|
||||
// Stop and dispose the MQTT server if it is no longer needed!
|
||||
await mqttServer.StopAsync();
|
||||
}
|
||||
}
|
||||
|
||||
static X509Certificate2 CreateSelfSignedCertificate(string oid)
|
||||
{
|
||||
var sanBuilder = new SubjectAlternativeNameBuilder();
|
||||
sanBuilder.AddIpAddress(IPAddress.Loopback);
|
||||
sanBuilder.AddIpAddress(IPAddress.IPv6Loopback);
|
||||
sanBuilder.AddDnsName("localhost");
|
||||
|
||||
using (var rsa = RSA.Create())
|
||||
{
|
||||
var certRequest = new CertificateRequest("CN=localhost", rsa, HashAlgorithmName.SHA512, RSASignaturePadding.Pkcs1);
|
||||
|
||||
certRequest.CertificateExtensions.Add(
|
||||
new X509KeyUsageExtension(X509KeyUsageFlags.DataEncipherment | X509KeyUsageFlags.KeyEncipherment | X509KeyUsageFlags.DigitalSignature, false));
|
||||
|
||||
certRequest.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(new OidCollection { new(oid) }, false));
|
||||
|
||||
certRequest.CertificateExtensions.Add(sanBuilder.Build());
|
||||
|
||||
using (var certificate = certRequest.CreateSelfSigned(DateTimeOffset.Now.AddMinutes(-10), DateTimeOffset.Now.AddMinutes(10)))
|
||||
{
|
||||
var pfxCertificate = new X509Certificate2(
|
||||
certificate.Export(X509ContentType.Pfx),
|
||||
(string)null!,
|
||||
X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable);
|
||||
|
||||
return pfxCertificate;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<IsPackable>false</IsPackable>
|
||||
<DisableImplicitAspNetCoreAnalyzers>true</DisableImplicitAspNetCoreAnalyzers>
|
||||
<EnableNETAnalyzers>false</EnableNETAnalyzers>
|
||||
<EnforceCodeStyleInBuild>false</EnforceCodeStyleInBuild>
|
||||
<NoWarn>1591;NETSDK1138;NU1803;NU1901;NU1902</NoWarn>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<NuGetAuditMode>all</NuGetAuditMode>
|
||||
<NuGetAudit>true</NuGetAudit>
|
||||
<NuGetAuditLevel>low</NuGetAuditLevel>
|
||||
<AnalysisLevel>latest-Recommended</AnalysisLevel>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Source\MQTTnet.AspnetCore\MQTTnet.AspNetCore.csproj"/>
|
||||
<ProjectReference Include="..\..\Source\MQTTnet.Server\MQTTnet.Server.csproj"/>
|
||||
<ProjectReference Include="..\MQTTnet\MQTTnet.csproj"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="wwwroot\"/>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,34 @@
|
||||
@page
|
||||
@model IndexModel
|
||||
@{
|
||||
ViewData["Title"] = "Home page";
|
||||
}
|
||||
|
||||
<div>
|
||||
<h1>MQTTnet ASP.NET Core Test App</h1>
|
||||
</div>
|
||||
|
||||
<script src="https://unpkg.com/mqtt/dist/mqtt.min.js"></script>
|
||||
|
||||
<script>
|
||||
const options = {
|
||||
clean: true,
|
||||
connectTimeout: 4000,
|
||||
clientId: 'mqttnet_web_client'
|
||||
}
|
||||
|
||||
const client = mqtt.connect('ws://localhost:5000/mqtt')
|
||||
|
||||
client.on('connect', function () {
|
||||
console.log('Connected')
|
||||
client.subscribe('#', function (err) {
|
||||
if (!err) {
|
||||
client.publish('test', 'Hello mqtt')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
client.on('message', function (topic, message) {
|
||||
console.log(message.toString())
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,23 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace MQTTnet.AspTestApp.Pages
|
||||
{
|
||||
public class IndexModel : PageModel
|
||||
{
|
||||
private readonly ILogger<IndexModel> _logger;
|
||||
|
||||
public IndexModel(ILogger<IndexModel> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public void OnGet()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>@ViewData["Title"] - MQTTnet</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@RenderBody()
|
||||
|
||||
@await RenderSectionAsync("Scripts", required: false)
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,2 @@
|
||||
@namespace MQTTnet.AspTestApp.Pages
|
||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
@@ -0,0 +1,3 @@
|
||||
@{
|
||||
Layout = "_Layout";
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using MQTTnet;
|
||||
using MQTTnet.AspNetCore;
|
||||
using MQTTnet.Server;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
builder.Services.AddRazorPages();
|
||||
|
||||
// Setup MQTT stuff.
|
||||
builder.Services.AddMqttServer();
|
||||
builder.Services.AddConnections();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
if (!app.Environment.IsDevelopment())
|
||||
{
|
||||
app.UseExceptionHandler("/Error");
|
||||
}
|
||||
|
||||
app.UseStaticFiles();
|
||||
|
||||
app.UseRouting();
|
||||
|
||||
app.UseAuthorization();
|
||||
|
||||
app.MapRazorPages();
|
||||
|
||||
// Setup MQTT stuff.
|
||||
app.MapMqtt("/mqtt");
|
||||
|
||||
app.UseMqttServer(server =>
|
||||
{
|
||||
server.StartedAsync += args =>
|
||||
{
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
var mqttApplicationMessage = new MqttApplicationMessageBuilder()
|
||||
.WithPayload("Test application message from MQTTnet server.")
|
||||
.WithTopic("message")
|
||||
.Build();
|
||||
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
await server.InjectApplicationMessage(new InjectedMqttApplicationMessage(mqttApplicationMessage)
|
||||
{
|
||||
SenderClientId = "server"
|
||||
});
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine(e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromSeconds(5));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
});
|
||||
|
||||
app.Run();
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"DetailedErrors": true,
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"version": "1.0",
|
||||
"defaultProvider": "cdnjs",
|
||||
"libraries": []
|
||||
}
|
||||
@@ -1,18 +1,23 @@
|
||||
using System;
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System.Linq;
|
||||
using MQTTnet.Server;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MQTTnet.AspNetCore
|
||||
namespace MQTTnet.AspNetCore;
|
||||
|
||||
public static class ApplicationBuilderExtensions
|
||||
{
|
||||
public static class ApplicationBuilderExtensions
|
||||
[Obsolete(
|
||||
"This class is obsolete and will be removed in a future version. The recommended alternative is to use MapMqtt inside Microsoft.AspNetCore.Builder.UseEndpoints(...).")]
|
||||
public static IApplicationBuilder UseMqttEndpoint(this IApplicationBuilder app, string path = "/mqtt")
|
||||
{
|
||||
public static IApplicationBuilder UseMqttEndpoint(this IApplicationBuilder app, string path = "/mqtt")
|
||||
{
|
||||
app.UseWebSockets();
|
||||
app.Use(async (context, next) =>
|
||||
app.UseWebSockets();
|
||||
app.Use(
|
||||
async (context, next) =>
|
||||
{
|
||||
if (!context.WebSockets.IsWebSocketRequest || context.Request.Path != path)
|
||||
{
|
||||
@@ -24,7 +29,7 @@ namespace MQTTnet.AspNetCore
|
||||
|
||||
if (context.Request.Headers.TryGetValue("Sec-WebSocket-Protocol", out var requestedSubProtocolValues))
|
||||
{
|
||||
subProtocol = SelectSubProtocol(requestedSubProtocolValues);
|
||||
subProtocol = MqttSubProtocolSelector.SelectSubProtocol(requestedSubProtocolValues);
|
||||
}
|
||||
|
||||
var adapter = app.ApplicationServices.GetRequiredService<MqttWebSocketServerAdapter>();
|
||||
@@ -34,24 +39,15 @@ namespace MQTTnet.AspNetCore
|
||||
}
|
||||
});
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
public static string SelectSubProtocol(IList<string> requestedSubProtocolValues)
|
||||
{
|
||||
// Order the protocols to also match "mqtt", "mqttv-3.1", "mqttv-3.11" etc.
|
||||
return requestedSubProtocolValues
|
||||
.OrderByDescending(p => p.Length)
|
||||
.FirstOrDefault(p => p.ToLower().StartsWith("mqtt"));
|
||||
}
|
||||
|
||||
public static IApplicationBuilder UseMqttServer(this IApplicationBuilder app, Action<IMqttServer> configure)
|
||||
{
|
||||
var server = app.ApplicationServices.GetRequiredService<IMqttServer>();
|
||||
|
||||
configure(server);
|
||||
|
||||
return app;
|
||||
}
|
||||
return app;
|
||||
}
|
||||
}
|
||||
|
||||
public static IApplicationBuilder UseMqttServer(this IApplicationBuilder app, Action<MqttServer> configure)
|
||||
{
|
||||
var server = app.ApplicationServices.GetRequiredService<MqttServer>();
|
||||
|
||||
configure(server);
|
||||
|
||||
return app;
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,18 @@
|
||||
using MQTTnet.Server;
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.AspNetCore
|
||||
namespace MQTTnet.AspNetCore;
|
||||
|
||||
public sealed class AspNetMqttServerOptionsBuilder : MqttServerOptionsBuilder
|
||||
{
|
||||
public class AspNetMqttServerOptionsBuilder : MqttServerOptionsBuilder
|
||||
public AspNetMqttServerOptionsBuilder(IServiceProvider serviceProvider)
|
||||
{
|
||||
public AspNetMqttServerOptionsBuilder(IServiceProvider serviceProvider)
|
||||
{
|
||||
ServiceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
public IServiceProvider ServiceProvider { get; }
|
||||
ServiceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
|
||||
}
|
||||
}
|
||||
|
||||
public IServiceProvider ServiceProvider { get; }
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace MQTTnet.AspNetCore;
|
||||
|
||||
public static class BufferExtensions
|
||||
{
|
||||
public static ArraySegment<byte> GetArray(this ReadOnlyMemory<byte> memory)
|
||||
{
|
||||
if (!MemoryMarshal.TryGetArray(memory, out var result))
|
||||
{
|
||||
throw new InvalidOperationException("Buffer backed by array was expected");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.AspNetCore.Client.Tcp;
|
||||
using MQTTnet.Client;
|
||||
using MQTTnet.Client.Options;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Formatter;
|
||||
|
||||
namespace MQTTnet.AspNetCore.Client
|
||||
{
|
||||
public class MqttClientConnectionContextFactory : IMqttClientAdapterFactory
|
||||
{
|
||||
public IMqttChannelAdapter CreateClientAdapter(IMqttClientOptions options, IMqttNetChildLogger logger)
|
||||
{
|
||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||
|
||||
switch (options.ChannelOptions)
|
||||
{
|
||||
case MqttClientTcpOptions tcpOptions:
|
||||
{
|
||||
var endpoint = new DnsEndPoint(tcpOptions.Server, tcpOptions.GetPort());
|
||||
var tcpConnection = new TcpConnection(endpoint);
|
||||
|
||||
var writer = new SpanBasedMqttPacketWriter();
|
||||
var formatter = new MqttPacketFormatterAdapter(options.ProtocolVersion, writer);
|
||||
return new MqttConnectionContext(formatter, tcpConnection);
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace MQTTnet.AspNetCore.Client.Tcp
|
||||
{
|
||||
public static class BufferExtensions
|
||||
{
|
||||
public static ArraySegment<byte> GetArray(this Memory<byte> memory)
|
||||
{
|
||||
return ((ReadOnlyMemory<byte>)memory).GetArray();
|
||||
}
|
||||
|
||||
public static ArraySegment<byte> GetArray(this ReadOnlyMemory<byte> memory)
|
||||
{
|
||||
if (!MemoryMarshal.TryGetArray(memory, out var result))
|
||||
{
|
||||
throw new InvalidOperationException("Buffer backed by array was expected");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
using System.IO.Pipelines;
|
||||
|
||||
namespace MQTTnet.AspNetCore.Client.Tcp
|
||||
{
|
||||
public class DuplexPipe : IDuplexPipe
|
||||
{
|
||||
public DuplexPipe(PipeReader reader, PipeWriter writer)
|
||||
{
|
||||
Input = reader;
|
||||
Output = writer;
|
||||
}
|
||||
|
||||
public PipeReader Input { get; }
|
||||
|
||||
public PipeWriter Output { get; }
|
||||
|
||||
public static DuplexPipePair CreateConnectionPair(PipeOptions inputOptions, PipeOptions outputOptions)
|
||||
{
|
||||
var input = new Pipe(inputOptions);
|
||||
var output = new Pipe(outputOptions);
|
||||
|
||||
var transportToApplication = new DuplexPipe(output.Reader, input.Writer);
|
||||
var applicationToTransport = new DuplexPipe(input.Reader, output.Writer);
|
||||
|
||||
return new DuplexPipePair(applicationToTransport, transportToApplication);
|
||||
}
|
||||
|
||||
// This class exists to work around issues with value tuple on .NET Framework
|
||||
public readonly struct DuplexPipePair
|
||||
{
|
||||
public IDuplexPipe Transport { get; }
|
||||
public IDuplexPipe Application { get; }
|
||||
|
||||
public DuplexPipePair(IDuplexPipe transport, IDuplexPipe application)
|
||||
{
|
||||
Transport = transport;
|
||||
Application = application;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO.Pipelines;
|
||||
using System.Net.Sockets;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MQTTnet.AspNetCore.Client.Tcp
|
||||
{
|
||||
public class SocketAwaitable : ICriticalNotifyCompletion
|
||||
{
|
||||
private static readonly Action _callbackCompleted = () => { };
|
||||
|
||||
private readonly PipeScheduler _ioScheduler;
|
||||
|
||||
private Action _callback;
|
||||
private int _bytesTransferred;
|
||||
private SocketError _error;
|
||||
|
||||
public SocketAwaitable(PipeScheduler ioScheduler)
|
||||
{
|
||||
_ioScheduler = ioScheduler;
|
||||
}
|
||||
|
||||
public bool IsCompleted => ReferenceEquals(_callback, _callbackCompleted);
|
||||
|
||||
public SocketAwaitable GetAwaiter() => this;
|
||||
|
||||
public int GetResult()
|
||||
{
|
||||
Debug.Assert(ReferenceEquals(_callback, _callbackCompleted));
|
||||
|
||||
_callback = null;
|
||||
|
||||
if (_error != SocketError.Success)
|
||||
{
|
||||
throw new SocketException((int)_error);
|
||||
}
|
||||
|
||||
return _bytesTransferred;
|
||||
}
|
||||
|
||||
public void OnCompleted(Action continuation)
|
||||
{
|
||||
if (ReferenceEquals(_callback, _callbackCompleted) ||
|
||||
ReferenceEquals(Interlocked.CompareExchange(ref _callback, continuation, null), _callbackCompleted))
|
||||
{
|
||||
Task.Run(continuation);
|
||||
}
|
||||
}
|
||||
|
||||
public void UnsafeOnCompleted(Action continuation)
|
||||
{
|
||||
OnCompleted(continuation);
|
||||
}
|
||||
|
||||
public void Complete(int bytesTransferred, SocketError socketError)
|
||||
{
|
||||
_error = socketError;
|
||||
_bytesTransferred = bytesTransferred;
|
||||
var continuation = Interlocked.Exchange(ref _callback, _callbackCompleted);
|
||||
|
||||
if (continuation != null)
|
||||
{
|
||||
_ioScheduler.Schedule(state => ((Action)state)(), continuation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
using System;
|
||||
using System.IO.Pipelines;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace MQTTnet.AspNetCore.Client.Tcp
|
||||
{
|
||||
public class SocketReceiver
|
||||
{
|
||||
private readonly Socket _socket;
|
||||
private readonly SocketAsyncEventArgs _eventArgs = new SocketAsyncEventArgs();
|
||||
private readonly SocketAwaitable _awaitable;
|
||||
|
||||
public SocketReceiver(Socket socket, PipeScheduler scheduler)
|
||||
{
|
||||
_socket = socket;
|
||||
_awaitable = new SocketAwaitable(scheduler);
|
||||
_eventArgs.UserToken = _awaitable;
|
||||
_eventArgs.Completed += (_, e) => ((SocketAwaitable)e.UserToken).Complete(e.BytesTransferred, e.SocketError);
|
||||
}
|
||||
|
||||
public SocketAwaitable ReceiveAsync(Memory<byte> buffer)
|
||||
{
|
||||
#if NETCOREAPP2_1
|
||||
_eventArgs.SetBuffer(buffer);
|
||||
#else
|
||||
var segment = buffer.GetArray();
|
||||
_eventArgs.SetBuffer(segment.Array, segment.Offset, segment.Count);
|
||||
#endif
|
||||
if (!_socket.ReceiveAsync(_eventArgs))
|
||||
{
|
||||
_awaitable.Complete(_eventArgs.BytesTransferred, _eventArgs.SocketError);
|
||||
}
|
||||
|
||||
return _awaitable;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO.Pipelines;
|
||||
using System.Net.Sockets;
|
||||
|
||||
#if NETCOREAPP2_1
|
||||
using System.Runtime.InteropServices;
|
||||
#endif
|
||||
|
||||
namespace MQTTnet.AspNetCore.Client.Tcp
|
||||
{
|
||||
public class SocketSender
|
||||
{
|
||||
private readonly Socket _socket;
|
||||
private readonly SocketAsyncEventArgs _eventArgs = new SocketAsyncEventArgs();
|
||||
private readonly SocketAwaitable _awaitable;
|
||||
|
||||
private List<ArraySegment<byte>> _bufferList;
|
||||
|
||||
public SocketSender(Socket socket, PipeScheduler scheduler)
|
||||
{
|
||||
_socket = socket;
|
||||
_awaitable = new SocketAwaitable(scheduler);
|
||||
_eventArgs.UserToken = _awaitable;
|
||||
_eventArgs.Completed += (_, e) => ((SocketAwaitable)e.UserToken).Complete(e.BytesTransferred, e.SocketError);
|
||||
}
|
||||
|
||||
public SocketAwaitable SendAsync(in ReadOnlySequence<byte> buffers)
|
||||
{
|
||||
if (buffers.IsSingleSegment)
|
||||
{
|
||||
return SendAsync(buffers.First);
|
||||
}
|
||||
|
||||
#if NETCOREAPP2_1
|
||||
if (!_eventArgs.MemoryBuffer.Equals(Memory<byte>.Empty))
|
||||
#else
|
||||
if (_eventArgs.Buffer != null)
|
||||
#endif
|
||||
{
|
||||
_eventArgs.SetBuffer(null, 0, 0);
|
||||
}
|
||||
|
||||
_eventArgs.BufferList = GetBufferList(buffers);
|
||||
|
||||
if (!_socket.SendAsync(_eventArgs))
|
||||
{
|
||||
_awaitable.Complete(_eventArgs.BytesTransferred, _eventArgs.SocketError);
|
||||
}
|
||||
|
||||
return _awaitable;
|
||||
}
|
||||
|
||||
private SocketAwaitable SendAsync(ReadOnlyMemory<byte> memory)
|
||||
{
|
||||
// The BufferList getter is much less expensive then the setter.
|
||||
if (_eventArgs.BufferList != null)
|
||||
{
|
||||
_eventArgs.BufferList = null;
|
||||
}
|
||||
|
||||
#if NETCOREAPP2_1
|
||||
_eventArgs.SetBuffer(MemoryMarshal.AsMemory(memory));
|
||||
#else
|
||||
var segment = memory.GetArray();
|
||||
_eventArgs.SetBuffer(segment.Array, segment.Offset, segment.Count);
|
||||
#endif
|
||||
if (!_socket.SendAsync(_eventArgs))
|
||||
{
|
||||
_awaitable.Complete(_eventArgs.BytesTransferred, _eventArgs.SocketError);
|
||||
}
|
||||
|
||||
return _awaitable;
|
||||
}
|
||||
|
||||
private List<ArraySegment<byte>> GetBufferList(in ReadOnlySequence<byte> buffer)
|
||||
{
|
||||
Debug.Assert(!buffer.IsEmpty);
|
||||
Debug.Assert(!buffer.IsSingleSegment);
|
||||
|
||||
if (_bufferList == null)
|
||||
{
|
||||
_bufferList = new List<ArraySegment<byte>>();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Buffers are pooled, so it's OK to root them until the next multi-buffer write.
|
||||
_bufferList.Clear();
|
||||
}
|
||||
|
||||
foreach (var b in buffer)
|
||||
{
|
||||
_bufferList.Add(b.GetArray());
|
||||
}
|
||||
|
||||
return _bufferList;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,258 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Pipelines;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Connections;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using MQTTnet.Exceptions;
|
||||
|
||||
namespace MQTTnet.AspNetCore.Client.Tcp
|
||||
{
|
||||
public class TcpConnection : ConnectionContext
|
||||
{
|
||||
private volatile bool _aborted;
|
||||
private readonly EndPoint _endPoint;
|
||||
private SocketSender _sender;
|
||||
private SocketReceiver _receiver;
|
||||
|
||||
private Socket _socket;
|
||||
private IDuplexPipe _application;
|
||||
|
||||
public bool IsConnected { get; private set; }
|
||||
public override string ConnectionId { get; set; }
|
||||
public override IFeatureCollection Features { get; }
|
||||
public override IDictionary<object, object> Items { get; set; }
|
||||
public override IDuplexPipe Transport { get; set; }
|
||||
|
||||
public TcpConnection(EndPoint endPoint)
|
||||
{
|
||||
_endPoint = endPoint;
|
||||
}
|
||||
|
||||
public TcpConnection(Socket socket)
|
||||
{
|
||||
_socket = socket;
|
||||
_endPoint = socket.RemoteEndPoint;
|
||||
|
||||
_sender = new SocketSender(_socket, PipeScheduler.ThreadPool);
|
||||
_receiver = new SocketReceiver(_socket, PipeScheduler.ThreadPool);
|
||||
}
|
||||
|
||||
public Task DisposeAsync()
|
||||
{
|
||||
IsConnected = false;
|
||||
|
||||
Transport?.Output.Complete();
|
||||
Transport?.Input.Complete();
|
||||
|
||||
_socket?.Dispose();
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task StartAsync()
|
||||
{
|
||||
if (_socket == null)
|
||||
{
|
||||
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
_sender = new SocketSender(_socket, PipeScheduler.ThreadPool);
|
||||
_receiver = new SocketReceiver(_socket, PipeScheduler.ThreadPool);
|
||||
await _socket.ConnectAsync(_endPoint);
|
||||
}
|
||||
|
||||
var pair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default);
|
||||
|
||||
Transport = pair.Transport;
|
||||
_application = pair.Application;
|
||||
|
||||
_ = ExecuteAsync();
|
||||
|
||||
IsConnected = true;
|
||||
}
|
||||
|
||||
private async Task ExecuteAsync()
|
||||
{
|
||||
Exception sendError = null;
|
||||
try
|
||||
{
|
||||
// Spawn send and receive logic
|
||||
var receiveTask = DoReceive();
|
||||
var sendTask = DoSend();
|
||||
|
||||
// If the sending task completes then close the receive
|
||||
// We don't need to do this in the other direction because the kestrel
|
||||
// will trigger the output closing once the input is complete.
|
||||
if (await Task.WhenAny(receiveTask, sendTask) == sendTask)
|
||||
{
|
||||
// Tell the reader it's being aborted
|
||||
_socket.Dispose();
|
||||
}
|
||||
|
||||
// Now wait for both to complete
|
||||
await receiveTask;
|
||||
sendError = await sendTask;
|
||||
|
||||
// Dispose the socket(should noop if already called)
|
||||
_socket.Dispose();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Unexpected exception in {nameof(TcpConnection)}.{nameof(StartAsync)}: " + ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Complete the output after disposing the socket
|
||||
_application.Input.Complete(sendError);
|
||||
}
|
||||
}
|
||||
private async Task DoReceive()
|
||||
{
|
||||
Exception error = null;
|
||||
|
||||
try
|
||||
{
|
||||
await ProcessReceives();
|
||||
}
|
||||
catch (SocketException ex) when (ex.SocketErrorCode == SocketError.ConnectionReset)
|
||||
{
|
||||
error = new MqttCommunicationException(ex);
|
||||
}
|
||||
catch (SocketException ex) when (ex.SocketErrorCode == SocketError.OperationAborted ||
|
||||
ex.SocketErrorCode == SocketError.ConnectionAborted ||
|
||||
ex.SocketErrorCode == SocketError.Interrupted ||
|
||||
ex.SocketErrorCode == SocketError.InvalidArgument)
|
||||
{
|
||||
if (!_aborted)
|
||||
{
|
||||
// Calling Dispose after ReceiveAsync can cause an "InvalidArgument" error on *nix.
|
||||
error = ConnectionAborted();
|
||||
}
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
if (!_aborted)
|
||||
{
|
||||
error = ConnectionAborted();
|
||||
}
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
error = ex;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
error = new IOException(ex.Message, ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (_aborted)
|
||||
{
|
||||
error = error ?? ConnectionAborted();
|
||||
}
|
||||
|
||||
_application.Output.Complete(error);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ProcessReceives()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
// Ensure we have some reasonable amount of buffer space
|
||||
var buffer = _application.Output.GetMemory();
|
||||
|
||||
var bytesReceived = await _receiver.ReceiveAsync(buffer);
|
||||
|
||||
if (bytesReceived == 0)
|
||||
{
|
||||
// FIN
|
||||
break;
|
||||
}
|
||||
|
||||
_application.Output.Advance(bytesReceived);
|
||||
|
||||
var flushTask = _application.Output.FlushAsync();
|
||||
|
||||
if (!flushTask.IsCompleted)
|
||||
{
|
||||
await flushTask;
|
||||
}
|
||||
|
||||
var result = flushTask.GetAwaiter().GetResult();
|
||||
if (result.IsCompleted)
|
||||
{
|
||||
// Pipe consumer is shut down, do we stop writing
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Exception ConnectionAborted()
|
||||
{
|
||||
return new MqttCommunicationException("Connection Aborted");
|
||||
}
|
||||
|
||||
private async Task<Exception> DoSend()
|
||||
{
|
||||
Exception error = null;
|
||||
|
||||
try
|
||||
{
|
||||
await ProcessSends();
|
||||
}
|
||||
catch (SocketException ex) when (ex.SocketErrorCode == SocketError.OperationAborted)
|
||||
{
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
error = ex;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
error = new IOException(ex.Message, ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_aborted = true;
|
||||
_socket.Shutdown(SocketShutdown.Both);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
private async Task ProcessSends()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
// Wait for data to write from the pipe producer
|
||||
var result = await _application.Input.ReadAsync();
|
||||
var buffer = result.Buffer;
|
||||
|
||||
if (result.IsCanceled)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
var end = buffer.End;
|
||||
var isCompleted = result.IsCompleted;
|
||||
if (!buffer.IsEmpty)
|
||||
{
|
||||
await _sender.SendAsync(buffer);
|
||||
}
|
||||
|
||||
_application.Input.AdvanceTo(end);
|
||||
|
||||
if (isCompleted)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,8 @@
|
||||
using Microsoft.AspNetCore.Connections;
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Microsoft.AspNetCore.Connections;
|
||||
|
||||
namespace MQTTnet.AspNetCore
|
||||
{
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.IO.Pipelines;
|
||||
|
||||
namespace MQTTnet.AspNetCore;
|
||||
|
||||
public class DuplexPipe : IDuplexPipe
|
||||
{
|
||||
public DuplexPipe(PipeReader reader, PipeWriter writer)
|
||||
{
|
||||
Input = reader;
|
||||
Output = writer;
|
||||
}
|
||||
|
||||
public PipeReader Input { get; }
|
||||
|
||||
public PipeWriter Output { get; }
|
||||
|
||||
public static DuplexPipePair CreateConnectionPair(PipeOptions inputOptions, PipeOptions outputOptions)
|
||||
{
|
||||
var input = new Pipe(inputOptions);
|
||||
var output = new Pipe(outputOptions);
|
||||
|
||||
var transportToApplication = new DuplexPipe(output.Reader, input.Writer);
|
||||
var applicationToTransport = new DuplexPipe(input.Reader, output.Writer);
|
||||
|
||||
return new DuplexPipePair(applicationToTransport, transportToApplication);
|
||||
}
|
||||
|
||||
// This class exists to work around issues with value tuple on .NET Framework
|
||||
public readonly struct DuplexPipePair
|
||||
{
|
||||
public IDuplexPipe Transport { get; }
|
||||
public IDuplexPipe Application { get; }
|
||||
|
||||
public DuplexPipePair(IDuplexPipe transport, IDuplexPipe application)
|
||||
{
|
||||
Transport = transport;
|
||||
Application = application;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
|
||||
namespace MQTTnet.AspNetCore
|
||||
{
|
||||
public static class EndpointRouterExtensions
|
||||
{
|
||||
public static void MapMqtt(this IEndpointRouteBuilder endpoints, string pattern)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(endpoints);
|
||||
|
||||
endpoints.MapConnectionHandler<MqttConnectionHandler>(pattern, options =>
|
||||
{
|
||||
options.WebSockets.SubProtocolSelector = MqttSubProtocolSelector.SelectSubProtocol;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,28 +1,63 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard2.0;netcoreapp2.1</TargetFrameworks>
|
||||
<Product />
|
||||
<Company />
|
||||
<Authors />
|
||||
<PackageId />
|
||||
<LangVersion>7.2</LangVersion>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AssemblyName>MQTTnet.AspNetCore</AssemblyName>
|
||||
<RootNamespace>MQTTnet.AspNetCore</RootNamespace>
|
||||
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||
<Company>The contributors of MQTTnet</Company>
|
||||
<Product>MQTTnet</Product>
|
||||
<Description>MQTTnet is a high performance .NET library for MQTT based communication. It provides a MQTT client and a MQTT server (broker) and supports v3.1.0, v3.1.1 and v5.0.0 of the MQTT protocol.</Description>
|
||||
<Authors>The contributors of MQTTnet</Authors>
|
||||
<PackageId>MQTTnet.AspNetCore</PackageId>
|
||||
<SignAssembly>false</SignAssembly>
|
||||
<DelaySign>false</DelaySign>
|
||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
<Copyright>Copyright (c) .NET Foundation and Contributors</Copyright>
|
||||
<PackageProjectUrl>https://github.com/dotnet/MQTTnet</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/dotnet/MQTTnet.git</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<PackageTags>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 Blazor</PackageTags>
|
||||
<NeutralLanguage>en-US</NeutralLanguage>
|
||||
<EnableNETAnalyzers>false</EnableNETAnalyzers>
|
||||
<EnforceCodeStyleInBuild>false</EnforceCodeStyleInBuild>
|
||||
<PackageIcon>nuget.png</PackageIcon>
|
||||
<EmbedUntrackedSources>true</EmbedUntrackedSources>
|
||||
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<PackageReleaseNotes>For release notes please go to MQTTnet release notes (https://www.nuget.org/packages/MQTTnet/).</PackageReleaseNotes>
|
||||
<DisableImplicitAspNetCoreAnalyzers>true</DisableImplicitAspNetCoreAnalyzers>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<NoWarn>1591;NETSDK1138;NU1803;NU1901;NU1902</NoWarn>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<NuGetAuditMode>all</NuGetAuditMode>
|
||||
<NuGetAudit>true</NuGetAudit>
|
||||
<NuGetAuditLevel>low</NuGetAuditLevel>
|
||||
<NuGetAuditLevel>low</NuGetAuditLevel>
|
||||
<AnalysisLevel>latest-Recommended</AnalysisLevel>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<DefineConstants>RELEASE;NETSTANDARD2_0</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\..\Images\nuget.png">
|
||||
<Pack>True</Pack>
|
||||
<PackagePath>\</PackagePath>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Connections.Abstractions" Version="2.1.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Http.Connections" Version="1.0.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="2.1.1" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All"/>
|
||||
<FrameworkReference Include="Microsoft.AspNetCore.App"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MQTTnet\MQTTnet.csproj" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release'">
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MQTTnet.Server\MQTTnet.Server.csproj"/>
|
||||
<ProjectReference Include="..\MQTTnet\MQTTnet.csproj"/>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Diagnostics.Logger;
|
||||
using MQTTnet.Formatter;
|
||||
|
||||
namespace MQTTnet.AspNetCore
|
||||
{
|
||||
public sealed class MqttClientConnectionContextFactory : IMqttClientAdapterFactory
|
||||
{
|
||||
public IMqttChannelAdapter CreateClientAdapter(MqttClientOptions options, MqttPacketInspector packetInspector, IMqttNetLogger logger)
|
||||
{
|
||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||
|
||||
switch (options.ChannelOptions)
|
||||
{
|
||||
case MqttClientTcpOptions tcpOptions:
|
||||
{
|
||||
var tcpConnection = new SocketConnection(tcpOptions.RemoteEndpoint);
|
||||
|
||||
var formatter = new MqttPacketFormatterAdapter(options.ProtocolVersion, new MqttBufferWriter(4096, 65535));
|
||||
return new MqttConnectionContext(formatter, tcpConnection);
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,181 +1,239 @@
|
||||
using Microsoft.AspNetCore.Connections;
|
||||
using Microsoft.AspNetCore.Http.Connections.Features;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.AspNetCore.Client.Tcp;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Packets;
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.IO.Pipelines;
|
||||
using System.Net;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Connections;
|
||||
using Microsoft.AspNetCore.Http.Connections.Features;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Packets;
|
||||
|
||||
namespace MQTTnet.AspNetCore
|
||||
namespace MQTTnet.AspNetCore;
|
||||
|
||||
public sealed class MqttConnectionContext : IMqttChannelAdapter
|
||||
{
|
||||
public class MqttConnectionContext : IMqttChannelAdapter
|
||||
readonly ConnectionContext _connection;
|
||||
readonly AsyncLock _writerLock = new();
|
||||
|
||||
PipeReader _input;
|
||||
PipeWriter _output;
|
||||
|
||||
public MqttConnectionContext(MqttPacketFormatterAdapter packetFormatterAdapter, ConnectionContext connection)
|
||||
{
|
||||
public MqttConnectionContext(MqttPacketFormatterAdapter packetFormatterAdapter, ConnectionContext connection)
|
||||
{
|
||||
PacketFormatterAdapter = packetFormatterAdapter ?? throw new ArgumentNullException(nameof(packetFormatterAdapter));
|
||||
Connection = connection ?? throw new ArgumentNullException(nameof(connection));
|
||||
|
||||
if (Connection.Transport != null)
|
||||
{
|
||||
_input = Connection.Transport.Input;
|
||||
_output = Connection.Transport.Output;
|
||||
}
|
||||
|
||||
_reader = new SpanBasedMqttPacketBodyReader();
|
||||
}
|
||||
|
||||
private PipeReader _input;
|
||||
private PipeWriter _output;
|
||||
private readonly SpanBasedMqttPacketBodyReader _reader;
|
||||
|
||||
public string Endpoint
|
||||
{
|
||||
get
|
||||
{
|
||||
var connection = Http?.HttpContext?.Connection;
|
||||
if (connection == null)
|
||||
{
|
||||
return Connection.ConnectionId;
|
||||
}
|
||||
|
||||
return $"{connection.RemoteIpAddress}:{connection.RemotePort}";
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsSecureConnection => Http?.HttpContext?.Request?.IsHttps ?? false;
|
||||
|
||||
public X509Certificate2 ClientCertificate => Http?.HttpContext?.Connection?.ClientCertificate;
|
||||
|
||||
private IHttpContextFeature Http => Connection.Features.Get<IHttpContextFeature>();
|
||||
|
||||
public ConnectionContext Connection { get; }
|
||||
public MqttPacketFormatterAdapter PacketFormatterAdapter { get; }
|
||||
|
||||
public long BytesSent { get; set; }
|
||||
public long BytesReceived { get; set; }
|
||||
|
||||
public Action ReadingPacketStartedCallback { get; set; }
|
||||
public Action ReadingPacketCompletedCallback { get; set; }
|
||||
|
||||
private readonly SemaphoreSlim _writerSemaphore = new SemaphoreSlim(1, 1);
|
||||
|
||||
public async Task ConnectAsync(TimeSpan timeout, CancellationToken cancellationToken)
|
||||
{
|
||||
if (Connection is TcpConnection tcp && !tcp.IsConnected)
|
||||
{
|
||||
await tcp.StartAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
_input = Connection.Transport.Input;
|
||||
_output = Connection.Transport.Output;
|
||||
}
|
||||
|
||||
public Task DisconnectAsync(TimeSpan timeout, CancellationToken cancellationToken)
|
||||
{
|
||||
_input?.Complete();
|
||||
_output?.Complete();
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task<MqttBasePacket> ReceivePacketAsync(TimeSpan timeout, CancellationToken cancellationToken)
|
||||
{
|
||||
var input = Connection.Transport.Input;
|
||||
|
||||
try
|
||||
{
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
ReadResult readResult;
|
||||
var readTask = input.ReadAsync(cancellationToken);
|
||||
if (readTask.IsCompleted)
|
||||
{
|
||||
readResult = readTask.Result;
|
||||
}
|
||||
else
|
||||
{
|
||||
readResult = await readTask.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
var buffer = readResult.Buffer;
|
||||
|
||||
var consumed = buffer.Start;
|
||||
var observed = buffer.Start;
|
||||
|
||||
try
|
||||
{
|
||||
if (!buffer.IsEmpty)
|
||||
{
|
||||
if (PacketFormatterAdapter.TryDecode(_reader, buffer, out var packet, out consumed, out observed, out var received))
|
||||
{
|
||||
BytesReceived += received;
|
||||
return packet;
|
||||
}
|
||||
else
|
||||
{
|
||||
// we did receive something but the message is not yet complete
|
||||
ReadingPacketStartedCallback?.Invoke();
|
||||
}
|
||||
}
|
||||
else if (readResult.IsCompleted)
|
||||
{
|
||||
throw new MqttCommunicationException("Connection Aborted");
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
// The buffer was sliced up to where it was consumed, so we can just advance to the start.
|
||||
// We mark examined as buffer.End so that if we didn't receive a full frame, we'll wait for more data
|
||||
// before yielding the read again.
|
||||
input.AdvanceTo(consumed, observed);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
ReadingPacketCompletedCallback?.Invoke();
|
||||
}
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
return null;
|
||||
}
|
||||
|
||||
public void ResetStatistics()
|
||||
{
|
||||
BytesReceived = 0;
|
||||
BytesSent = 0;
|
||||
}
|
||||
|
||||
public async Task SendPacketAsync(MqttBasePacket packet, TimeSpan timeout, CancellationToken cancellationToken)
|
||||
{
|
||||
var formatter = PacketFormatterAdapter;
|
||||
|
||||
|
||||
await _writerSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
var buffer = formatter.Encode(packet);
|
||||
var msg = buffer.AsMemory();
|
||||
var output = _output;
|
||||
msg.CopyTo(output.GetMemory(msg.Length));
|
||||
BytesSent += msg.Length;
|
||||
PacketFormatterAdapter.FreeBuffer();
|
||||
output.Advance(msg.Length);
|
||||
await output.FlushAsync().ConfigureAwait(false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_writerSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
PacketFormatterAdapter = packetFormatterAdapter ?? throw new ArgumentNullException(nameof(packetFormatterAdapter));
|
||||
_connection = connection ?? throw new ArgumentNullException(nameof(connection));
|
||||
|
||||
if (!(_connection is SocketConnection tcp) || tcp.IsConnected)
|
||||
{
|
||||
_input = connection.Transport.Input;
|
||||
_output = connection.Transport.Output;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public long BytesReceived { get; private set; }
|
||||
|
||||
public long BytesSent { get; private set; }
|
||||
|
||||
public X509Certificate2 ClientCertificate
|
||||
{
|
||||
get
|
||||
{
|
||||
// mqtt over tcp
|
||||
var tlsFeature = _connection.Features.Get<ITlsConnectionFeature>();
|
||||
if (tlsFeature != null)
|
||||
{
|
||||
return tlsFeature.ClientCertificate;
|
||||
}
|
||||
|
||||
// mqtt over websocket
|
||||
var httpFeature = _connection.Features.Get<IHttpContextFeature>();
|
||||
return httpFeature?.HttpContext?.Connection.ClientCertificate;
|
||||
}
|
||||
}
|
||||
|
||||
public EndPoint RemoteEndPoint
|
||||
{
|
||||
get
|
||||
{
|
||||
// mqtt over tcp
|
||||
if (_connection.RemoteEndPoint != null)
|
||||
{
|
||||
return _connection.RemoteEndPoint;
|
||||
}
|
||||
|
||||
// mqtt over websocket
|
||||
var httpFeature = _connection.Features.Get<IHttpConnectionFeature>();
|
||||
if (httpFeature?.RemoteIpAddress != null)
|
||||
{
|
||||
return new IPEndPoint(httpFeature.RemoteIpAddress, httpFeature.RemotePort);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsSecureConnection
|
||||
{
|
||||
get
|
||||
{
|
||||
// mqtt over tcp
|
||||
var tlsFeature = _connection.Features.Get<ITlsConnectionFeature>();
|
||||
if (tlsFeature != null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// mqtt over websocket
|
||||
var httpFeature = _connection.Features.Get<IHttpContextFeature>();
|
||||
if (httpFeature?.HttpContext != null)
|
||||
{
|
||||
return httpFeature.HttpContext.Request.IsHttps;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public MqttPacketFormatterAdapter PacketFormatterAdapter { get; }
|
||||
|
||||
public async Task ConnectAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
if (_connection is SocketConnection tcp && !tcp.IsConnected)
|
||||
{
|
||||
await tcp.StartAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
_input = _connection.Transport.Input;
|
||||
_output = _connection.Transport.Output;
|
||||
}
|
||||
|
||||
public Task DisconnectAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_input?.Complete();
|
||||
_output?.Complete();
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_writerLock.Dispose();
|
||||
}
|
||||
|
||||
public async Task<MqttPacket> ReceivePacketAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
ReadResult readResult;
|
||||
var readTask = _input.ReadAsync(cancellationToken);
|
||||
if (readTask.IsCompleted)
|
||||
{
|
||||
readResult = readTask.Result;
|
||||
}
|
||||
else
|
||||
{
|
||||
readResult = await readTask.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
var buffer = readResult.Buffer;
|
||||
|
||||
var consumed = buffer.Start;
|
||||
var observed = buffer.Start;
|
||||
|
||||
try
|
||||
{
|
||||
if (!buffer.IsEmpty)
|
||||
{
|
||||
if (PacketFormatterAdapter.TryDecode(buffer, out var packet, out consumed, out observed, out var received))
|
||||
{
|
||||
BytesReceived += received;
|
||||
return packet;
|
||||
}
|
||||
}
|
||||
else if (readResult.IsCompleted)
|
||||
{
|
||||
throw new MqttCommunicationException("Connection Aborted");
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
// The buffer was sliced up to where it was consumed, so we can just advance to the start.
|
||||
// We mark examined as buffer.End so that if we didn't receive a full frame, we'll wait for more data
|
||||
// before yielding the read again.
|
||||
_input.AdvanceTo(consumed, observed);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
// completing the channel makes sure that there is no more data read after a protocol error
|
||||
_input?.Complete(exception);
|
||||
_output?.Complete(exception);
|
||||
|
||||
throw;
|
||||
}
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
return null;
|
||||
}
|
||||
|
||||
public void ResetStatistics()
|
||||
{
|
||||
BytesReceived = 0;
|
||||
BytesSent = 0;
|
||||
}
|
||||
|
||||
public async Task SendPacketAsync(MqttPacket packet, CancellationToken cancellationToken)
|
||||
{
|
||||
using (await _writerLock.EnterAsync(cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
try
|
||||
{
|
||||
var buffer = PacketFormatterAdapter.Encode(packet);
|
||||
|
||||
if (buffer.Payload.Length == 0)
|
||||
{
|
||||
// zero copy
|
||||
// https://github.com/dotnet/runtime/blob/e31ddfdc4f574b26231233dc10c9a9c402f40590/src/libraries/System.IO.Pipelines/src/System/IO/Pipelines/StreamPipeWriter.cs#L279
|
||||
await _output.WriteAsync(buffer.Packet, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
WritePacketBuffer(_output, buffer);
|
||||
await _output.FlushAsync(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
BytesSent += buffer.Length;
|
||||
}
|
||||
finally
|
||||
{
|
||||
PacketFormatterAdapter.Cleanup();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void WritePacketBuffer(PipeWriter output, MqttPacketBuffer buffer)
|
||||
{
|
||||
// copy MqttPacketBuffer's Packet and Payload to the same buffer block of PipeWriter
|
||||
// MqttPacket will be transmitted within the bounds of a WebSocket frame after PipeWriter.FlushAsync
|
||||
|
||||
var span = output.GetSpan(buffer.Length);
|
||||
|
||||
buffer.Packet.AsSpan().CopyTo(span);
|
||||
int offset = buffer.Packet.Count;
|
||||
buffer.Payload.CopyTo(destination: span.Slice(offset));
|
||||
output.Advance(buffer.Length);
|
||||
}
|
||||
}
|
||||
@@ -1,50 +1,57 @@
|
||||
using Microsoft.AspNetCore.Connections;
|
||||
using Microsoft.AspNetCore.Connections.Features;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Server;
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Connections;
|
||||
using Microsoft.AspNetCore.Connections.Features;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Diagnostics.Logger;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.AspNetCore
|
||||
namespace MQTTnet.AspNetCore;
|
||||
|
||||
public sealed class MqttConnectionHandler : ConnectionHandler, IMqttServerAdapter
|
||||
{
|
||||
public class MqttConnectionHandler : ConnectionHandler, IMqttServerAdapter
|
||||
MqttServerOptions _serverOptions;
|
||||
|
||||
public Func<IMqttChannelAdapter, Task> ClientHandler { get; set; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
public Func<IMqttChannelAdapter, Task> ClientHandler { get; set; }
|
||||
}
|
||||
|
||||
public override async Task OnConnectedAsync(ConnectionContext connection)
|
||||
public override async Task OnConnectedAsync(ConnectionContext connection)
|
||||
{
|
||||
// required for websocket transport to work
|
||||
var transferFormatFeature = connection.Features.Get<ITransferFormatFeature>();
|
||||
if (transferFormatFeature != null)
|
||||
{
|
||||
// required for websocket transport to work
|
||||
var transferFormatFeature = connection.Features.Get<ITransferFormatFeature>();
|
||||
if (transferFormatFeature != null)
|
||||
transferFormatFeature.ActiveFormat = TransferFormat.Binary;
|
||||
}
|
||||
|
||||
var formatter = new MqttPacketFormatterAdapter(new MqttBufferWriter(_serverOptions.WriterBufferSize, _serverOptions.WriterBufferSizeMax));
|
||||
using (var adapter = new MqttConnectionContext(formatter, connection))
|
||||
{
|
||||
var clientHandler = ClientHandler;
|
||||
if (clientHandler != null)
|
||||
{
|
||||
transferFormatFeature.ActiveFormat = TransferFormat.Binary;
|
||||
await clientHandler(adapter).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
var writer = new SpanBasedMqttPacketWriter();
|
||||
var formatter = new MqttPacketFormatterAdapter(writer);
|
||||
using (var adapter = new MqttConnectionContext(formatter, connection))
|
||||
{
|
||||
var clientHandler = ClientHandler;
|
||||
if (clientHandler != null)
|
||||
{
|
||||
await clientHandler(adapter).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Task StartAsync(IMqttServerOptions options)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task StopAsync()
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Task StartAsync(MqttServerOptions options, IMqttNetLogger logger)
|
||||
{
|
||||
_serverOptions = options;
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task StopAsync()
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
@@ -1,32 +1,48 @@
|
||||
using System;
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
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.Diagnostics.Logger;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.AspNetCore
|
||||
namespace MQTTnet.AspNetCore;
|
||||
|
||||
public sealed class MqttHostedServer : MqttServer, IHostedService
|
||||
{
|
||||
public class MqttHostedServer : MqttServer, IHostedService
|
||||
readonly IHostApplicationLifetime _hostApplicationLifetime;
|
||||
readonly MqttServerFactory _mqttFactory;
|
||||
|
||||
public MqttHostedServer(
|
||||
IHostApplicationLifetime hostApplicationLifetime,
|
||||
MqttServerFactory mqttFactory,
|
||||
MqttServerOptions options,
|
||||
IEnumerable<IMqttServerAdapter> adapters,
|
||||
IMqttNetLogger logger) : base(options, adapters, logger)
|
||||
{
|
||||
private readonly IMqttServerOptions _options;
|
||||
|
||||
public MqttHostedServer(IMqttServerOptions options, IEnumerable<IMqttServerAdapter> adapters, IMqttNetLogger logger)
|
||||
: base(adapters, logger.CreateChildLogger(nameof(MqttHostedServer)))
|
||||
{
|
||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
||||
}
|
||||
|
||||
public Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return StartAsync(_options);
|
||||
}
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return StopAsync();
|
||||
}
|
||||
_mqttFactory = mqttFactory ?? throw new ArgumentNullException(nameof(mqttFactory));
|
||||
_hostApplicationLifetime = hostApplicationLifetime;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
// The yield makes sure that the hosted service is considered up and running.
|
||||
await Task.Yield();
|
||||
|
||||
_hostApplicationLifetime.ApplicationStarted.Register(OnStarted);
|
||||
}
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return StopAsync(_mqttFactory.CreateMqttServerStopOptionsBuilder().Build());
|
||||
}
|
||||
|
||||
void OnStarted()
|
||||
{
|
||||
_ = StartAsync();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace MQTTnet.AspNetCore;
|
||||
|
||||
public static class MqttSubProtocolSelector
|
||||
{
|
||||
public static string SelectSubProtocol(HttpRequest request)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(request);
|
||||
|
||||
string subProtocol = null;
|
||||
if (request.Headers.TryGetValue("Sec-WebSocket-Protocol", out var requestedSubProtocolValues))
|
||||
{
|
||||
subProtocol = SelectSubProtocol(requestedSubProtocolValues);
|
||||
}
|
||||
|
||||
return subProtocol;
|
||||
}
|
||||
|
||||
public static string SelectSubProtocol(IList<string> requestedSubProtocolValues)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(requestedSubProtocolValues);
|
||||
|
||||
// Order the protocols to also match "mqtt", "mqttv-3.1", "mqttv-3.11" etc.
|
||||
return requestedSubProtocolValues.OrderByDescending(p => p.Length).FirstOrDefault(p => p.ToLower().StartsWith("mqtt"));
|
||||
}
|
||||
}
|
||||
@@ -1,70 +1,68 @@
|
||||
using System;
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.WebSockets;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Diagnostics.Logger;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Implementations;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.AspNetCore
|
||||
namespace MQTTnet.AspNetCore;
|
||||
|
||||
public sealed class MqttWebSocketServerAdapter : IMqttServerAdapter
|
||||
{
|
||||
public class MqttWebSocketServerAdapter : IMqttServerAdapter
|
||||
IMqttNetLogger _logger = MqttNetNullLogger.Instance;
|
||||
|
||||
public Func<IMqttChannelAdapter, Task> ClientHandler { get; set; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
private readonly IMqttNetChildLogger _logger;
|
||||
}
|
||||
|
||||
public MqttWebSocketServerAdapter(IMqttNetChildLogger logger)
|
||||
public async Task RunWebSocketConnectionAsync(WebSocket webSocket, HttpContext httpContext)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(webSocket);
|
||||
|
||||
var remoteAddress = httpContext.Connection.RemoteIpAddress;
|
||||
var remoteEndPoint = remoteAddress == null ? null : new IPEndPoint(remoteAddress, httpContext.Connection.RemotePort);
|
||||
|
||||
var clientCertificate = await httpContext.Connection.GetClientCertificateAsync().ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
if (logger == null) throw new ArgumentNullException(nameof(logger));
|
||||
var isSecureConnection = clientCertificate != null;
|
||||
|
||||
_logger = logger.CreateChildLogger(nameof(MqttTcpServerAdapter));
|
||||
}
|
||||
|
||||
public Func<IMqttChannelAdapter, Task> ClientHandler { get; set; }
|
||||
|
||||
public Task StartAsync(IMqttServerOptions options)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task StopAsync()
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task RunWebSocketConnectionAsync(WebSocket webSocket, HttpContext httpContext)
|
||||
{
|
||||
if (webSocket == null) throw new ArgumentNullException(nameof(webSocket));
|
||||
|
||||
var endpoint = $"{httpContext.Connection.RemoteIpAddress}:{httpContext.Connection.RemotePort}";
|
||||
|
||||
var clientCertificate = await httpContext.Connection.GetClientCertificateAsync().ConfigureAwait(false);
|
||||
try
|
||||
var clientHandler = ClientHandler;
|
||||
if (clientHandler != null)
|
||||
{
|
||||
var isSecureConnection = clientCertificate != null;
|
||||
var formatter = new MqttPacketFormatterAdapter(new MqttBufferWriter(4096, 65535));
|
||||
var channel = new MqttWebSocketChannel(webSocket, remoteEndPoint, isSecureConnection, clientCertificate);
|
||||
|
||||
var clientHandler = ClientHandler;
|
||||
if (clientHandler != null)
|
||||
using (var channelAdapter = new MqttChannelAdapter(channel, formatter, _logger))
|
||||
{
|
||||
var writer = new SpanBasedMqttPacketWriter();
|
||||
var formatter = new MqttPacketFormatterAdapter(writer);
|
||||
var channel = new MqttWebSocketChannel(webSocket, endpoint, isSecureConnection, clientCertificate);
|
||||
|
||||
using (var channelAdapter = new MqttChannelAdapter(channel, formatter, _logger.CreateChildLogger(nameof(MqttWebSocketServerAdapter))))
|
||||
{
|
||||
await clientHandler(channelAdapter).ConfigureAwait(false);
|
||||
}
|
||||
await clientHandler(channelAdapter).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
clientCertificate?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
finally
|
||||
{
|
||||
clientCertificate?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public Task StartAsync(MqttServerOptions options, IMqttNetLogger logger)
|
||||
{
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task StopAsync()
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
@@ -1,110 +1,135 @@
|
||||
using System;
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Runtime.InteropServices;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Packets;
|
||||
|
||||
namespace MQTTnet.AspNetCore
|
||||
namespace MQTTnet.AspNetCore;
|
||||
|
||||
public static class ReaderExtensions
|
||||
{
|
||||
public static class ReaderExtensions
|
||||
public static bool TryDecode(
|
||||
this MqttPacketFormatterAdapter formatter,
|
||||
in ReadOnlySequence<byte> input,
|
||||
out MqttPacket packet,
|
||||
out SequencePosition consumed,
|
||||
out SequencePosition observed,
|
||||
out int bytesRead)
|
||||
{
|
||||
public static bool TryDecode(this MqttPacketFormatterAdapter formatter,
|
||||
SpanBasedMqttPacketBodyReader reader,
|
||||
in ReadOnlySequence<byte> input,
|
||||
out MqttBasePacket packet,
|
||||
out SequencePosition consumed,
|
||||
out SequencePosition observed,
|
||||
out int bytesRead)
|
||||
ArgumentNullException.ThrowIfNull(formatter);
|
||||
|
||||
packet = null;
|
||||
consumed = input.Start;
|
||||
observed = input.End;
|
||||
bytesRead = 0;
|
||||
var copy = input;
|
||||
|
||||
if (copy.Length < 2)
|
||||
{
|
||||
if (formatter == null) throw new ArgumentNullException(nameof(formatter));
|
||||
|
||||
packet = null;
|
||||
consumed = input.Start;
|
||||
observed = input.End;
|
||||
bytesRead = 0;
|
||||
var copy = input;
|
||||
|
||||
if (copy.Length < 2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var fixedheader = copy.First.Span[0];
|
||||
if (!TryReadBodyLength(ref copy, out int headerLength, out var bodyLength))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (copy.Length < bodyLength)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var bodySlice = copy.Slice(0, bodyLength);
|
||||
var buffer = bodySlice.GetMemory();
|
||||
reader.SetBuffer(buffer);
|
||||
|
||||
var receivedMqttPacket = new ReceivedMqttPacket(fixedheader, reader, buffer.Length + 2);
|
||||
|
||||
if (formatter.ProtocolVersion == MqttProtocolVersion.Unknown)
|
||||
{
|
||||
formatter.DetectProtocolVersion(receivedMqttPacket);
|
||||
}
|
||||
|
||||
packet = formatter.Decode(receivedMqttPacket);
|
||||
consumed = bodySlice.End;
|
||||
observed = bodySlice.End;
|
||||
bytesRead = headerLength + bodyLength;
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static ReadOnlyMemory<byte> GetMemory(this in ReadOnlySequence<byte> input)
|
||||
if (!TryReadBodyLength(ref copy, out var headerLength, out var bodyLength))
|
||||
{
|
||||
if (input.IsSingleSegment)
|
||||
{
|
||||
return input.First;
|
||||
}
|
||||
|
||||
// Should be rare
|
||||
return input.ToArray();
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool TryReadBodyLength(ref ReadOnlySequence<byte> input, out int headerLength, out int bodyLength)
|
||||
var fixedHeader = copy.First.Span[0];
|
||||
copy = copy.Slice(headerLength);
|
||||
if (copy.Length < bodyLength)
|
||||
{
|
||||
// Alorithm taken from https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/errata01/os/mqtt-v3.1.1-errata01-os-complete.html.
|
||||
var multiplier = 1;
|
||||
var value = 0;
|
||||
byte encodedByte;
|
||||
var index = 1;
|
||||
headerLength = 0;
|
||||
bodyLength = 0;
|
||||
|
||||
var temp = input.Slice(0, Math.Min(5, input.Length)).GetMemory();
|
||||
return false;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
if (index == temp.Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
encodedByte = temp.Span[index];
|
||||
index++;
|
||||
var bodySlice = copy.Slice(0, bodyLength);
|
||||
var bodySegment = GetArraySegment(ref bodySlice);
|
||||
|
||||
value += (byte)(encodedByte & 127) * multiplier;
|
||||
if (multiplier > 128 * 128 * 128)
|
||||
{
|
||||
throw new MqttProtocolViolationException($"Remaining length is invalid (Data={string.Join(",", temp.Slice(1, index).ToArray())}).");
|
||||
}
|
||||
var receivedMqttPacket = new ReceivedMqttPacket(fixedHeader, bodySegment, headerLength + bodyLength);
|
||||
if (formatter.ProtocolVersion == MqttProtocolVersion.Unknown)
|
||||
{
|
||||
formatter.DetectProtocolVersion(receivedMqttPacket);
|
||||
}
|
||||
|
||||
multiplier *= 128;
|
||||
} while ((encodedByte & 128) != 0);
|
||||
packet = formatter.Decode(receivedMqttPacket);
|
||||
consumed = bodySlice.End;
|
||||
observed = bodySlice.End;
|
||||
bytesRead = headerLength + bodyLength;
|
||||
return true;
|
||||
}
|
||||
|
||||
input = input.Slice(index);
|
||||
static ArraySegment<byte> GetArraySegment(ref ReadOnlySequence<byte> input)
|
||||
{
|
||||
if (input.IsSingleSegment && MemoryMarshal.TryGetArray(input.First, out var segment))
|
||||
{
|
||||
return segment;
|
||||
}
|
||||
|
||||
headerLength = index;
|
||||
bodyLength = value;
|
||||
return true;
|
||||
// Should be rare
|
||||
var array = input.ToArray();
|
||||
return new ArraySegment<byte>(array);
|
||||
}
|
||||
|
||||
|
||||
static void ThrowProtocolViolationException(ReadOnlySpan<byte> valueSpan, int index)
|
||||
{
|
||||
throw new MqttProtocolViolationException($"Remaining length is invalid (Data={string.Join(",", valueSpan.Slice(1, index).ToArray())}).");
|
||||
}
|
||||
|
||||
static bool TryReadBodyLength(ref ReadOnlySequence<byte> input, out int headerLength, out int bodyLength)
|
||||
{
|
||||
var valueSequence = input.Slice(0, Math.Min(5, input.Length));
|
||||
if (valueSequence.IsSingleSegment)
|
||||
{
|
||||
var valueSpan = valueSequence.FirstSpan;
|
||||
|
||||
return TryReadBodyLength(valueSpan, out headerLength, out bodyLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
Span<byte> valueSpan = stackalloc byte[8];
|
||||
valueSequence.CopyTo(valueSpan);
|
||||
valueSpan = valueSpan.Slice(0, (int)valueSequence.Length);
|
||||
return TryReadBodyLength(valueSpan, out headerLength, out bodyLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool TryReadBodyLength(ReadOnlySpan<byte> span, out int headerLength, out int bodyLength)
|
||||
{
|
||||
// Alorithm taken from https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/errata01/os/mqtt-v3.1.1-errata01-os-complete.html.
|
||||
var multiplier = 1;
|
||||
var value = 0;
|
||||
byte encodedByte;
|
||||
var index = 1;
|
||||
headerLength = 0;
|
||||
bodyLength = 0;
|
||||
|
||||
do
|
||||
{
|
||||
if (index == span.Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
encodedByte = span[index];
|
||||
index++;
|
||||
|
||||
value += (byte)(encodedByte & 127) * multiplier;
|
||||
if (multiplier > 128 * 128 * 128)
|
||||
{
|
||||
ThrowProtocolViolationException(span, index);
|
||||
}
|
||||
|
||||
multiplier *= 128;
|
||||
} while ((encodedByte & 128) != 0);
|
||||
|
||||
headerLength = index;
|
||||
bodyLength = value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,98 +1,109 @@
|
||||
using System;
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Diagnostics.Logger;
|
||||
using MQTTnet.Server;
|
||||
using MQTTnet.Implementations;
|
||||
using MQTTnet.Server.Internal.Adapter;
|
||||
|
||||
namespace MQTTnet.AspNetCore
|
||||
namespace MQTTnet.AspNetCore;
|
||||
|
||||
public static class ServiceCollectionExtensions
|
||||
{
|
||||
public static class ServiceCollectionExtensions
|
||||
public static IServiceCollection AddHostedMqttServer(this IServiceCollection services, MqttServerOptions options)
|
||||
{
|
||||
public static IServiceCollection AddHostedMqttServer(this IServiceCollection services, IMqttServerOptions options)
|
||||
{
|
||||
if (options == null) throw new ArgumentNullException(nameof(options));
|
||||
|
||||
services.AddSingleton(options);
|
||||
ArgumentNullException.ThrowIfNull(services);
|
||||
ArgumentNullException.ThrowIfNull(options);
|
||||
|
||||
services.AddHostedMqttServer();
|
||||
services.AddSingleton(options);
|
||||
services.AddHostedMqttServer();
|
||||
|
||||
return services;
|
||||
}
|
||||
return services;
|
||||
}
|
||||
|
||||
public static IServiceCollection AddHostedMqttServer(this IServiceCollection services, Action<MqttServerOptionsBuilder> configure)
|
||||
{
|
||||
services.AddSingleton<IMqttServerOptions>(s => {
|
||||
var builder = new MqttServerOptionsBuilder();
|
||||
configure(builder);
|
||||
return builder.Build();
|
||||
});
|
||||
public static IServiceCollection AddHostedMqttServer(this IServiceCollection services, Action<MqttServerOptionsBuilder> configure)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(services);
|
||||
|
||||
services.AddHostedMqttServer();
|
||||
var serverOptionsBuilder = new MqttServerOptionsBuilder();
|
||||
|
||||
return services;
|
||||
}
|
||||
configure?.Invoke(serverOptionsBuilder);
|
||||
|
||||
public static IServiceCollection AddHostedMqttServerWithServices(this IServiceCollection services, Action<AspNetMqttServerOptionsBuilder> configure)
|
||||
{
|
||||
services.AddSingleton<IMqttServerOptions>(s => {
|
||||
var options = serverOptionsBuilder.Build();
|
||||
|
||||
return AddHostedMqttServer(services, options);
|
||||
}
|
||||
|
||||
public static void AddHostedMqttServer(this IServiceCollection services)
|
||||
{
|
||||
// The user may have these services already registered.
|
||||
services.TryAddSingleton<IMqttNetLogger>(MqttNetNullLogger.Instance);
|
||||
services.TryAddSingleton(new MqttServerFactory());
|
||||
|
||||
services.AddSingleton<MqttHostedServer>();
|
||||
services.AddSingleton<IHostedService>(s => s.GetService<MqttHostedServer>());
|
||||
services.AddSingleton<MqttServer>(s => s.GetService<MqttHostedServer>());
|
||||
}
|
||||
|
||||
public static IServiceCollection AddHostedMqttServerWithServices(this IServiceCollection services, Action<AspNetMqttServerOptionsBuilder> configure)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(services);
|
||||
|
||||
services.AddSingleton(
|
||||
s =>
|
||||
{
|
||||
var builder = new AspNetMqttServerOptionsBuilder(s);
|
||||
configure(builder);
|
||||
return builder.Build();
|
||||
});
|
||||
|
||||
services.AddHostedMqttServer();
|
||||
services.AddHostedMqttServer();
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
public static IServiceCollection AddHostedMqttServer<TOptions>(this IServiceCollection services)
|
||||
where TOptions : class, IMqttServerOptions
|
||||
{
|
||||
services.AddSingleton<IMqttServerOptions, TOptions>();
|
||||
|
||||
services.AddHostedMqttServer();
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
private static IServiceCollection AddHostedMqttServer(this IServiceCollection services)
|
||||
{
|
||||
var logger = new MqttNetLogger();
|
||||
var childLogger = logger.CreateChildLogger();
|
||||
|
||||
services.AddSingleton<IMqttNetLogger>(logger);
|
||||
services.AddSingleton(childLogger);
|
||||
services.AddSingleton<MqttHostedServer>();
|
||||
services.AddSingleton<IHostedService>(s => s.GetService<MqttHostedServer>());
|
||||
services.AddSingleton<IMqttServer>(s => s.GetService<MqttHostedServer>());
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
public static IServiceCollection AddMqttWebSocketServerAdapter(this IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton<MqttWebSocketServerAdapter>();
|
||||
services.AddSingleton<IMqttServerAdapter>(s => s.GetService<MqttWebSocketServerAdapter>());
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
public static IServiceCollection AddMqttTcpServerAdapter(this IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton<MqttTcpServerAdapter>();
|
||||
services.AddSingleton<IMqttServerAdapter>(s => s.GetService<MqttTcpServerAdapter>());
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
public static IServiceCollection AddMqttConnectionHandler(this IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton<MqttConnectionHandler>();
|
||||
services.AddSingleton<IMqttServerAdapter>(s => s.GetService<MqttConnectionHandler>());
|
||||
|
||||
return services;
|
||||
}
|
||||
return services;
|
||||
}
|
||||
}
|
||||
|
||||
public static IServiceCollection AddMqttConnectionHandler(this IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton<MqttConnectionHandler>();
|
||||
services.AddSingleton<IMqttServerAdapter>(s => s.GetService<MqttConnectionHandler>());
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
public static void AddMqttLogger(this IServiceCollection services, IMqttNetLogger logger)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(services);
|
||||
|
||||
services.AddSingleton(logger);
|
||||
}
|
||||
|
||||
public static IServiceCollection AddMqttServer(this IServiceCollection serviceCollection, Action<MqttServerOptionsBuilder> configure = null)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(serviceCollection);
|
||||
|
||||
serviceCollection.AddMqttConnectionHandler();
|
||||
serviceCollection.AddHostedMqttServer(configure);
|
||||
|
||||
return serviceCollection;
|
||||
}
|
||||
|
||||
public static IServiceCollection AddMqttTcpServerAdapter(this IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton<MqttTcpServerAdapter>();
|
||||
services.AddSingleton<IMqttServerAdapter>(s => s.GetService<MqttTcpServerAdapter>());
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
public static IServiceCollection AddMqttWebSocketServerAdapter(this IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton<MqttWebSocketServerAdapter>();
|
||||
services.AddSingleton<IMqttServerAdapter>(s => s.GetService<MqttWebSocketServerAdapter>());
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO.Pipelines;
|
||||
using System.Net.Sockets;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MQTTnet.AspNetCore;
|
||||
|
||||
public class SocketAwaitable : ICriticalNotifyCompletion
|
||||
{
|
||||
static readonly Action _callbackCompleted = () =>
|
||||
{
|
||||
};
|
||||
|
||||
readonly PipeScheduler _ioScheduler;
|
||||
int _bytesTransferred;
|
||||
|
||||
Action _callback;
|
||||
SocketError _error;
|
||||
|
||||
public SocketAwaitable(PipeScheduler ioScheduler)
|
||||
{
|
||||
_ioScheduler = ioScheduler;
|
||||
}
|
||||
|
||||
public bool IsCompleted => ReferenceEquals(_callback, _callbackCompleted);
|
||||
|
||||
public void Complete(int bytesTransferred, SocketError socketError)
|
||||
{
|
||||
_error = socketError;
|
||||
_bytesTransferred = bytesTransferred;
|
||||
var continuation = Interlocked.Exchange(ref _callback, _callbackCompleted);
|
||||
|
||||
if (continuation != null)
|
||||
{
|
||||
_ioScheduler.Schedule(state => ((Action)state)(), continuation);
|
||||
}
|
||||
}
|
||||
|
||||
public SocketAwaitable GetAwaiter()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
public int GetResult()
|
||||
{
|
||||
Debug.Assert(ReferenceEquals(_callback, _callbackCompleted));
|
||||
|
||||
_callback = null;
|
||||
|
||||
if (_error != SocketError.Success)
|
||||
{
|
||||
throw new SocketException((int)_error);
|
||||
}
|
||||
|
||||
return _bytesTransferred;
|
||||
}
|
||||
|
||||
public void OnCompleted(Action continuation)
|
||||
{
|
||||
if (ReferenceEquals(_callback, _callbackCompleted) || ReferenceEquals(Interlocked.CompareExchange(ref _callback, continuation, null), _callbackCompleted))
|
||||
{
|
||||
Task.Run(continuation);
|
||||
}
|
||||
}
|
||||
|
||||
public void UnsafeOnCompleted(Action continuation)
|
||||
{
|
||||
OnCompleted(continuation);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,261 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Pipelines;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Connections;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using MQTTnet.Exceptions;
|
||||
|
||||
namespace MQTTnet.AspNetCore;
|
||||
|
||||
public sealed class SocketConnection : ConnectionContext
|
||||
{
|
||||
readonly EndPoint _endPoint;
|
||||
volatile bool _aborted;
|
||||
IDuplexPipe _application;
|
||||
SocketReceiver _receiver;
|
||||
SocketSender _sender;
|
||||
|
||||
Socket _socket;
|
||||
|
||||
public SocketConnection(EndPoint endPoint)
|
||||
{
|
||||
_endPoint = endPoint;
|
||||
}
|
||||
|
||||
public SocketConnection(Socket socket)
|
||||
{
|
||||
_socket = socket;
|
||||
_endPoint = socket.RemoteEndPoint;
|
||||
|
||||
_sender = new SocketSender(_socket, PipeScheduler.ThreadPool);
|
||||
_receiver = new SocketReceiver(_socket, PipeScheduler.ThreadPool);
|
||||
}
|
||||
|
||||
public override string ConnectionId { get; set; }
|
||||
public override IFeatureCollection Features { get; }
|
||||
|
||||
public bool IsConnected { get; private set; }
|
||||
public override IDictionary<object, object> Items { get; set; }
|
||||
public override IDuplexPipe Transport { get; set; }
|
||||
|
||||
public override ValueTask DisposeAsync()
|
||||
{
|
||||
IsConnected = false;
|
||||
|
||||
Transport?.Output.Complete();
|
||||
Transport?.Input.Complete();
|
||||
|
||||
_socket?.Dispose();
|
||||
|
||||
return base.DisposeAsync();
|
||||
}
|
||||
|
||||
public async Task StartAsync()
|
||||
{
|
||||
if (_socket == null)
|
||||
{
|
||||
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
_sender = new SocketSender(_socket, PipeScheduler.ThreadPool);
|
||||
_receiver = new SocketReceiver(_socket, PipeScheduler.ThreadPool);
|
||||
await _socket.ConnectAsync(_endPoint);
|
||||
}
|
||||
|
||||
var pair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default);
|
||||
|
||||
Transport = pair.Transport;
|
||||
_application = pair.Application;
|
||||
|
||||
_ = ExecuteAsync();
|
||||
|
||||
IsConnected = true;
|
||||
}
|
||||
|
||||
Exception ConnectionAborted()
|
||||
{
|
||||
return new MqttCommunicationException("Connection Aborted");
|
||||
}
|
||||
|
||||
async Task DoReceive()
|
||||
{
|
||||
Exception error = null;
|
||||
|
||||
try
|
||||
{
|
||||
await ProcessReceives();
|
||||
}
|
||||
catch (SocketException ex) when (ex.SocketErrorCode == SocketError.ConnectionReset)
|
||||
{
|
||||
error = new MqttCommunicationException(ex);
|
||||
}
|
||||
catch (SocketException ex) when (ex.SocketErrorCode == SocketError.OperationAborted || ex.SocketErrorCode == SocketError.ConnectionAborted ||
|
||||
ex.SocketErrorCode == SocketError.Interrupted || ex.SocketErrorCode == SocketError.InvalidArgument)
|
||||
{
|
||||
if (!_aborted)
|
||||
{
|
||||
// Calling Dispose after ReceiveAsync can cause an "InvalidArgument" error on *nix.
|
||||
error = ConnectionAborted();
|
||||
}
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
if (!_aborted)
|
||||
{
|
||||
error = ConnectionAborted();
|
||||
}
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
error = ex;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
error = new IOException(ex.Message, ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (_aborted)
|
||||
{
|
||||
error = error ?? ConnectionAborted();
|
||||
}
|
||||
|
||||
_application.Output.Complete(error);
|
||||
}
|
||||
}
|
||||
|
||||
async Task<Exception> DoSend()
|
||||
{
|
||||
Exception error = null;
|
||||
|
||||
try
|
||||
{
|
||||
await ProcessSends();
|
||||
}
|
||||
catch (SocketException ex) when (ex.SocketErrorCode == SocketError.OperationAborted)
|
||||
{
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
error = ex;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
error = new IOException(ex.Message, ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_aborted = true;
|
||||
_socket.Shutdown(SocketShutdown.Both);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
async Task ExecuteAsync()
|
||||
{
|
||||
Exception sendError = null;
|
||||
try
|
||||
{
|
||||
// Spawn send and receive logic
|
||||
var receiveTask = DoReceive();
|
||||
var sendTask = DoSend();
|
||||
|
||||
// If the sending task completes then close the receive
|
||||
// We don't need to do this in the other direction because the kestrel
|
||||
// will trigger the output closing once the input is complete.
|
||||
if (await Task.WhenAny(receiveTask, sendTask).ConfigureAwait(false) == sendTask)
|
||||
{
|
||||
// Tell the reader it's being aborted
|
||||
_socket.Dispose();
|
||||
}
|
||||
|
||||
// Now wait for both to complete
|
||||
await receiveTask;
|
||||
sendError = await sendTask;
|
||||
|
||||
// Dispose the socket(should noop if already called)
|
||||
_socket.Dispose();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Unexpected exception in {nameof(SocketConnection)}.{nameof(StartAsync)}: " + ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Complete the output after disposing the socket
|
||||
await _application.Input.CompleteAsync(sendError).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
async Task ProcessReceives()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
// Ensure we have some reasonable amount of buffer space
|
||||
var buffer = _application.Output.GetMemory();
|
||||
|
||||
var bytesReceived = await _receiver.ReceiveAsync(buffer);
|
||||
|
||||
if (bytesReceived == 0)
|
||||
{
|
||||
// FIN
|
||||
break;
|
||||
}
|
||||
|
||||
_application.Output.Advance(bytesReceived);
|
||||
|
||||
var flushTask = _application.Output.FlushAsync();
|
||||
|
||||
if (!flushTask.IsCompleted)
|
||||
{
|
||||
await flushTask;
|
||||
}
|
||||
|
||||
var result = flushTask.GetAwaiter().GetResult();
|
||||
if (result.IsCompleted)
|
||||
{
|
||||
// Pipe consumer is shut down, do we stop writing
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async Task ProcessSends()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
// Wait for data to write from the pipe producer
|
||||
var result = await _application.Input.ReadAsync();
|
||||
var buffer = result.Buffer;
|
||||
|
||||
if (result.IsCanceled)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
var end = buffer.End;
|
||||
var isCompleted = result.IsCompleted;
|
||||
if (!buffer.IsEmpty)
|
||||
{
|
||||
await _sender.SendAsync(buffer);
|
||||
}
|
||||
|
||||
_application.Input.AdvanceTo(end);
|
||||
|
||||
if (isCompleted)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.IO.Pipelines;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace MQTTnet.AspNetCore;
|
||||
|
||||
public sealed class SocketReceiver
|
||||
{
|
||||
readonly SocketAwaitable _awaitable;
|
||||
readonly SocketAsyncEventArgs _eventArgs = new();
|
||||
readonly Socket _socket;
|
||||
|
||||
public SocketReceiver(Socket socket, PipeScheduler scheduler)
|
||||
{
|
||||
_socket = socket;
|
||||
_awaitable = new SocketAwaitable(scheduler);
|
||||
_eventArgs.UserToken = _awaitable;
|
||||
_eventArgs.Completed += (_, e) => ((SocketAwaitable)e.UserToken).Complete(e.BytesTransferred, e.SocketError);
|
||||
}
|
||||
|
||||
public SocketAwaitable ReceiveAsync(Memory<byte> buffer)
|
||||
{
|
||||
_eventArgs.SetBuffer(buffer);
|
||||
|
||||
if (!_socket.ReceiveAsync(_eventArgs))
|
||||
{
|
||||
_awaitable.Complete(_eventArgs.BytesTransferred, _eventArgs.SocketError);
|
||||
}
|
||||
|
||||
return _awaitable;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO.Pipelines;
|
||||
using System.Net.Sockets;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace MQTTnet.AspNetCore;
|
||||
|
||||
public sealed class SocketSender
|
||||
{
|
||||
readonly SocketAwaitable _awaitable;
|
||||
readonly SocketAsyncEventArgs _eventArgs = new();
|
||||
readonly Socket _socket;
|
||||
|
||||
List<ArraySegment<byte>> _bufferList;
|
||||
|
||||
public SocketSender(Socket socket, PipeScheduler scheduler)
|
||||
{
|
||||
_socket = socket;
|
||||
_awaitable = new SocketAwaitable(scheduler);
|
||||
_eventArgs.UserToken = _awaitable;
|
||||
_eventArgs.Completed += (_, e) => ((SocketAwaitable)e.UserToken).Complete(e.BytesTransferred, e.SocketError);
|
||||
}
|
||||
|
||||
public SocketAwaitable SendAsync(in ReadOnlySequence<byte> buffers)
|
||||
{
|
||||
if (buffers.IsSingleSegment)
|
||||
{
|
||||
return SendAsync(buffers.First);
|
||||
}
|
||||
|
||||
if (!_eventArgs.MemoryBuffer.Equals(Memory<byte>.Empty))
|
||||
{
|
||||
_eventArgs.SetBuffer(null, 0, 0);
|
||||
}
|
||||
|
||||
_eventArgs.BufferList = GetBufferList(buffers);
|
||||
|
||||
if (!_socket.SendAsync(_eventArgs))
|
||||
{
|
||||
_awaitable.Complete(_eventArgs.BytesTransferred, _eventArgs.SocketError);
|
||||
}
|
||||
|
||||
return _awaitable;
|
||||
}
|
||||
|
||||
List<ArraySegment<byte>> GetBufferList(in ReadOnlySequence<byte> buffer)
|
||||
{
|
||||
Debug.Assert(!buffer.IsEmpty);
|
||||
Debug.Assert(!buffer.IsSingleSegment);
|
||||
|
||||
if (_bufferList == null)
|
||||
{
|
||||
_bufferList = new List<ArraySegment<byte>>();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Buffers are pooled, so it's OK to root them until the next multi-buffer write.
|
||||
_bufferList.Clear();
|
||||
}
|
||||
|
||||
foreach (var b in buffer)
|
||||
{
|
||||
_bufferList.Add(b.GetArray());
|
||||
}
|
||||
|
||||
return _bufferList;
|
||||
}
|
||||
|
||||
SocketAwaitable SendAsync(ReadOnlyMemory<byte> memory)
|
||||
{
|
||||
// The BufferList getter is much less expensive then the setter.
|
||||
if (_eventArgs.BufferList != null)
|
||||
{
|
||||
_eventArgs.BufferList = null;
|
||||
}
|
||||
|
||||
_eventArgs.SetBuffer(MemoryMarshal.AsMemory(memory));
|
||||
|
||||
if (!_socket.SendAsync(_eventArgs))
|
||||
{
|
||||
_awaitable.Complete(_eventArgs.BytesTransferred, _eventArgs.SocketError);
|
||||
}
|
||||
|
||||
return _awaitable;
|
||||
}
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Formatter;
|
||||
using System;
|
||||
using System.Buffers.Binary;
|
||||
using System.Text;
|
||||
|
||||
namespace MQTTnet.AspNetCore
|
||||
{
|
||||
public class SpanBasedMqttPacketBodyReader : IMqttPacketBodyReader
|
||||
{
|
||||
private ReadOnlyMemory<byte> _buffer;
|
||||
|
||||
private int _offset;
|
||||
|
||||
public void SetBuffer(ReadOnlyMemory<byte> buffer)
|
||||
{
|
||||
_buffer = buffer;
|
||||
_offset = 0;
|
||||
}
|
||||
|
||||
public int Length => _buffer.Length;
|
||||
|
||||
public bool EndOfStream => _buffer.Length.Equals(_offset);
|
||||
|
||||
public int Offset => _offset;
|
||||
|
||||
public byte ReadByte()
|
||||
{
|
||||
return _buffer.Span[_offset++];
|
||||
}
|
||||
|
||||
public byte[] ReadRemainingData()
|
||||
{
|
||||
return _buffer.Slice(_offset).ToArray();
|
||||
}
|
||||
|
||||
public byte[] ReadWithLengthPrefix()
|
||||
{
|
||||
return ReadSegmentWithLengthPrefix().ToArray();
|
||||
}
|
||||
|
||||
private ReadOnlySpan<byte> ReadSegmentWithLengthPrefix()
|
||||
{
|
||||
var span = _buffer.Span;
|
||||
var length = BinaryPrimitives.ReadUInt16BigEndian(span.Slice(_offset));
|
||||
|
||||
var result = span.Slice(_offset+2, length);
|
||||
_offset += 2 + length;
|
||||
return result;
|
||||
}
|
||||
|
||||
public unsafe string ReadStringWithLengthPrefix()
|
||||
{
|
||||
var buffer = ReadSegmentWithLengthPrefix();
|
||||
if (buffer.Length == 0)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
fixed (byte* bytes = &buffer.GetPinnableReference())
|
||||
{
|
||||
var result = Encoding.UTF8.GetString(bytes, buffer.Length);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public ushort ReadTwoByteInteger()
|
||||
{
|
||||
var result = BinaryPrimitives.ReadUInt16BigEndian(_buffer.Span.Slice(_offset));
|
||||
_offset += 2;
|
||||
return result;
|
||||
}
|
||||
|
||||
public uint ReadFourByteInteger()
|
||||
{
|
||||
var result = BinaryPrimitives.ReadUInt32BigEndian(_buffer.Span.Slice(_offset));
|
||||
_offset += 4;
|
||||
return result;
|
||||
}
|
||||
|
||||
public uint ReadVariableLengthInteger()
|
||||
{
|
||||
var multiplier = 1;
|
||||
var value = 0U;
|
||||
byte encodedByte;
|
||||
|
||||
do
|
||||
{
|
||||
encodedByte = ReadByte();
|
||||
value += (uint)((encodedByte & 127) * multiplier);
|
||||
|
||||
if (multiplier > 2097152)
|
||||
{
|
||||
throw new MqttProtocolViolationException("Variable length integer is invalid.");
|
||||
}
|
||||
|
||||
multiplier *= 128;
|
||||
} while ((encodedByte & 128) != 0);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public bool ReadBoolean()
|
||||
{
|
||||
var buffer = ReadByte();
|
||||
|
||||
if (buffer == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (buffer == 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
throw new MqttProtocolViolationException("Boolean values can be 0 or 1 only.");
|
||||
}
|
||||
|
||||
public void Seek(int position)
|
||||
{
|
||||
_offset = position;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,146 +0,0 @@
|
||||
using MQTTnet.Formatter;
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Buffers.Binary;
|
||||
using System.Text;
|
||||
|
||||
namespace MQTTnet.AspNetCore
|
||||
{
|
||||
public class SpanBasedMqttPacketWriter : IMqttPacketWriter
|
||||
{
|
||||
private readonly ArrayPool<byte> _pool;
|
||||
|
||||
public SpanBasedMqttPacketWriter()
|
||||
{
|
||||
_pool = ArrayPool<byte>.Create();
|
||||
|
||||
}
|
||||
|
||||
private byte[] _buffer;
|
||||
private int _position;
|
||||
|
||||
public int Length { get; set; }
|
||||
|
||||
public void FreeBuffer()
|
||||
{
|
||||
_pool.Return(_buffer);
|
||||
}
|
||||
|
||||
public byte[] GetBuffer()
|
||||
{
|
||||
return _buffer;
|
||||
}
|
||||
|
||||
public void Reset(int v)
|
||||
{
|
||||
_buffer = _pool.Rent(1500);
|
||||
Length = v;
|
||||
_position = v;
|
||||
}
|
||||
|
||||
public void Seek(int v)
|
||||
{
|
||||
_position = v;
|
||||
}
|
||||
|
||||
public void Write(byte value)
|
||||
{
|
||||
GrowIfNeeded(1);
|
||||
_buffer[_position] = value;
|
||||
Commit(1);
|
||||
}
|
||||
|
||||
public void Write(ushort value)
|
||||
{
|
||||
GrowIfNeeded(2);
|
||||
|
||||
BinaryPrimitives.WriteUInt16BigEndian(_buffer.AsSpan(_position), value);
|
||||
Commit(2);
|
||||
}
|
||||
|
||||
public void Write(IMqttPacketWriter propertyWriter)
|
||||
{
|
||||
if (propertyWriter == null) throw new ArgumentNullException(nameof(propertyWriter));
|
||||
|
||||
GrowIfNeeded(propertyWriter.Length);
|
||||
Write(propertyWriter.GetBuffer(), 0, propertyWriter.Length);
|
||||
Commit(propertyWriter.Length);
|
||||
}
|
||||
|
||||
public void Write(byte[] payload, int start, int length)
|
||||
{
|
||||
GrowIfNeeded(length);
|
||||
|
||||
payload.AsSpan(start, length).CopyTo(_buffer.AsSpan(_position));
|
||||
Commit(length);
|
||||
}
|
||||
|
||||
public void WriteVariableLengthInteger(uint value)
|
||||
{
|
||||
GrowIfNeeded(4);
|
||||
|
||||
var x = value;
|
||||
do
|
||||
{
|
||||
var encodedByte = x % 128;
|
||||
x = x / 128;
|
||||
if (x > 0)
|
||||
{
|
||||
encodedByte = encodedByte | 128;
|
||||
}
|
||||
|
||||
_buffer[_position] = (byte)encodedByte;
|
||||
Commit(1);
|
||||
} while (x > 0);
|
||||
}
|
||||
|
||||
public void WriteWithLengthPrefix(string value)
|
||||
{
|
||||
var bytesLength = Encoding.UTF8.GetByteCount(value ?? string.Empty);
|
||||
GrowIfNeeded(bytesLength + 2);
|
||||
|
||||
Write((ushort)bytesLength);
|
||||
Encoding.UTF8.GetBytes(value ?? string.Empty, 0, value?.Length ?? 0, _buffer, _position);
|
||||
Commit(bytesLength);
|
||||
}
|
||||
|
||||
public void WriteWithLengthPrefix(byte[] payload)
|
||||
{
|
||||
GrowIfNeeded(payload.Length + 2);
|
||||
|
||||
Write((ushort)payload.Length);
|
||||
payload.CopyTo(_buffer, _position);
|
||||
Commit(payload.Length);
|
||||
}
|
||||
|
||||
private void Commit(int count)
|
||||
{
|
||||
if (_position == Length)
|
||||
{
|
||||
Length += count;
|
||||
}
|
||||
|
||||
_position += count;
|
||||
}
|
||||
|
||||
private void GrowIfNeeded(int requiredAdditional)
|
||||
{
|
||||
var requiredTotal = _position + requiredAdditional;
|
||||
if (_buffer.Length >= requiredTotal)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var newBufferLength = _buffer.Length;
|
||||
while (newBufferLength < requiredTotal)
|
||||
{
|
||||
newBufferLength *= 2;
|
||||
}
|
||||
|
||||
var newBuffer = _pool.Rent(newBufferLength);
|
||||
Array.Copy(_buffer, newBuffer, _buffer.Length);
|
||||
_pool.Return(_buffer);
|
||||
_buffer = newBuffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Jobs;
|
||||
using MQTTnet.Internal;
|
||||
|
||||
namespace MQTTnet.Benchmarks
|
||||
{
|
||||
[SimpleJob(RuntimeMoniker.Net60)]
|
||||
[MemoryDiagnoser]
|
||||
public class AsyncLockBenchmark : BaseBenchmark
|
||||
{
|
||||
[Benchmark]
|
||||
public async Task Synchronize_100_Tasks()
|
||||
{
|
||||
const int tasksCount = 100;
|
||||
|
||||
var tasks = new Task[tasksCount];
|
||||
var asyncLock = new AsyncLock();
|
||||
var globalI = 0;
|
||||
|
||||
for (var i = 0; i < tasksCount; i++)
|
||||
{
|
||||
tasks[i] = Task.Run(
|
||||
async () =>
|
||||
{
|
||||
using (await asyncLock.EnterAsync().ConfigureAwait(false))
|
||||
{
|
||||
var localI = globalI;
|
||||
await Task.Delay(5).ConfigureAwait(false); // Increase the chance for wrong data.
|
||||
localI++;
|
||||
globalI = localI;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
await Task.WhenAll(tasks).ConfigureAwait(false);
|
||||
|
||||
if (globalI != tasksCount)
|
||||
{
|
||||
throw new Exception($"Code is broken ({globalI})!");
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public async Task Wait_100_000_Times()
|
||||
{
|
||||
var asyncLock = new AsyncLock();
|
||||
|
||||
using (var cancellationToken = new CancellationTokenSource())
|
||||
{
|
||||
for (var i = 0; i < 100000; i++)
|
||||
{
|
||||
using (await asyncLock.EnterAsync(cancellationToken.Token).ConfigureAwait(false))
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace MQTTnet.Benchmarks
|
||||
{
|
||||
public abstract class BaseBenchmark
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,50 +1,26 @@
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.Internal;
|
||||
using MQTTnet.Packets;
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.Diagnostics.Logger;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Tests.Mockups;
|
||||
|
||||
namespace MQTTnet.Benchmarks
|
||||
{
|
||||
[MemoryDiagnoser]
|
||||
public class ChannelAdapterBenchmark
|
||||
public sealed class ChannelAdapterBenchmark : BaseBenchmark
|
||||
{
|
||||
private MqttChannelAdapter _channelAdapter;
|
||||
private int _iterations;
|
||||
private MemoryStream _stream;
|
||||
private MqttPublishPacket _packet;
|
||||
|
||||
[GlobalSetup]
|
||||
public void Setup()
|
||||
{
|
||||
_packet = new MqttPublishPacket
|
||||
{
|
||||
Topic = "A"
|
||||
};
|
||||
|
||||
var serializer = new MqttPacketFormatterAdapter(MqttProtocolVersion.V311);
|
||||
|
||||
var serializedPacket = Join(serializer.Encode(_packet));
|
||||
|
||||
_iterations = 10000;
|
||||
|
||||
_stream = new MemoryStream(_iterations * serializedPacket.Length);
|
||||
|
||||
for (var i = 0; i < _iterations; i++)
|
||||
{
|
||||
_stream.Write(serializedPacket, 0, serializedPacket.Length);
|
||||
}
|
||||
|
||||
_stream.Position = 0;
|
||||
|
||||
var channel = new TestMqttChannel(_stream);
|
||||
|
||||
_channelAdapter = new MqttChannelAdapter(channel, serializer, new MqttNetLogger().CreateChildLogger(nameof(MqttChannelAdapter)));
|
||||
}
|
||||
MqttChannelAdapter _channelAdapter;
|
||||
int _iterations;
|
||||
MqttPublishPacket _packet;
|
||||
MemoryStream _stream;
|
||||
|
||||
[Benchmark]
|
||||
public void Receive_10000_Messages()
|
||||
@@ -53,7 +29,7 @@ namespace MQTTnet.Benchmarks
|
||||
|
||||
for (var i = 0; i < 10000; i++)
|
||||
{
|
||||
_channelAdapter.ReceivePacketAsync(TimeSpan.Zero, CancellationToken.None).GetAwaiter().GetResult();
|
||||
_channelAdapter.ReceivePacketAsync(CancellationToken.None).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
_stream.Position = 0;
|
||||
@@ -66,13 +42,41 @@ namespace MQTTnet.Benchmarks
|
||||
|
||||
for (var i = 0; i < 10000; i++)
|
||||
{
|
||||
_channelAdapter.SendPacketAsync(_packet, TimeSpan.Zero, CancellationToken.None).GetAwaiter().GetResult();
|
||||
_channelAdapter.SendPacketAsync(_packet, CancellationToken.None).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
_stream.Position = 0;
|
||||
}
|
||||
|
||||
private static byte[] Join(params ArraySegment<byte>[] chunks)
|
||||
[GlobalSetup]
|
||||
public void Setup()
|
||||
{
|
||||
_packet = new MqttPublishPacket
|
||||
{
|
||||
Topic = "A"
|
||||
};
|
||||
|
||||
var serializer = new MqttPacketFormatterAdapter(MqttProtocolVersion.V311, new MqttBufferWriter(4096, 65535));
|
||||
|
||||
var serializedPacket = Join(serializer.Encode(_packet).Join());
|
||||
|
||||
_iterations = 10000;
|
||||
|
||||
_stream = new MemoryStream(_iterations * serializedPacket.Length);
|
||||
|
||||
for (var i = 0; i < _iterations; i++)
|
||||
{
|
||||
_stream.Write(serializedPacket, 0, serializedPacket.Length);
|
||||
}
|
||||
|
||||
_stream.Position = 0;
|
||||
|
||||
var channel = new MemoryMqttChannel(_stream);
|
||||
|
||||
_channelAdapter = new MqttChannelAdapter(channel, serializer, new MqttNetEventLogger());
|
||||
}
|
||||
|
||||
static byte[] Join(params ArraySegment<byte>[] chunks)
|
||||
{
|
||||
var buffer = new MemoryStream();
|
||||
foreach (var chunk in chunks)
|
||||
@@ -83,4 +87,4 @@ namespace MQTTnet.Benchmarks
|
||||
return buffer.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Jobs;
|
||||
using MQTTnet.Diagnostics.Logger;
|
||||
|
||||
namespace MQTTnet.Benchmarks
|
||||
{
|
||||
[SimpleJob(RuntimeMoniker.Net60)]
|
||||
[RPlotExporter]
|
||||
[MemoryDiagnoser]
|
||||
public class LoggerBenchmark : BaseBenchmark
|
||||
{
|
||||
MqttNetNullLogger _nullLogger;
|
||||
MqttNetSourceLogger _sourceNullLogger;
|
||||
|
||||
MqttNetEventLogger _eventLogger;
|
||||
MqttNetSourceLogger _sourceEventLogger;
|
||||
|
||||
MqttNetEventLogger _eventLoggerNoListener;
|
||||
MqttNetSourceLogger _sourceEventLoggerNoListener;
|
||||
|
||||
bool _useHandler;
|
||||
|
||||
[GlobalSetup]
|
||||
public void Setup()
|
||||
{
|
||||
_nullLogger = new MqttNetNullLogger();
|
||||
_sourceNullLogger = _nullLogger.WithSource("Source");
|
||||
|
||||
_eventLogger = new MqttNetEventLogger();
|
||||
_eventLogger.LogMessagePublished += OnLogMessagePublished;
|
||||
_sourceEventLogger = _eventLogger.WithSource("Source");
|
||||
|
||||
_eventLoggerNoListener = new MqttNetEventLogger();
|
||||
_sourceEventLoggerNoListener = _eventLoggerNoListener.WithSource("Source");
|
||||
}
|
||||
|
||||
void OnLogMessagePublished(object sender, MqttNetLogMessagePublishedEventArgs eventArgs)
|
||||
{
|
||||
if (_useHandler)
|
||||
{
|
||||
eventArgs.LogMessage.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void Log_10000_Messages_No_Listener()
|
||||
{
|
||||
_useHandler = false;
|
||||
|
||||
for (var i = 0; i < 10000; i++)
|
||||
{
|
||||
_sourceEventLoggerNoListener.Verbose("test log message {0}", "parameter");
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void Log_10000_Messages_With_To_String()
|
||||
{
|
||||
_useHandler = true;
|
||||
|
||||
for (var i = 0; i < 10000; i++)
|
||||
{
|
||||
_sourceEventLogger.Verbose("test log message {0}", "parameter");
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void Log_10000_Messages_Without_To_String()
|
||||
{
|
||||
_useHandler = false;
|
||||
|
||||
for (var i = 0; i < 10000; i++)
|
||||
{
|
||||
_sourceEventLogger.Verbose("test log message {0}", "parameter");
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void Log_10000_Messages_With_NullLogger()
|
||||
{
|
||||
for (var i = 0; i < 10000; i++)
|
||||
{
|
||||
_sourceNullLogger.Verbose("test log message {0}", "parameter");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<OutputType>Exe</OutputType>
|
||||
<DebugType>Full</DebugType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<IsPackable>false</IsPackable>
|
||||
<EnableNETAnalyzers>false</EnableNETAnalyzers>
|
||||
<EnforceCodeStyleInBuild>false</EnforceCodeStyleInBuild>
|
||||
<DisableImplicitAspNetCoreAnalyzers>true</DisableImplicitAspNetCoreAnalyzers>
|
||||
<NoWarn>1591;NETSDK1138;NU1803;NU1901;NU1902;CS8892</NoWarn>
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
<NuGetAuditMode>all</NuGetAuditMode>
|
||||
<NuGetAudit>true</NuGetAudit>
|
||||
<NuGetAuditLevel>low</NuGetAuditLevel>
|
||||
<AnalysisLevel>latest-Recommended</AnalysisLevel>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.14.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Source\MQTTnet.Server\MQTTnet.Server.csproj"/>
|
||||
<ProjectReference Include="..\..\Source\MQTTnet.AspnetCore\MQTTnet.AspNetCore.csproj"/>
|
||||
<ProjectReference Include="..\MQTTnet.Tests\MQTTnet.Tests.csproj"/>
|
||||
<ProjectReference Include="..\MQTTnet\MQTTnet.csproj"/>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,40 @@
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Jobs;
|
||||
using System;
|
||||
|
||||
|
||||
namespace MQTTnet.Benchmarks
|
||||
{
|
||||
[SimpleJob(RuntimeMoniker.Net60)]
|
||||
[RPlotExporter, RankColumn]
|
||||
[MemoryDiagnoser]
|
||||
public class MemoryCopyBenchmark
|
||||
{
|
||||
const int max_length = 1024 * 8;
|
||||
private byte[] source;
|
||||
private byte[] target;
|
||||
|
||||
[Params(64 - 1, 128 - 1, 256 - 1, 512 - 1, 1024 - 1, 2048 - 1, 5096 - 1)]
|
||||
public int Length { get; set; }
|
||||
|
||||
[GlobalSetup]
|
||||
public void Setup()
|
||||
{
|
||||
source = new byte[max_length];
|
||||
target = new byte[max_length];
|
||||
}
|
||||
|
||||
[Benchmark(Baseline = true)]
|
||||
public void Array_Copy()
|
||||
{
|
||||
Array.Copy(source, 0, target, 0, Length);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void Memory_Copy()
|
||||
{
|
||||
MQTTnet.Internal.MqttMemoryHelper.Copy(source, 0, target, 0, Length);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using MQTTnet.Packets;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.Benchmarks;
|
||||
|
||||
[MemoryDiagnoser]
|
||||
public class MessageDeliveryBenchmark : BaseBenchmark
|
||||
{
|
||||
List<string> _allSubscribedTopics; // Keep track of the subset of topics that are subscribed
|
||||
CancellationTokenSource _cancellationTokenSource;
|
||||
|
||||
object _lockMsgCount;
|
||||
int _messagesExpectedCount;
|
||||
int _messagesReceivedCount;
|
||||
Dictionary<string, IMqttClient> _mqttPublisherClientsByPublisherName;
|
||||
|
||||
MqttServer _mqttServer;
|
||||
List<IMqttClient> _mqttSubscriberClients;
|
||||
Dictionary<string, string> _publisherByTopic;
|
||||
List<MqttApplicationMessage> _topicPublishMessages;
|
||||
|
||||
Dictionary<string, List<string>> _topicsByPublisher;
|
||||
|
||||
[Params(1000, 10000)] public int NumPublishers;
|
||||
|
||||
[Params(5, 10, 20, 50)] public int NumSubscribedTopicsPerSubscriber;
|
||||
|
||||
[Params(10)] public int NumSubscribers;
|
||||
|
||||
[Params(1, 5)] public int NumTopicsPerPublisher;
|
||||
|
||||
[GlobalCleanup]
|
||||
public void Cleanup()
|
||||
{
|
||||
foreach (var mp in _mqttPublisherClientsByPublisherName)
|
||||
{
|
||||
var mqttPublisherClient = mp.Value;
|
||||
mqttPublisherClient.DisconnectAsync().GetAwaiter().GetResult();
|
||||
mqttPublisherClient.Dispose();
|
||||
}
|
||||
|
||||
_mqttPublisherClientsByPublisherName.Clear();
|
||||
|
||||
foreach (var mqttSubscriber in _mqttSubscriberClients)
|
||||
{
|
||||
mqttSubscriber.DisconnectAsync().GetAwaiter().GetResult();
|
||||
mqttSubscriber.Dispose();
|
||||
}
|
||||
|
||||
_mqttSubscriberClients.Clear();
|
||||
|
||||
_mqttServer.StopAsync().GetAwaiter().GetResult();
|
||||
_mqttServer.Dispose();
|
||||
_mqttServer = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Publish messages and wait for messages sent to subscribers
|
||||
/// </summary>
|
||||
[Benchmark]
|
||||
public void DeliverMessages()
|
||||
{
|
||||
// There should be one message received per publish for each subscribed topic
|
||||
_messagesExpectedCount = NumSubscribedTopicsPerSubscriber * NumSubscribers;
|
||||
|
||||
// Loop for a while and exchange messages
|
||||
|
||||
_messagesReceivedCount = 0;
|
||||
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
// same payload for all messages
|
||||
var payload = new byte[] { 1, 2, 3, 4 };
|
||||
|
||||
var tasks = new List<Task>();
|
||||
|
||||
// publish a message for each subscribed topic
|
||||
foreach (var topic in _allSubscribedTopics)
|
||||
{
|
||||
var message = new MqttApplicationMessageBuilder().WithTopic(topic).WithPayload(payload).Build();
|
||||
// pick the correct publisher
|
||||
var publisherName = _publisherByTopic[topic];
|
||||
var publisherClient = _mqttPublisherClientsByPublisherName[publisherName];
|
||||
_ = publisherClient.PublishAsync(message);
|
||||
}
|
||||
|
||||
// Wait one message per publish to be received by subscriber (in the subscriber's application message handler)
|
||||
try
|
||||
{
|
||||
Task.Delay(30000, _cancellationTokenSource.Token).GetAwaiter().GetResult();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
_cancellationTokenSource.Dispose();
|
||||
|
||||
if (_messagesReceivedCount < _messagesExpectedCount)
|
||||
{
|
||||
throw new Exception(string.Format("Messages Received Count mismatch, expected {0}, received {1}", _messagesExpectedCount, _messagesReceivedCount));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[GlobalSetup]
|
||||
public void Setup()
|
||||
{
|
||||
_lockMsgCount = new object();
|
||||
|
||||
Dictionary<string, List<string>> singleWildcardTopicsByPublisher;
|
||||
Dictionary<string, List<string>> multiWildcardTopicsByPublisher;
|
||||
|
||||
TopicGenerator.Generate(NumPublishers, NumTopicsPerPublisher, out _topicsByPublisher, out singleWildcardTopicsByPublisher, out multiWildcardTopicsByPublisher);
|
||||
|
||||
var topics = _topicsByPublisher.First().Value;
|
||||
_topicPublishMessages = new List<MqttApplicationMessage>();
|
||||
// Prepare messages, same for each publisher
|
||||
foreach (var topic in topics)
|
||||
{
|
||||
var message = new MqttApplicationMessageBuilder().WithTopic(topic).WithPayload(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 }).Build();
|
||||
_topicPublishMessages.Add(message);
|
||||
}
|
||||
|
||||
// Create server
|
||||
var serverFactory = new MqttServerFactory();
|
||||
var clientFactory = new MqttClientFactory();
|
||||
var serverOptions = new MqttServerOptionsBuilder().WithDefaultEndpoint().Build();
|
||||
_mqttServer = serverFactory.CreateMqttServer(serverOptions);
|
||||
_mqttServer.StartAsync().GetAwaiter().GetResult();
|
||||
|
||||
// Create publisher clients
|
||||
_mqttPublisherClientsByPublisherName = new Dictionary<string, IMqttClient>();
|
||||
foreach (var pt in _topicsByPublisher)
|
||||
{
|
||||
var publisherName = pt.Key;
|
||||
var mqttClient = clientFactory.CreateMqttClient();
|
||||
var publisherOptions = new MqttClientOptionsBuilder().WithTcpServer("localhost").WithClientId(publisherName).WithKeepAlivePeriod(TimeSpan.FromSeconds(30)).Build();
|
||||
mqttClient.ConnectAsync(publisherOptions).GetAwaiter().GetResult();
|
||||
_mqttPublisherClientsByPublisherName.Add(publisherName, mqttClient);
|
||||
}
|
||||
|
||||
// Create subscriber clients
|
||||
_mqttSubscriberClients = new List<IMqttClient>();
|
||||
for (var i = 0; i < NumSubscribers; i++)
|
||||
{
|
||||
var mqttSubscriberClient = clientFactory.CreateMqttClient();
|
||||
_mqttSubscriberClients.Add(mqttSubscriberClient);
|
||||
|
||||
var subscriberOptions = new MqttClientOptionsBuilder().WithTcpServer("localhost").WithClientId("subscriber" + i).Build();
|
||||
mqttSubscriberClient.ApplicationMessageReceivedAsync += r =>
|
||||
{
|
||||
// count messages and signal cancellation when expected message count is reached
|
||||
lock (_lockMsgCount)
|
||||
{
|
||||
++_messagesReceivedCount;
|
||||
if (_messagesReceivedCount == _messagesExpectedCount)
|
||||
{
|
||||
_cancellationTokenSource.Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
mqttSubscriberClient.ConnectAsync(subscriberOptions).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
|
||||
var allTopics = new List<string>();
|
||||
_publisherByTopic = new Dictionary<string, string>();
|
||||
foreach (var t in _topicsByPublisher)
|
||||
{
|
||||
foreach (var topic in t.Value)
|
||||
{
|
||||
_publisherByTopic.Add(topic, t.Key);
|
||||
allTopics.Add(topic);
|
||||
}
|
||||
}
|
||||
|
||||
// Subscribe to NumSubscribedTopics topics spread across all topics
|
||||
_allSubscribedTopics = new List<string>();
|
||||
|
||||
var totalNumTopics = NumPublishers * NumTopicsPerPublisher;
|
||||
var topicIndexStep = totalNumTopics / (NumSubscribedTopicsPerSubscriber * NumSubscribers);
|
||||
if (topicIndexStep * NumSubscribedTopicsPerSubscriber * NumSubscribers != totalNumTopics)
|
||||
{
|
||||
throw new Exception(
|
||||
string.Format(
|
||||
"The total number of topics must be divisible by the number of subscribed topics across all subscribers. Total number of topics: {0}, topic step: {1}",
|
||||
totalNumTopics,
|
||||
topicIndexStep));
|
||||
}
|
||||
|
||||
var topicIndex = 0;
|
||||
foreach (var mqttSubscriber in _mqttSubscriberClients)
|
||||
{
|
||||
for (var i = 0; i < NumSubscribedTopicsPerSubscriber; ++i, topicIndex += topicIndexStep)
|
||||
{
|
||||
var topic = allTopics[topicIndex];
|
||||
_allSubscribedTopics.Add(topic);
|
||||
var subOptions = new MqttClientSubscribeOptionsBuilder().WithTopicFilter(new MqttTopicFilter { Topic = topic }).Build();
|
||||
mqttSubscriber.SubscribeAsync(subOptions).GetAwaiter().GetResult();
|
||||
}
|
||||
}
|
||||
|
||||
Task.Delay(1000).GetAwaiter().GetResult();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Jobs;
|
||||
using MQTTnet.Server;
|
||||
|
||||
namespace MQTTnet.Benchmarks;
|
||||
|
||||
[SimpleJob(RuntimeMoniker.Net60)]
|
||||
[RPlotExporter]
|
||||
[RankColumn]
|
||||
[MemoryDiagnoser]
|
||||
public class MessageProcessingBenchmark : BaseBenchmark
|
||||
{
|
||||
MqttApplicationMessage _message;
|
||||
IMqttClient _mqttClient;
|
||||
MqttServer _mqttServer;
|
||||
|
||||
[Benchmark]
|
||||
public void Send_10000_Messages()
|
||||
{
|
||||
for (var i = 0; i < 10000; i++)
|
||||
{
|
||||
_mqttClient.PublishAsync(_message).GetAwaiter().GetResult();
|
||||
}
|
||||
}
|
||||
|
||||
[GlobalSetup]
|
||||
public void Setup()
|
||||
{
|
||||
var serverOptions = new MqttServerOptionsBuilder().Build();
|
||||
|
||||
var serverFactory = new MqttServerFactory();
|
||||
_mqttServer = serverFactory.CreateMqttServer(serverOptions);
|
||||
var clientFactory = new MqttClientFactory();
|
||||
_mqttClient = clientFactory.CreateMqttClient();
|
||||
|
||||
_mqttServer.StartAsync().GetAwaiter().GetResult();
|
||||
|
||||
var clientOptions = new MqttClientOptionsBuilder().WithTcpServer("localhost").Build();
|
||||
|
||||
_mqttClient.ConnectAsync(clientOptions).GetAwaiter().GetResult();
|
||||
|
||||
_message = new MqttApplicationMessageBuilder().WithTopic("A").Build();
|
||||
}
|
||||
}
|
||||
@@ -1,24 +1,23 @@
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using MQTTnet.Client;
|
||||
|
||||
|
||||
using MQTTnet.AspNetCore;
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Jobs;
|
||||
using Microsoft.AspNetCore;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using MQTTnet.Server;
|
||||
using MQTTnet.Diagnostics;
|
||||
using MQTTnet.AspNetCore.Client;
|
||||
using MQTTnet.Client.Options;
|
||||
using MQTTnet.AspNetCore;
|
||||
using MQTTnet.Diagnostics.Logger;
|
||||
|
||||
namespace MQTTnet.Benchmarks
|
||||
{
|
||||
[SimpleJob(RuntimeMoniker.Net60)]
|
||||
[MemoryDiagnoser]
|
||||
public class MessageProcessingMqttConnectionContextBenchmark
|
||||
public class MessageProcessingMqttConnectionContextBenchmark : BaseBenchmark
|
||||
{
|
||||
private IWebHost _host;
|
||||
private IMqttClient _mqttClient;
|
||||
private MqttApplicationMessage _message;
|
||||
IWebHost _host;
|
||||
IMqttClient _mqttClient;
|
||||
MqttApplicationMessage _message;
|
||||
|
||||
[GlobalSetup]
|
||||
public void Setup()
|
||||
@@ -26,11 +25,8 @@ namespace MQTTnet.Benchmarks
|
||||
_host = WebHost.CreateDefaultBuilder()
|
||||
.UseKestrel(o => o.ListenAnyIP(1883, l => l.UseMqtt()))
|
||||
.ConfigureServices(services => {
|
||||
var mqttServerOptions = new MqttServerOptionsBuilder()
|
||||
.WithoutDefaultEndpoint()
|
||||
.Build();
|
||||
services
|
||||
.AddHostedMqttServer(mqttServerOptions)
|
||||
.AddHostedMqttServer(mqttServerOptions => mqttServerOptions.WithoutDefaultEndpoint())
|
||||
.AddMqttConnectionHandler();
|
||||
})
|
||||
.Configure(app => {
|
||||
@@ -40,8 +36,8 @@ namespace MQTTnet.Benchmarks
|
||||
})
|
||||
.Build();
|
||||
|
||||
var factory = new MqttFactory();
|
||||
_mqttClient = factory.CreateMqttClient(new MqttNetLogger(), new MqttClientConnectionContextFactory());
|
||||
var factory = new MqttClientFactory();
|
||||
_mqttClient = factory.CreateMqttClient(new MqttNetEventLogger(), new MqttClientConnectionContextFactory());
|
||||
|
||||
_host.StartAsync().GetAwaiter().GetResult();
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Text;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Jobs;
|
||||
using MQTTnet.Formatter;
|
||||
|
||||
namespace MQTTnet.Benchmarks
|
||||
{
|
||||
[SimpleJob(RuntimeMoniker.Net60)]
|
||||
[MemoryDiagnoser]
|
||||
public class MqttBufferReaderBenchmark
|
||||
{
|
||||
byte[] _buffer;
|
||||
int _bufferLength;
|
||||
|
||||
[GlobalSetup]
|
||||
public void GlobalSetup()
|
||||
{
|
||||
var writer = new MqttBufferWriter(1024, 1024);
|
||||
writer.WriteString("hgfjkdfkjlghfdjghdfljkdfhgdlkjfshgsldkfjghsdflkjghdsflkjhrstiuoghlkfjbhnfbutghjoiöjhklötnbhtroliöuhbjntluiobkjzbhtdrlskbhtruhjkfthgbkftgjhgfiklhotriuöhbjtrsioöbtrsötrhträhtrühjtriüoätrhjtsrölbktrbnhtrulöbionhströloubinströoliubhnsöotrunbtöroisntröointröioujhgötiohjgötorshjnbgtöorihbnjtröoihbjntröobntröoibntrjhötrohjbtröoihntröoibnrtoiöbtrjnboöitrhjtnriohötrhjtöroihjtroöihjtroösibntsroönbotöirsbntöoihjntröoihntroöbtrboöitrnhoöitrhjntröoishbnjtröosbhtröbntriohjtröoijtöoitbjöotibjnhöotirhbjntroöibhnjrtoöibnhtroöibnhtörsbnhtöoirbnhtöroibntoörhjnbträöbtrbträbtrbtirbätrsibohjntrsöiobthnjiohjsrtoib");
|
||||
|
||||
_buffer = writer.GetBuffer();
|
||||
_bufferLength = writer.Length;
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void Use_Span()
|
||||
{
|
||||
var span = _buffer.AsSpan(0, _bufferLength);
|
||||
Encoding.UTF8.GetString(span);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void Use_Encoding()
|
||||
{
|
||||
Encoding.UTF8.GetString(_buffer, 0, _bufferLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Jobs;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Tests.Mockups;
|
||||
|
||||
namespace MQTTnet.Benchmarks
|
||||
{
|
||||
[SimpleJob(RuntimeMoniker.Net60)]
|
||||
[MemoryDiagnoser]
|
||||
public class MqttPacketReaderWriterBenchmark : BaseBenchmark
|
||||
{
|
||||
readonly byte[] _demoPayload = new byte[1024];
|
||||
|
||||
byte[] _readPayload;
|
||||
|
||||
[GlobalCleanup]
|
||||
public void GlobalCleanup()
|
||||
{
|
||||
}
|
||||
|
||||
[GlobalSetup]
|
||||
public void GlobalSetup()
|
||||
{
|
||||
TestEnvironment.EnableLogger = false;
|
||||
|
||||
var writer = new MqttBufferWriter(4096, 65535);
|
||||
writer.WriteString("A relative short string.");
|
||||
writer.WriteBinary(_demoPayload);
|
||||
writer.WriteByte(0x01);
|
||||
writer.WriteByte(0x02);
|
||||
writer.WriteVariableByteInteger(5647382);
|
||||
writer.WriteString("A relative short string.");
|
||||
writer.WriteVariableByteInteger(8574489);
|
||||
writer.WriteBinary(_demoPayload);
|
||||
writer.WriteByte(2);
|
||||
writer.WriteByte(0x02);
|
||||
writer.WriteString("fjgffiogfhgfhoihgoireghreghreguhreguireoghreouighreouighreughreguiorehreuiohruiorehreuioghreug");
|
||||
writer.WriteBinary(_demoPayload);
|
||||
|
||||
_readPayload = new ArraySegment<byte>(writer.GetBuffer(), 0, writer.Length).ToArray();
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void Read_100_000_Messages()
|
||||
{
|
||||
var reader = new MqttBufferReader();
|
||||
reader.SetBuffer(_readPayload, 0, _readPayload.Length);
|
||||
|
||||
for (var i = 0; i < 100000; i++)
|
||||
{
|
||||
reader.Seek(0);
|
||||
|
||||
reader.ReadString();
|
||||
reader.ReadBinaryData();
|
||||
reader.ReadByte();
|
||||
reader.ReadByte();
|
||||
reader.ReadVariableByteInteger();
|
||||
reader.ReadString();
|
||||
reader.ReadVariableByteInteger();
|
||||
reader.ReadBinaryData();
|
||||
reader.ReadByte();
|
||||
reader.ReadByte();
|
||||
reader.ReadString();
|
||||
reader.ReadBinaryData();
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void Write_100_000_Messages()
|
||||
{
|
||||
var writer = new MqttBufferWriter(4096, 65535);
|
||||
|
||||
for (var i = 0; i < 100000; i++)
|
||||
{
|
||||
writer.WriteString("A relative short string.");
|
||||
writer.WriteByte(0x01);
|
||||
writer.WriteByte(0x02);
|
||||
writer.WriteVariableByteInteger(5647382);
|
||||
writer.WriteString("A relative short string.");
|
||||
writer.WriteVariableByteInteger(8574589);
|
||||
writer.WriteBinary(_demoPayload);
|
||||
writer.WriteByte(2);
|
||||
writer.WriteByte(0x02);
|
||||
writer.WriteString("fjgffiogfhgfhoihgoireghreghreguhreguireoghreouighreouighreughreguiorehreuiohruiorehreuioghreug");
|
||||
writer.WriteBinary(_demoPayload);
|
||||
|
||||
writer.Reset(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Jobs;
|
||||
using MQTTnet.Channel;
|
||||
using MQTTnet.Diagnostics.Logger;
|
||||
using MQTTnet.Implementations;
|
||||
using MQTTnet.Server;
|
||||
using MQTTnet.Server.Internal.Adapter;
|
||||
|
||||
namespace MQTTnet.Benchmarks;
|
||||
|
||||
[SimpleJob(RuntimeMoniker.Net60)]
|
||||
[MemoryDiagnoser]
|
||||
public class MqttTcpChannelBenchmark : BaseBenchmark
|
||||
{
|
||||
IMqttChannel _clientChannel;
|
||||
MqttServer _mqttServer;
|
||||
IMqttChannel _serverChannel;
|
||||
|
||||
[Benchmark]
|
||||
public async Task Send_10000_Chunks()
|
||||
{
|
||||
var size = 5;
|
||||
var iterations = 10000;
|
||||
|
||||
await Task.WhenAll(WriteAsync(iterations, size), ReadAsync(iterations, size));
|
||||
}
|
||||
|
||||
[GlobalSetup]
|
||||
public void Setup()
|
||||
{
|
||||
var serverFactory = new MqttServerFactory();
|
||||
var tcpServer = new MqttTcpServerAdapter();
|
||||
tcpServer.ClientHandler += args =>
|
||||
{
|
||||
_serverChannel = (IMqttChannel)args.GetType().GetField("_channel", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(args);
|
||||
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
var serverOptions = new MqttServerOptionsBuilder().Build();
|
||||
_mqttServer = serverFactory.CreateMqttServer(serverOptions, new[] { tcpServer }, new MqttNetEventLogger());
|
||||
|
||||
|
||||
_mqttServer.StartAsync().GetAwaiter().GetResult();
|
||||
|
||||
var clientOptions = new MqttClientOptionsBuilder().WithTcpServer("localhost").Build();
|
||||
|
||||
var tcpOptions = (MqttClientTcpOptions)clientOptions.ChannelOptions;
|
||||
_clientChannel = new MqttTcpChannel(new MqttClientOptions { ChannelOptions = tcpOptions });
|
||||
|
||||
_clientChannel.ConnectAsync(CancellationToken.None).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
async Task ReadAsync(int iterations, int size)
|
||||
{
|
||||
await Task.Yield();
|
||||
|
||||
var expected = iterations * size;
|
||||
long read = 0;
|
||||
|
||||
while (read < expected)
|
||||
{
|
||||
var readResult = await _clientChannel.ReadAsync(new byte[size], 0, size, CancellationToken.None).ConfigureAwait(false);
|
||||
read += readResult;
|
||||
}
|
||||
}
|
||||
|
||||
async Task WriteAsync(int iterations, int size)
|
||||
{
|
||||
await Task.Yield();
|
||||
|
||||
var buffer = new ReadOnlySequence<byte>(new byte[size]);
|
||||
|
||||
for (var i = 0; i < iterations; i++)
|
||||
{
|
||||
await _serverChannel.WriteAsync(buffer, true, CancellationToken.None).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using BenchmarkDotNet.Loggers;
|
||||
using BenchmarkDotNet.Running;
|
||||
using MQTTnet.Diagnostics;
|
||||
|
||||
namespace MQTTnet.Benchmarks
|
||||
{
|
||||
public static class Program
|
||||
{
|
||||
static int _selectedBenchmarkIndex;
|
||||
static List<Type> _benchmarks;
|
||||
|
||||
public static void Main(string[] arguments)
|
||||
{
|
||||
_benchmarks = CollectBenchmarks();
|
||||
HandleArguments(arguments);
|
||||
|
||||
while (true)
|
||||
{
|
||||
RenderMenu();
|
||||
|
||||
var key = Console.ReadKey(true);
|
||||
|
||||
if (key.Key == ConsoleKey.DownArrow)
|
||||
{
|
||||
_selectedBenchmarkIndex++;
|
||||
if (_selectedBenchmarkIndex > _benchmarks.Count - 1)
|
||||
{
|
||||
_selectedBenchmarkIndex = 0;
|
||||
}
|
||||
}
|
||||
else if (key.Key == ConsoleKey.UpArrow)
|
||||
{
|
||||
_selectedBenchmarkIndex--;
|
||||
if (_selectedBenchmarkIndex < 0)
|
||||
{
|
||||
_selectedBenchmarkIndex = _benchmarks.Count - 1;
|
||||
}
|
||||
}
|
||||
else if (key.Key == ConsoleKey.Escape)
|
||||
{
|
||||
Environment.Exit(0);
|
||||
}
|
||||
else if (key.Key == ConsoleKey.Enter)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
BenchmarkRunner.Run(_benchmarks[_selectedBenchmarkIndex]);
|
||||
|
||||
Console.ForegroundColor = ConsoleColor.Yellow;
|
||||
Console.WriteLine("Press any key to exit");
|
||||
Console.ReadLine();
|
||||
}
|
||||
|
||||
static List<Type> CollectBenchmarks()
|
||||
{
|
||||
var benchmarks = new List<Type>();
|
||||
|
||||
var types = typeof(Program).Assembly.GetExportedTypes();
|
||||
foreach (var type in types)
|
||||
{
|
||||
if (typeof(BaseBenchmark).IsAssignableFrom(type))
|
||||
{
|
||||
if (type != typeof(BaseBenchmark))
|
||||
{
|
||||
benchmarks.Add(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return benchmarks.OrderBy(b => b.Name).ToList();
|
||||
}
|
||||
|
||||
static void HandleArguments(string[] arguments)
|
||||
{
|
||||
if (arguments.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Allow for preselection to avoid developer frustration.
|
||||
|
||||
if (int.TryParse(arguments[0], out var benchmarkIndex))
|
||||
{
|
||||
_selectedBenchmarkIndex = benchmarkIndex;
|
||||
return;
|
||||
}
|
||||
|
||||
_selectedBenchmarkIndex = _benchmarks.FindIndex(b => b.Name.Equals(arguments[0]));
|
||||
|
||||
if (_selectedBenchmarkIndex < 0)
|
||||
{
|
||||
_selectedBenchmarkIndex = 0;
|
||||
}
|
||||
|
||||
if (_selectedBenchmarkIndex > _benchmarks.Count - 1)
|
||||
{
|
||||
_selectedBenchmarkIndex = _benchmarks.Count - 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void RenderMenu()
|
||||
{
|
||||
Console.Clear();
|
||||
|
||||
Console.WriteLine($"MQTTnet - Benchmarks");
|
||||
Console.WriteLine("-----------------------------------------------");
|
||||
Console.ForegroundColor = ConsoleColor.Yellow;
|
||||
Console.WriteLine("Press arrow keys for benchmark selection");
|
||||
Console.WriteLine("Press Enter for benchmark execution");
|
||||
Console.WriteLine("Press Esc for exit");
|
||||
Console.ForegroundColor = ConsoleColor.White;
|
||||
Console.WriteLine();
|
||||
|
||||
for (var index = 0; index < _benchmarks.Count; index++)
|
||||
{
|
||||
var benchmark = _benchmarks[index];
|
||||
|
||||
if (_selectedBenchmarkIndex == index)
|
||||
{
|
||||
Console.Write("-> ");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.Write(" ");
|
||||
}
|
||||
|
||||
Console.WriteLine(benchmark.Name);
|
||||
}
|
||||
|
||||
Console.WriteLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Jobs;
|
||||
using MQTTnet.Adapter;
|
||||
using MQTTnet.AspNetCore;
|
||||
using MQTTnet.Exceptions;
|
||||
using MQTTnet.Formatter;
|
||||
using MQTTnet.Packets;
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.IO;
|
||||
using System.IO.Pipelines;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MQTTnet.Benchmarks
|
||||
{
|
||||
[SimpleJob(RuntimeMoniker.Net60)]
|
||||
[RPlotExporter, RankColumn]
|
||||
[MemoryDiagnoser]
|
||||
public class ReaderExtensionsBenchmark
|
||||
{
|
||||
MqttPacketFormatterAdapter mqttPacketFormatter;
|
||||
MemoryStream stream;
|
||||
|
||||
[GlobalSetup]
|
||||
public void GlobalSetup()
|
||||
{
|
||||
mqttPacketFormatter = new MqttPacketFormatterAdapter(MqttProtocolVersion.V311, new MqttBufferWriter(4096, 65535));
|
||||
var mqttMessage = new MqttApplicationMessageBuilder()
|
||||
.WithTopic("topic")
|
||||
.WithPayload(new byte[10 * 1024])
|
||||
.Build();
|
||||
|
||||
var packet = MqttPublishPacketFactory.Create(mqttMessage);
|
||||
|
||||
var buffer = mqttPacketFormatter.Encode(packet);
|
||||
stream = new MemoryStream();
|
||||
stream.Write(buffer.Packet);
|
||||
stream.Write(buffer.Payload.ToArray());
|
||||
mqttPacketFormatter.Cleanup();
|
||||
}
|
||||
|
||||
[Benchmark(Baseline = true)]
|
||||
public async Task Before()
|
||||
{
|
||||
stream.Position = 0;
|
||||
var input = PipeReader.Create(stream);
|
||||
|
||||
while (true)
|
||||
{
|
||||
ReadResult readResult;
|
||||
var readTask = input.ReadAsync();
|
||||
if (readTask.IsCompleted)
|
||||
{
|
||||
readResult = readTask.Result;
|
||||
}
|
||||
else
|
||||
{
|
||||
readResult = await readTask.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
var buffer = readResult.Buffer;
|
||||
|
||||
var consumed = buffer.Start;
|
||||
var observed = buffer.Start;
|
||||
|
||||
try
|
||||
{
|
||||
if (!buffer.IsEmpty)
|
||||
{
|
||||
if (ReaderExtensions_Before.TryDecode(mqttPacketFormatter, buffer, out var packet, out consumed, out observed, out var received))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (readResult.IsCompleted)
|
||||
{
|
||||
throw new MqttCommunicationException("Connection Aborted");
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
// The buffer was sliced up to where it was consumed, so we can just advance to the start.
|
||||
// We mark examined as buffer.End so that if we didn't receive a full frame, we'll wait for more data
|
||||
// before yielding the read again.
|
||||
input.AdvanceTo(consumed, observed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public async Task After()
|
||||
{
|
||||
stream.Position = 0;
|
||||
var input = PipeReader.Create(stream);
|
||||
|
||||
while (true)
|
||||
{
|
||||
ReadResult readResult;
|
||||
var readTask = input.ReadAsync();
|
||||
if (readTask.IsCompleted)
|
||||
{
|
||||
readResult = readTask.Result;
|
||||
}
|
||||
else
|
||||
{
|
||||
readResult = await readTask.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
var buffer = readResult.Buffer;
|
||||
|
||||
var consumed = buffer.Start;
|
||||
var observed = buffer.Start;
|
||||
|
||||
try
|
||||
{
|
||||
if (!buffer.IsEmpty)
|
||||
{
|
||||
if (ReaderExtensions.TryDecode(mqttPacketFormatter, buffer, out var packet, out consumed, out observed, out var received))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (readResult.IsCompleted)
|
||||
{
|
||||
throw new MqttCommunicationException("Connection Aborted");
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
// The buffer was sliced up to where it was consumed, so we can just advance to the start.
|
||||
// We mark examined as buffer.End so that if we didn't receive a full frame, we'll wait for more data
|
||||
// before yielding the read again.
|
||||
input.AdvanceTo(consumed, observed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class ReaderExtensions_Before
|
||||
{
|
||||
public static bool TryDecode(MqttPacketFormatterAdapter formatter,
|
||||
in ReadOnlySequence<byte> input,
|
||||
out MqttPacket packet,
|
||||
out SequencePosition consumed,
|
||||
out SequencePosition observed,
|
||||
out int bytesRead)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(formatter);
|
||||
|
||||
packet = null;
|
||||
consumed = input.Start;
|
||||
observed = input.End;
|
||||
bytesRead = 0;
|
||||
var copy = input;
|
||||
|
||||
if (copy.Length < 2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var fixedHeader = copy.First.Span[0];
|
||||
if (!TryReadBodyLength(ref copy, out int headerLength, out var bodyLength))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (copy.Length < bodyLength)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var bodySlice = copy.Slice(0, bodyLength);
|
||||
var buffer = GetMemory(bodySlice).ToArray();
|
||||
|
||||
var receivedMqttPacket = new ReceivedMqttPacket(fixedHeader, new ArraySegment<byte>(buffer, 0, buffer.Length), buffer.Length + 2);
|
||||
|
||||
if (formatter.ProtocolVersion == MqttProtocolVersion.Unknown)
|
||||
{
|
||||
formatter.DetectProtocolVersion(receivedMqttPacket);
|
||||
}
|
||||
|
||||
packet = formatter.Decode(receivedMqttPacket);
|
||||
consumed = bodySlice.End;
|
||||
observed = bodySlice.End;
|
||||
bytesRead = headerLength + bodyLength;
|
||||
return true;
|
||||
}
|
||||
|
||||
static ReadOnlyMemory<byte> GetMemory(in ReadOnlySequence<byte> input)
|
||||
{
|
||||
if (input.IsSingleSegment)
|
||||
{
|
||||
return input.First;
|
||||
}
|
||||
|
||||
// Should be rare
|
||||
return input.ToArray();
|
||||
}
|
||||
|
||||
static bool TryReadBodyLength(ref ReadOnlySequence<byte> input, out int headerLength, out int bodyLength)
|
||||
{
|
||||
// Alorithm taken from https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/errata01/os/mqtt-v3.1.1-errata01-os-complete.html.
|
||||
var multiplier = 1;
|
||||
var value = 0;
|
||||
byte encodedByte;
|
||||
var index = 1;
|
||||
headerLength = 0;
|
||||
bodyLength = 0;
|
||||
|
||||
var temp = GetMemory(input.Slice(0, Math.Min(5, input.Length))).Span;
|
||||
|
||||
do
|
||||
{
|
||||
if (index == temp.Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
encodedByte = temp[index];
|
||||
index++;
|
||||
|
||||
value += (byte)(encodedByte & 127) * multiplier;
|
||||
if (multiplier > 128 * 128 * 128)
|
||||
{
|
||||
throw new MqttProtocolViolationException($"Remaining length is invalid (Data={string.Join(",", temp.Slice(1, index).ToArray())}).");
|
||||
}
|
||||
|
||||
multiplier *= 128;
|
||||
} while ((encodedByte & 128) != 0);
|
||||
|
||||
input = input.Slice(index);
|
||||
|
||||
headerLength = index;
|
||||
bodyLength = value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||