├── .gitignore
├── Git synch this fork to upstream master.bat
├── LICENSE
├── NetMQ.ReactiveExtensions.SamplePublisher
├── App.config
├── NetMQ.ReactiveExtensions.SamplePublisher.xproj
├── Program.cs
├── Properties
│ └── AssemblyInfo.cs
├── packages.config
└── project.json
├── NetMQ.ReactiveExtensions.SampleSubscriber
├── App.config
├── NetMQ.ReactiveExtensions.SampleSubscriber.xproj
├── Program.cs
├── Properties
│ └── AssemblyInfo.cs
├── packages.config
└── project.json
├── NetMQ.ReactiveExtensions.Tests
├── GlobalTimeout.cs
├── NUnitUtils.cs
├── NetMQ.ReactiveExtensions.Tests.xproj
├── NetMQ_ReactiveExtensions_MessageTypes.cs
├── NetMQ_ReactiveExtensions_Test_Batch_1.cs
├── NetMQ_ReactiveExtensions_Test_Batch_2.cs
├── Properties
│ └── AssemblyInfo.cs
├── RouterDealer_Test1.cs
├── RouterDealer_Test2.cs
├── SerializeExceptionToXml_Tests.cs
├── SerializeViaProtoBuf_Tests.cs
├── app.config
├── packages.config
└── project.json
├── NetMQ.ReactiveExtensions.sln
├── NetMQ.ReactiveExtensions
├── .gitignore
├── AnonymousDisposable.cs
├── INetMqTransportShared.cs
├── IPublisherNetMq.cs
├── ISubjectNetMQ.cs
├── NetMQ.ReactiveExtensions.xproj
├── NetMqTransportShared.cs
├── Properties
│ └── AssemblyInfo.cs
├── PublisherNetMq.cs
├── SerializeExceptionViaXml.cs
├── SerializeViaProtoBuf.cs
├── SubjectNetMQ.cs
├── SubscriberNetMq.cs
├── app.config
├── packages.config
└── project.json
├── NuGet
├── .gitignore
├── NetMQ.ReactiveExtensions.nuspec
└── nuget.config
├── README.md
└── img
├── NetMQ.ico
├── NetMQLogo-128px.png
├── NetMQLogo-32px.png
├── NetMQLogo-400px.png
├── NetMQLogo-48px.png
├── NetMQLogo-64px.png
└── NetMQLogo.svg
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | x64/
19 | x86/
20 | bld/
21 | [Bb]in/
22 | [Oo]bj/
23 |
24 | # Visual Studio 2015 cache/options directory
25 | .vs/
26 | # Uncomment if you have tasks that create the project's static files in wwwroot
27 | #wwwroot/
28 |
29 | # MSTest test Results
30 | [Tt]est[Rr]esult*/
31 | [Bb]uild[Ll]og.*
32 |
33 | # NUNIT
34 | *.VisualState.xml
35 | TestResult.xml
36 |
37 | # Build Results of an ATL Project
38 | [Dd]ebugPS/
39 | [Rr]eleasePS/
40 | dlldata.c
41 |
42 | # DNX
43 | project.lock.json
44 | artifacts/
45 |
46 | *_i.c
47 | *_p.c
48 | *_i.h
49 | *.ilk
50 | *.meta
51 | *.obj
52 | *.pch
53 | *.pdb
54 | *.pgc
55 | *.pgd
56 | *.rsp
57 | *.sbr
58 | *.tlb
59 | *.tli
60 | *.tlh
61 | *.tmp
62 | *.tmp_proj
63 | *.log
64 | *.vspscc
65 | *.vssscc
66 | .builds
67 | *.pidb
68 | *.svclog
69 | *.scc
70 |
71 | # Chutzpah Test files
72 | _Chutzpah*
73 |
74 | # Visual C++ cache files
75 | ipch/
76 | *.aps
77 | *.ncb
78 | *.opendb
79 | *.opensdf
80 | *.sdf
81 | *.cachefile
82 |
83 | # Visual Studio profiler
84 | *.psess
85 | *.vsp
86 | *.vspx
87 | *.sap
88 |
89 | # TFS 2012 Local Workspace
90 | $tf/
91 |
92 | # Guidance Automation Toolkit
93 | *.gpState
94 |
95 | # ReSharper is a .NET coding add-in
96 | _ReSharper*/
97 | *.[Rr]e[Ss]harper
98 | *.DotSettings.user
99 |
100 | # JustCode is a .NET coding add-in
101 | .JustCode
102 |
103 | # TeamCity is a build add-in
104 | _TeamCity*
105 |
106 | # DotCover is a Code Coverage Tool
107 | *.dotCover
108 |
109 | # NCrunch
110 | _NCrunch_*
111 | .*crunch*.local.xml
112 | nCrunchTemp_*
113 |
114 | # MightyMoose
115 | *.mm.*
116 | AutoTest.Net/
117 |
118 | # Web workbench (sass)
119 | .sass-cache/
120 |
121 | # Installshield output folder
122 | [Ee]xpress/
123 |
124 | # DocProject is a documentation generator add-in
125 | DocProject/buildhelp/
126 | DocProject/Help/*.HxT
127 | DocProject/Help/*.HxC
128 | DocProject/Help/*.hhc
129 | DocProject/Help/*.hhk
130 | DocProject/Help/*.hhp
131 | DocProject/Help/Html2
132 | DocProject/Help/html
133 |
134 | # Click-Once directory
135 | publish/
136 |
137 | # Publish Web Output
138 | *.[Pp]ublish.xml
139 | *.azurePubxml
140 | # TODO: Comment the next line if you want to checkin your web deploy settings
141 | # but database connection strings (with potential passwords) will be unencrypted
142 | *.pubxml
143 | *.publishproj
144 |
145 | # NuGet Packages
146 | *.nupkg
147 | # The packages folder can be ignored because of Package Restore
148 | **/packages/*
149 | # except build/, which is used as an MSBuild target.
150 | !**/packages/build/
151 | # Uncomment if necessary however generally it will be regenerated when needed
152 | #!**/packages/repositories.config
153 | # NuGet v3's project.json files produces more ignoreable files
154 | *.nuget.props
155 | *.nuget.targets
156 |
157 | # Microsoft Azure Build Output
158 | csx/
159 | *.build.csdef
160 |
161 | # Microsoft Azure Emulator
162 | ecf/
163 | rcf/
164 |
165 | # Microsoft Azure ApplicationInsights config file
166 | ApplicationInsights.config
167 |
168 | # Windows Store app package directory
169 | AppPackages/
170 | BundleArtifacts/
171 |
172 | # Visual Studio cache files
173 | # files ending in .cache can be ignored
174 | *.[Cc]ache
175 | # but keep track of directories ending in .cache
176 | !*.[Cc]ache/
177 |
178 | # Others
179 | ClientBin/
180 | ~$*
181 | *~
182 | *.dbmdl
183 | *.dbproj.schemaview
184 | *.pfx
185 | *.publishsettings
186 | node_modules/
187 | orleans.codegen.cs
188 |
189 | # RIA/Silverlight projects
190 | Generated_Code/
191 |
192 | # Backup & report files from converting an old project file
193 | # to a newer Visual Studio version. Backup files are not needed,
194 | # because we have git ;-)
195 | _UpgradeReport_Files/
196 | Backup*/
197 | UpgradeLog*.XML
198 | UpgradeLog*.htm
199 |
200 | # SQL Server files
201 | *.mdf
202 | *.ldf
203 |
204 | # Business Intelligence projects
205 | *.rdl.data
206 | *.bim.layout
207 | *.bim_*.settings
208 |
209 | # Microsoft Fakes
210 | FakesAssemblies/
211 |
212 | # GhostDoc plugin setting file
213 | *.GhostDoc.xml
214 |
215 | # Node.js Tools for Visual Studio
216 | .ntvs_analysis.dat
217 |
218 | # Visual Studio 6 build log
219 | *.plg
220 |
221 | # Visual Studio 6 workspace options file
222 | *.opt
223 |
224 | # Visual Studio LightSwitch build output
225 | **/*.HTMLClient/GeneratedArtifacts
226 | **/*.DesktopClient/GeneratedArtifacts
227 | **/*.DesktopClient/ModelManifest.xml
228 | **/*.Server/GeneratedArtifacts
229 | **/*.Server/ModelManifest.xml
230 | _Pvt_Extensions
231 |
232 | # Paket dependency manager
233 | .paket/paket.exe
234 |
235 | # FAKE - F# Make
236 | .fake/
237 | *.md.bak
238 | *.bak
239 |
--------------------------------------------------------------------------------
/Git synch this fork to upstream master.bat:
--------------------------------------------------------------------------------
1 | rem If we are are on a fork, synchronize this fork to the master.
2 | rem See https://help.github.com/articles/syncing-a-fork/
3 |
4 | rem If this has already been done, then this next line will have no effect.
5 | git remote add upstream https://github.com/NetMQ/NetMQ.ReactiveExtensions
6 | git fetch upstream
7 | git checkout master
8 | git merge upstream/master
9 | pause
10 |
11 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Mozilla Public License Version 2.0
2 | ==================================
3 |
4 | 1. Definitions
5 | --------------
6 |
7 | 1.1. "Contributor"
8 | means each individual or legal entity that creates, contributes to
9 | the creation of, or owns Covered Software.
10 |
11 | 1.2. "Contributor Version"
12 | means the combination of the Contributions of others (if any) used
13 | by a Contributor and that particular Contributor's Contribution.
14 |
15 | 1.3. "Contribution"
16 | means Covered Software of a particular Contributor.
17 |
18 | 1.4. "Covered Software"
19 | means Source Code Form to which the initial Contributor has attached
20 | the notice in Exhibit A, the Executable Form of such Source Code
21 | Form, and Modifications of such Source Code Form, in each case
22 | including portions thereof.
23 |
24 | 1.5. "Incompatible With Secondary Licenses"
25 | means
26 |
27 | (a) that the initial Contributor has attached the notice described
28 | in Exhibit B to the Covered Software; or
29 |
30 | (b) that the Covered Software was made available under the terms of
31 | version 1.1 or earlier of the License, but not also under the
32 | terms of a Secondary License.
33 |
34 | 1.6. "Executable Form"
35 | means any form of the work other than Source Code Form.
36 |
37 | 1.7. "Larger Work"
38 | means a work that combines Covered Software with other material, in
39 | a separate file or files, that is not Covered Software.
40 |
41 | 1.8. "License"
42 | means this document.
43 |
44 | 1.9. "Licensable"
45 | means having the right to grant, to the maximum extent possible,
46 | whether at the time of the initial grant or subsequently, any and
47 | all of the rights conveyed by this License.
48 |
49 | 1.10. "Modifications"
50 | means any of the following:
51 |
52 | (a) any file in Source Code Form that results from an addition to,
53 | deletion from, or modification of the contents of Covered
54 | Software; or
55 |
56 | (b) any new file in Source Code Form that contains any Covered
57 | Software.
58 |
59 | 1.11. "Patent Claims" of a Contributor
60 | means any patent claim(s), including without limitation, method,
61 | process, and apparatus claims, in any patent Licensable by such
62 | Contributor that would be infringed, but for the grant of the
63 | License, by the making, using, selling, offering for sale, having
64 | made, import, or transfer of either its Contributions or its
65 | Contributor Version.
66 |
67 | 1.12. "Secondary License"
68 | means either the GNU General Public License, Version 2.0, the GNU
69 | Lesser General Public License, Version 2.1, the GNU Affero General
70 | Public License, Version 3.0, or any later versions of those
71 | licenses.
72 |
73 | 1.13. "Source Code Form"
74 | means the form of the work preferred for making modifications.
75 |
76 | 1.14. "You" (or "Your")
77 | means an individual or a legal entity exercising rights under this
78 | License. For legal entities, "You" includes any entity that
79 | controls, is controlled by, or is under common control with You. For
80 | purposes of this definition, "control" means (a) the power, direct
81 | or indirect, to cause the direction or management of such entity,
82 | whether by contract or otherwise, or (b) ownership of more than
83 | fifty percent (50%) of the outstanding shares or beneficial
84 | ownership of such entity.
85 |
86 | 2. License Grants and Conditions
87 | --------------------------------
88 |
89 | 2.1. Grants
90 |
91 | Each Contributor hereby grants You a world-wide, royalty-free,
92 | non-exclusive license:
93 |
94 | (a) under intellectual property rights (other than patent or trademark)
95 | Licensable by such Contributor to use, reproduce, make available,
96 | modify, display, perform, distribute, and otherwise exploit its
97 | Contributions, either on an unmodified basis, with Modifications, or
98 | as part of a Larger Work; and
99 |
100 | (b) under Patent Claims of such Contributor to make, use, sell, offer
101 | for sale, have made, import, and otherwise transfer either its
102 | Contributions or its Contributor Version.
103 |
104 | 2.2. Effective Date
105 |
106 | The licenses granted in Section 2.1 with respect to any Contribution
107 | become effective for each Contribution on the date the Contributor first
108 | distributes such Contribution.
109 |
110 | 2.3. Limitations on Grant Scope
111 |
112 | The licenses granted in this Section 2 are the only rights granted under
113 | this License. No additional rights or licenses will be implied from the
114 | distribution or licensing of Covered Software under this License.
115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a
116 | Contributor:
117 |
118 | (a) for any code that a Contributor has removed from Covered Software;
119 | or
120 |
121 | (b) for infringements caused by: (i) Your and any other third party's
122 | modifications of Covered Software, or (ii) the combination of its
123 | Contributions with other software (except as part of its Contributor
124 | Version); or
125 |
126 | (c) under Patent Claims infringed by Covered Software in the absence of
127 | its Contributions.
128 |
129 | This License does not grant any rights in the trademarks, service marks,
130 | or logos of any Contributor (except as may be necessary to comply with
131 | the notice requirements in Section 3.4).
132 |
133 | 2.4. Subsequent Licenses
134 |
135 | No Contributor makes additional grants as a result of Your choice to
136 | distribute the Covered Software under a subsequent version of this
137 | License (see Section 10.2) or under the terms of a Secondary License (if
138 | permitted under the terms of Section 3.3).
139 |
140 | 2.5. Representation
141 |
142 | Each Contributor represents that the Contributor believes its
143 | Contributions are its original creation(s) or it has sufficient rights
144 | to grant the rights to its Contributions conveyed by this License.
145 |
146 | 2.6. Fair Use
147 |
148 | This License is not intended to limit any rights You have under
149 | applicable copyright doctrines of fair use, fair dealing, or other
150 | equivalents.
151 |
152 | 2.7. Conditions
153 |
154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
155 | in Section 2.1.
156 |
157 | 3. Responsibilities
158 | -------------------
159 |
160 | 3.1. Distribution of Source Form
161 |
162 | All distribution of Covered Software in Source Code Form, including any
163 | Modifications that You create or to which You contribute, must be under
164 | the terms of this License. You must inform recipients that the Source
165 | Code Form of the Covered Software is governed by the terms of this
166 | License, and how they can obtain a copy of this License. You may not
167 | attempt to alter or restrict the recipients' rights in the Source Code
168 | Form.
169 |
170 | 3.2. Distribution of Executable Form
171 |
172 | If You distribute Covered Software in Executable Form then:
173 |
174 | (a) such Covered Software must also be made available in Source Code
175 | Form, as described in Section 3.1, and You must inform recipients of
176 | the Executable Form how they can obtain a copy of such Source Code
177 | Form by reasonable means in a timely manner, at a charge no more
178 | than the cost of distribution to the recipient; and
179 |
180 | (b) You may distribute such Executable Form under the terms of this
181 | License, or sublicense it under different terms, provided that the
182 | license for the Executable Form does not attempt to limit or alter
183 | the recipients' rights in the Source Code Form under this License.
184 |
185 | 3.3. Distribution of a Larger Work
186 |
187 | You may create and distribute a Larger Work under terms of Your choice,
188 | provided that You also comply with the requirements of this License for
189 | the Covered Software. If the Larger Work is a combination of Covered
190 | Software with a work governed by one or more Secondary Licenses, and the
191 | Covered Software is not Incompatible With Secondary Licenses, this
192 | License permits You to additionally distribute such Covered Software
193 | under the terms of such Secondary License(s), so that the recipient of
194 | the Larger Work may, at their option, further distribute the Covered
195 | Software under the terms of either this License or such Secondary
196 | License(s).
197 |
198 | 3.4. Notices
199 |
200 | You may not remove or alter the substance of any license notices
201 | (including copyright notices, patent notices, disclaimers of warranty,
202 | or limitations of liability) contained within the Source Code Form of
203 | the Covered Software, except that You may alter any license notices to
204 | the extent required to remedy known factual inaccuracies.
205 |
206 | 3.5. Application of Additional Terms
207 |
208 | You may choose to offer, and to charge a fee for, warranty, support,
209 | indemnity or liability obligations to one or more recipients of Covered
210 | Software. However, You may do so only on Your own behalf, and not on
211 | behalf of any Contributor. You must make it absolutely clear that any
212 | such warranty, support, indemnity, or liability obligation is offered by
213 | You alone, and You hereby agree to indemnify every Contributor for any
214 | liability incurred by such Contributor as a result of warranty, support,
215 | indemnity or liability terms You offer. You may include additional
216 | disclaimers of warranty and limitations of liability specific to any
217 | jurisdiction.
218 |
219 | 4. Inability to Comply Due to Statute or Regulation
220 | ---------------------------------------------------
221 |
222 | If it is impossible for You to comply with any of the terms of this
223 | License with respect to some or all of the Covered Software due to
224 | statute, judicial order, or regulation then You must: (a) comply with
225 | the terms of this License to the maximum extent possible; and (b)
226 | describe the limitations and the code they affect. Such description must
227 | be placed in a text file included with all distributions of the Covered
228 | Software under this License. Except to the extent prohibited by statute
229 | or regulation, such description must be sufficiently detailed for a
230 | recipient of ordinary skill to be able to understand it.
231 |
232 | 5. Termination
233 | --------------
234 |
235 | 5.1. The rights granted under this License will terminate automatically
236 | if You fail to comply with any of its terms. However, if You become
237 | compliant, then the rights granted under this License from a particular
238 | Contributor are reinstated (a) provisionally, unless and until such
239 | Contributor explicitly and finally terminates Your grants, and (b) on an
240 | ongoing basis, if such Contributor fails to notify You of the
241 | non-compliance by some reasonable means prior to 60 days after You have
242 | come back into compliance. Moreover, Your grants from a particular
243 | Contributor are reinstated on an ongoing basis if such Contributor
244 | notifies You of the non-compliance by some reasonable means, this is the
245 | first time You have received notice of non-compliance with this License
246 | from such Contributor, and You become compliant prior to 30 days after
247 | Your receipt of the notice.
248 |
249 | 5.2. If You initiate litigation against any entity by asserting a patent
250 | infringement claim (excluding declaratory judgment actions,
251 | counter-claims, and cross-claims) alleging that a Contributor Version
252 | directly or indirectly infringes any patent, then the rights granted to
253 | You by any and all Contributors for the Covered Software under Section
254 | 2.1 of this License shall terminate.
255 |
256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all
257 | end user license agreements (excluding distributors and resellers) which
258 | have been validly granted by You or Your distributors under this License
259 | prior to termination shall survive termination.
260 |
261 | ************************************************************************
262 | * *
263 | * 6. Disclaimer of Warranty *
264 | * ------------------------- *
265 | * *
266 | * Covered Software is provided under this License on an "as is" *
267 | * basis, without warranty of any kind, either expressed, implied, or *
268 | * statutory, including, without limitation, warranties that the *
269 | * Covered Software is free of defects, merchantable, fit for a *
270 | * particular purpose or non-infringing. The entire risk as to the *
271 | * quality and performance of the Covered Software is with You. *
272 | * Should any Covered Software prove defective in any respect, You *
273 | * (not any Contributor) assume the cost of any necessary servicing, *
274 | * repair, or correction. This disclaimer of warranty constitutes an *
275 | * essential part of this License. No use of any Covered Software is *
276 | * authorized under this License except under this disclaimer. *
277 | * *
278 | ************************************************************************
279 |
280 | ************************************************************************
281 | * *
282 | * 7. Limitation of Liability *
283 | * -------------------------- *
284 | * *
285 | * Under no circumstances and under no legal theory, whether tort *
286 | * (including negligence), contract, or otherwise, shall any *
287 | * Contributor, or anyone who distributes Covered Software as *
288 | * permitted above, be liable to You for any direct, indirect, *
289 | * special, incidental, or consequential damages of any character *
290 | * including, without limitation, damages for lost profits, loss of *
291 | * goodwill, work stoppage, computer failure or malfunction, or any *
292 | * and all other commercial damages or losses, even if such party *
293 | * shall have been informed of the possibility of such damages. This *
294 | * limitation of liability shall not apply to liability for death or *
295 | * personal injury resulting from such party's negligence to the *
296 | * extent applicable law prohibits such limitation. Some *
297 | * jurisdictions do not allow the exclusion or limitation of *
298 | * incidental or consequential damages, so this exclusion and *
299 | * limitation may not apply to You. *
300 | * *
301 | ************************************************************************
302 |
303 | 8. Litigation
304 | -------------
305 |
306 | Any litigation relating to this License may be brought only in the
307 | courts of a jurisdiction where the defendant maintains its principal
308 | place of business and such litigation shall be governed by laws of that
309 | jurisdiction, without reference to its conflict-of-law provisions.
310 | Nothing in this Section shall prevent a party's ability to bring
311 | cross-claims or counter-claims.
312 |
313 | 9. Miscellaneous
314 | ----------------
315 |
316 | This License represents the complete agreement concerning the subject
317 | matter hereof. If any provision of this License is held to be
318 | unenforceable, such provision shall be reformed only to the extent
319 | necessary to make it enforceable. Any law or regulation which provides
320 | that the language of a contract shall be construed against the drafter
321 | shall not be used to construe this License against a Contributor.
322 |
323 | 10. Versions of the License
324 | ---------------------------
325 |
326 | 10.1. New Versions
327 |
328 | Mozilla Foundation is the license steward. Except as provided in Section
329 | 10.3, no one other than the license steward has the right to modify or
330 | publish new versions of this License. Each version will be given a
331 | distinguishing version number.
332 |
333 | 10.2. Effect of New Versions
334 |
335 | You may distribute the Covered Software under the terms of the version
336 | of the License under which You originally received the Covered Software,
337 | or under the terms of any subsequent version published by the license
338 | steward.
339 |
340 | 10.3. Modified Versions
341 |
342 | If you create software not governed by this License, and you want to
343 | create a new license for such software, you may create and use a
344 | modified version of this License if you rename the license and remove
345 | any references to the name of the license steward (except to note that
346 | such modified license differs from this License).
347 |
348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary
349 | Licenses
350 |
351 | If You choose to distribute Source Code Form that is Incompatible With
352 | Secondary Licenses under the terms of this version of the License, the
353 | notice described in Exhibit B of this License must be attached.
354 |
355 | Exhibit A - Source Code Form License Notice
356 | -------------------------------------------
357 |
358 | This Source Code Form is subject to the terms of the Mozilla Public
359 | License, v. 2.0. If a copy of the MPL was not distributed with this
360 | file, You can obtain one at http://mozilla.org/MPL/2.0/.
361 |
362 | If it is not possible or desirable to put the notice in a particular
363 | file, then You may include the notice in a location (such as a LICENSE
364 | file in a relevant directory) where a recipient would be likely to look
365 | for such a notice.
366 |
367 | You may add additional accurate notices of copyright ownership.
368 |
369 | Exhibit B - "Incompatible With Secondary Licenses" Notice
370 | ---------------------------------------------------------
371 |
372 | This Source Code Form is "Incompatible With Secondary Licenses", as
373 | defined by the Mozilla Public License, v. 2.0.
374 |
--------------------------------------------------------------------------------
/NetMQ.ReactiveExtensions.SamplePublisher/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/NetMQ.ReactiveExtensions.SamplePublisher/NetMQ.ReactiveExtensions.SamplePublisher.xproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 14.0
5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
6 |
7 |
8 |
9 | 72bbf61c-b5ac-49c5-b4f6-0eeeb97a98cf
10 | NetMQ.ReactiveExtensions.SamplePublisher
11 | ..\artifacts\obj\$(MSBuildProjectName)
12 | .\bin\
13 |
14 |
15 | 2.0
16 |
17 |
18 |
--------------------------------------------------------------------------------
/NetMQ.ReactiveExtensions.SamplePublisher/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using ProtoBuf;
4 |
5 | namespace NetMQ.ReactiveExtensions.SamplePublisher
6 | {
7 | class Program
8 | {
9 | static void Main(string[] args)
10 | {
11 | Console.Write("Reactive Extensions publisher demo:\n");
12 |
13 | string endPoint = "tcp://127.0.0.1:56001";
14 | if (args.Length >= 1)
15 | {
16 | endPoint = args[0];
17 | }
18 |
19 | Console.Write("Endpoint: {0}\n", endPoint);
20 |
21 | // Debug: Subscribe to ourself.
22 | {
23 | var subscriber = new SubscriberNetMq(endPoint, loggerDelegate: msg => Console.Write(msg));
24 | // Debug: subscribe to ourself. If you run the "SampleSubscriber" project now, you will see the same
25 | // messages appearing in that subscriber too.
26 | subscriber.Subscribe(message =>
27 | {
28 | Console.Write("Received: {0}, '{1}'.\n", message.Num, message.Name);
29 | });
30 | }
31 |
32 | // Publisher.
33 | {
34 | var publisher = new PublisherNetMq(endPoint, loggerDelegate: msg => Console.Write(msg));
35 |
36 | int i = 0;
37 | while (true)
38 | {
39 | var message = new MyMessage(i, "Bob");
40 |
41 | // When we call "OnNext", it binds a publisher to this endpoint endpoint.
42 | publisher.OnNext(message);
43 |
44 | Console.Write("Published: {0}, '{1}'.\n", message.Num, message.Name);
45 | Thread.Sleep(TimeSpan.FromMilliseconds(1000));
46 | i++;
47 | }
48 | }
49 |
50 | // NOTE: If you run the "SampleSubscriber" project now, you will see the same messages appearing in the subscriber.
51 | }
52 | }
53 |
54 | [ProtoContract]
55 | public class MyMessage
56 | {
57 | public MyMessage()
58 | {
59 |
60 | }
61 |
62 | public MyMessage(int num, string name)
63 | {
64 | Num = num;
65 | Name = name;
66 | }
67 |
68 | [ProtoMember(1)]
69 | public int Num { get; set; }
70 |
71 | [ProtoMember(2)]
72 | public string Name { get; set; }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/NetMQ.ReactiveExtensions.SamplePublisher/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("NetMQ.ReactiveExtensions.SampleServer")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("NetMQ.ReactiveExtensions.SampleServer")]
13 | [assembly: AssemblyCopyright("Copyright ©Shane Tolmie 2016")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("428889ac-3686-4930-b20d-ba0cbde35115")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("0.9.4.7")]
36 | [assembly: AssemblyFileVersion("0.9.4.7")]
37 |
--------------------------------------------------------------------------------
/NetMQ.ReactiveExtensions.SamplePublisher/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/NetMQ.ReactiveExtensions.SamplePublisher/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1.0.0-*",
3 | "buildOptions": {
4 | "emitEntryPoint": true
5 | },
6 |
7 | "dependencies": {
8 | "NetMQ.ReactiveExtensions": "1.0.0-*"
9 | },
10 | "frameworks": {
11 | "net45": {},
12 | "netcoreapp1.0": {
13 | "dependencies": {
14 | "Microsoft.NETCore.App": {
15 | "type": "platform",
16 | "version": "1.0.1"
17 | }
18 | },
19 | "imports": "dnxcore50"
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/NetMQ.ReactiveExtensions.SampleSubscriber/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/NetMQ.ReactiveExtensions.SampleSubscriber/NetMQ.ReactiveExtensions.SampleSubscriber.xproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 14.0
5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
6 |
7 |
8 |
9 | f8cad08f-532c-438b-9b7e-218dd6904840
10 | NetMQ.ReactiveExtensions.SampleSubscriber
11 | ..\artifacts\obj\$(MSBuildProjectName)
12 | .\bin\
13 |
14 |
15 | 2.0
16 |
17 |
18 |
--------------------------------------------------------------------------------
/NetMQ.ReactiveExtensions.SampleSubscriber/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using ProtoBuf;
4 |
5 | namespace NetMQ.ReactiveExtensions.SampleSubscriber
6 | {
7 | class Program
8 | {
9 | static void Main(string[] args)
10 | {
11 | Console.Write("Reactive Extensions subscriber demo:\n");
12 |
13 | string endPoint = "tcp://127.0.0.1:56001";
14 | if (args.Length >= 1)
15 | {
16 | endPoint = args[0];
17 | }
18 |
19 | Console.Write("Endpoint: {0}\n", endPoint);
20 |
21 | SubscriberNetMq subject = new SubscriberNetMq(endPoint, loggerDelegate: msg => Console.Write(msg));
22 | subject.Subscribe(message =>
23 | {
24 | Console.Write("Received: {0}, '{1}'.\n", message.Num, message.Name);
25 | });
26 |
27 | Console.WriteLine("[waiting for publisher - any key to exit]");
28 | Console.ReadKey();
29 | }
30 | }
31 |
32 |
33 |
34 | [ProtoContract]
35 | public class MyMessage
36 | {
37 | public MyMessage()
38 | {
39 |
40 | }
41 |
42 | public MyMessage(int num, string name)
43 | {
44 | Num = num;
45 | Name = name;
46 | }
47 |
48 | [ProtoMember(1)]
49 | public int Num { get; set; }
50 |
51 | [ProtoMember(2)]
52 | public string Name { get; set; }
53 | }
54 | }
--------------------------------------------------------------------------------
/NetMQ.ReactiveExtensions.SampleSubscriber/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("NetMQ.ReactiveExtensions.SampleClient")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("NetMQ.ReactiveExtensions.SampleClient")]
13 | [assembly: AssemblyCopyright("Copyright ©Shane Tolmie 2016")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("e9ce5a1c-0940-44a7-a03a-f3e26048ebb5")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("0.9.4.7")]
36 | [assembly: AssemblyFileVersion("0.9.4.7")]
37 |
--------------------------------------------------------------------------------
/NetMQ.ReactiveExtensions.SampleSubscriber/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/NetMQ.ReactiveExtensions.SampleSubscriber/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1.0.0-*",
3 | "buildOptions": {
4 | "emitEntryPoint": true
5 | },
6 |
7 | "dependencies": {
8 | "NetMQ.ReactiveExtensions": "1.0.0-*"
9 | },
10 | "frameworks": {
11 | "net45": {},
12 | "netcoreapp1.0": {
13 | "dependencies": {
14 | "Microsoft.NETCore.App": {
15 | "type": "platform",
16 | "version": "1.0.1"
17 | }
18 | },
19 | "imports": "dnxcore50"
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/NetMQ.ReactiveExtensions.Tests/GlobalTimeout.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace NetMQ.ReactiveExtensions.Tests
4 | {
5 | public static class GlobalTimeout
6 | {
7 | static GlobalTimeout()
8 | {
9 | Timeout = TimeSpan.FromSeconds(60);
10 | }
11 |
12 | public static TimeSpan Timeout { get; set; }
13 | }
14 | }
--------------------------------------------------------------------------------
/NetMQ.ReactiveExtensions.Tests/NUnitUtils.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net;
3 | using System.Net.Sockets;
4 | using NUnit.Framework;
5 |
6 | namespace NetMQ.ReactiveExtensions.Tests
7 | {
8 | public static class NUnitUtils
9 | {
10 | ///
11 | /// Intent: Returns next free TCP/IP port. See
12 | /// http://stackoverflow.com/questions/138043/find-the-next-tcp-port-in-net
13 | ///
14 | ///
15 | /// Yes. Quote: "I successfully used this technique to get a free port. I too was concerned about
16 | /// race-conditions, with some other process sneaking in and grabbing the recently-detected-as-free port. So I
17 | /// wrote a test with a forced Sleep(100) between var port = FreeTcpPort() and starting an HttpListener on the
18 | /// free port. I then ran 8 identical processes hammering on this in a loop. I could never hit the race
19 | /// condition. My anecdotal evidence (Win 7) is that the OS apparently cycles through the range of ephemeral
20 | /// ports (a few thousand) before coming around again. So the above snippet should be just fine."
21 | ///
22 | /// A free TCP/IP port.
23 | public static int TcpPortFree()
24 | {
25 | var l = new TcpListener(IPAddress.Loopback, 0);
26 | l.Start();
27 | var port = ((IPEndPoint) l.LocalEndpoint).Port;
28 | l.Stop();
29 | return port;
30 | }
31 |
32 | public static void PrintTestName()
33 | {
34 | Console.Write("\n\n*****\nTest: {0}\n*****\n", TestContext.CurrentContext.Test.Name);
35 | }
36 |
37 | public static void PrintElapsedTime(TimeSpan sw, int? max = null)
38 | {
39 | Console.Write("\n*****\nElapsed time: {0} milliseconds", sw.TotalMilliseconds);
40 | if (max != null)
41 | {
42 | Console.Write(" ({0:0,000}/sec)", (double) max/sw.TotalSeconds);
43 | }
44 | Console.Write("\n");
45 | }
46 | }
47 | }
--------------------------------------------------------------------------------
/NetMQ.ReactiveExtensions.Tests/NetMQ.ReactiveExtensions.Tests.xproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 14.0
5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
6 |
7 |
8 |
9 | 7d1cae81-2573-427b-a493-41d02a11eb30
10 | NetMQ.ReactiveExtensions.Tests
11 | ..\artifacts\obj\$(MSBuildProjectName)
12 | .\bin\
13 |
14 |
15 | 2.0
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/NetMQ.ReactiveExtensions.Tests/NetMQ_ReactiveExtensions_MessageTypes.cs:
--------------------------------------------------------------------------------
1 | #region
2 |
3 | using ProtoBuf;
4 |
5 | // ReSharper disable InconsistentNaming
6 |
7 | #endregion
8 |
9 | // ReSharper disable ConvertClosureToMethodGroup
10 |
11 | namespace NetMQ.ReactiveExtensions.Tests
12 | {
13 |
14 | #region Message types.
15 |
16 | [ProtoContract]
17 | public class MyMessageClassType1
18 | {
19 | public MyMessageClassType1()
20 | {
21 | }
22 |
23 | public MyMessageClassType1(int num, string name)
24 | {
25 | Num = num;
26 | Name = name;
27 | }
28 |
29 | [ProtoMember(1)]
30 | public int Num { get; set; }
31 |
32 | [ProtoMember(2)]
33 | public string Name { get; set; }
34 | }
35 |
36 | [ProtoContract]
37 | public class MyMessageClassType2
38 | {
39 | public MyMessageClassType2()
40 | {
41 | }
42 |
43 | public MyMessageClassType2(int num, string name)
44 | {
45 | Num = num;
46 | Name = name;
47 | }
48 |
49 | [ProtoMember(1)]
50 | public int Num { get; set; }
51 |
52 | [ProtoMember(2)]
53 | public string Name { get; set; }
54 | }
55 |
56 | [ProtoContract]
57 | public struct MyMessageStructType1
58 | {
59 | public MyMessageStructType1(int num, string name)
60 | {
61 | Num = num;
62 | Name = name;
63 | }
64 |
65 | [ProtoMember(1)]
66 | public int Num { get; set; }
67 |
68 | [ProtoMember(2)]
69 | public string Name { get; set; }
70 | }
71 |
72 | [ProtoContract]
73 | public struct MyMessageStructType2
74 | {
75 | public MyMessageStructType2(int num, string name)
76 | {
77 | Num = num;
78 | Name = name;
79 | }
80 |
81 | [ProtoMember(1)]
82 | public int Num { get; set; }
83 |
84 | [ProtoMember(2)]
85 | public string Name { get; set; }
86 | }
87 |
88 | public class MessageNotSerializableByProtobuf
89 | {
90 | public int NotSerializable { get; set; }
91 | }
92 |
93 | [ProtoContract]
94 | public class ClassNameIsLongerThenThirtyTwoCharactersForAbsolutelySure
95 | {
96 | public ClassNameIsLongerThenThirtyTwoCharactersForAbsolutelySure()
97 | {
98 | }
99 |
100 | public ClassNameIsLongerThenThirtyTwoCharactersForAbsolutelySure(int num, string name)
101 | {
102 | Num = num;
103 | Name = name;
104 | }
105 |
106 | [ProtoMember(1)]
107 | public int Num { get; set; }
108 |
109 | [ProtoMember(2)]
110 | public string Name { get; set; }
111 | }
112 |
113 | #endregion
114 | }
--------------------------------------------------------------------------------
/NetMQ.ReactiveExtensions.Tests/NetMQ_ReactiveExtensions_Test_Batch_1.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.Threading;
5 | using NUnit.Framework;
6 | // ReSharper disable InconsistentNaming
7 |
8 | namespace NetMQ.ReactiveExtensions.Tests
9 | {
10 | [TestFixture]
11 | public class NetMQ_ReactiveExtensions_Test_Batch_1
12 | {
13 | [Test]
14 | public void Can_Serialize_Class_Name_Longer_Then_Thirty_Two_Characters()
15 | {
16 | NUnitUtils.PrintTestName();
17 |
18 | var sw = Stopwatch.StartNew();
19 |
20 | var cd = new CountdownEvent(5);
21 | {
22 | var freePort = NUnitUtils.TcpPortFree();
23 |
24 | var pubSub =
25 | new SubjectNetMQ(
26 | "tcp://127.0.0.1:" + freePort, loggerDelegate: Console.Write);
27 | pubSub.Subscribe(o =>
28 | {
29 | Assert.IsTrue(o.Name == "Bob");
30 | Console.Write("Test: Num={0}, Name={1}\n", o.Num, o.Name);
31 | cd.Signal();
32 | },
33 | ex => { Console.WriteLine("Exception! {0}", ex.Message); });
34 |
35 | pubSub.OnNext(new ClassNameIsLongerThenThirtyTwoCharactersForAbsolutelySure(38, "Bob"));
36 | pubSub.OnNext(new ClassNameIsLongerThenThirtyTwoCharactersForAbsolutelySure(39, "Bob"));
37 | pubSub.OnNext(new ClassNameIsLongerThenThirtyTwoCharactersForAbsolutelySure(40, "Bob"));
38 | pubSub.OnNext(new ClassNameIsLongerThenThirtyTwoCharactersForAbsolutelySure(41, "Bob"));
39 | pubSub.OnNext(new ClassNameIsLongerThenThirtyTwoCharactersForAbsolutelySure(42, "Bob"));
40 | }
41 |
42 | if (cd.Wait(GlobalTimeout.Timeout) == false) // Blocks until _countdown.Signal has been called.
43 | {
44 | Assert.Fail("Timed out, this test should complete in {0} seconds.", GlobalTimeout.Timeout.TotalSeconds);
45 | }
46 |
47 | NUnitUtils.PrintElapsedTime(sw.Elapsed);
48 | }
49 |
50 |
51 | [Test]
52 | public void Can_Serialize_Using_Protobuf_With_Class()
53 | {
54 | NUnitUtils.PrintTestName();
55 |
56 | var sw = Stopwatch.StartNew();
57 |
58 | List timeMilliseconds = new List();
59 |
60 | var cd = new CountdownEvent(5);
61 | {
62 | var freePort = NUnitUtils.TcpPortFree();
63 |
64 | timeMilliseconds.Add(sw.Elapsed);
65 | var pubSub = new SubjectNetMQ("tcp://127.0.0.1:" + freePort,
66 | loggerDelegate: Console.Write);
67 | timeMilliseconds.Add(sw.Elapsed);
68 | pubSub.Subscribe(
69 | o =>
70 | {
71 | Assert.IsTrue(o.Name == "Bob");
72 | Console.Write("Test: Num={0}, Name={1}\n", o.Num, o.Name);
73 | cd.Signal();
74 | },
75 | ex =>
76 | {
77 | Console.WriteLine("Exception! {0}", ex.Message);
78 | });
79 | timeMilliseconds.Add(sw.Elapsed);
80 | pubSub.OnNext(new MyMessageClassType1(38, "Bob"));
81 | timeMilliseconds.Add(sw.Elapsed);
82 | pubSub.OnNext(new MyMessageClassType1(39, "Bob"));
83 | timeMilliseconds.Add(sw.Elapsed);
84 | pubSub.OnNext(new MyMessageClassType1(40, "Bob"));
85 | timeMilliseconds.Add(sw.Elapsed);
86 | pubSub.OnNext(new MyMessageClassType1(41, "Bob"));
87 | timeMilliseconds.Add(sw.Elapsed);
88 | pubSub.OnNext(new MyMessageClassType1(42, "Bob"));
89 | timeMilliseconds.Add(sw.Elapsed);
90 | }
91 |
92 | for (int i = 0; i < timeMilliseconds.Count; i++)
93 | {
94 | var t = timeMilliseconds[i];
95 | Console.WriteLine("- Stage {0}: {1:0,000} milliseconds", i, t.TotalMilliseconds);
96 | }
97 |
98 | if (cd.Wait(GlobalTimeout.Timeout) == false) // Blocks until _countdown.Signal has been called.
99 | {
100 | Assert.Fail("Timed out, this test should complete in {0} seconds.", GlobalTimeout.Timeout.TotalSeconds);
101 | }
102 |
103 | NUnitUtils.PrintElapsedTime(sw.Elapsed);
104 | }
105 |
106 | [Test]
107 | public void Can_Serialize_Using_Protobuf_With_Struct()
108 | {
109 | NUnitUtils.PrintTestName();
110 |
111 | var sw = Stopwatch.StartNew();
112 |
113 | var cd = new CountdownEvent(5);
114 | {
115 | var freePort = NUnitUtils.TcpPortFree();
116 |
117 | var pubSub = new SubjectNetMQ("tcp://127.0.0.1:" + freePort,
118 | loggerDelegate: Console.Write);
119 | pubSub.Subscribe(o =>
120 | {
121 | Assert.IsTrue(o.Name == "Bob");
122 | Console.Write("Test: Num={0}, Name={1}\n", o.Num, o.Name);
123 | cd.Signal();
124 | },
125 | ex => { Console.WriteLine("Exception! {0}", ex.Message); });
126 |
127 | pubSub.OnNext(new MyMessageStructType1(38, "Bob"));
128 | pubSub.OnNext(new MyMessageStructType1(39, "Bob"));
129 | pubSub.OnNext(new MyMessageStructType1(40, "Bob"));
130 | pubSub.OnNext(new MyMessageStructType1(41, "Bob"));
131 | pubSub.OnNext(new MyMessageStructType1(42, "Bob"));
132 | }
133 |
134 | if (cd.Wait(GlobalTimeout.Timeout) == false) // Blocks until _countdown.Signal has been called.
135 | {
136 | Assert.Fail("Timed out, this test should complete in {0} seconds.", GlobalTimeout.Timeout.TotalSeconds);
137 | }
138 |
139 | NUnitUtils.PrintElapsedTime(sw.Elapsed);
140 | }
141 |
142 | [Test]
143 | public static void Disposing_Of_One_Does_Not_Dispose_Of_The_Other()
144 | {
145 | NUnitUtils.PrintTestName();
146 | var sw = Stopwatch.StartNew();
147 |
148 | var max = 1000;
149 | var cd = new CountdownEvent(max);
150 | {
151 | var freePort = NUnitUtils.TcpPortFree();
152 | var pubSub = new SubjectNetMQ("tcp://127.0.0.1:" + freePort, loggerDelegate: Console.Write);
153 | var d1 = pubSub.Subscribe(o => { cd.Signal(); });
154 |
155 | var d2 = pubSub.Subscribe(o => { Assert.Fail(); },
156 | ex => { Console.WriteLine("Exception in subscriber thread."); });
157 | d2.Dispose();
158 |
159 | for (var i = 0; i < max; i++)
160 | {
161 | pubSub.OnNext(i);
162 | }
163 | }
164 |
165 | if (cd.Wait(GlobalTimeout.Timeout) == false) // Blocks until _countdown.Signal has been called.
166 | {
167 | Assert.Fail("Timed out, this test should complete in {0} seconds.", GlobalTimeout.Timeout.TotalSeconds);
168 | }
169 |
170 | NUnitUtils.PrintElapsedTime(sw.Elapsed);
171 | }
172 |
173 |
174 | [Test]
175 | public void Initialize_Publisher_Then_Subscriber()
176 | {
177 | NUnitUtils.PrintTestName();
178 |
179 | var sw = Stopwatch.StartNew();
180 |
181 | var cd = new CountdownEvent(5);
182 | {
183 | var freePort = NUnitUtils.TcpPortFree();
184 |
185 | var pubSub = new SubjectNetMQ("tcp://127.0.0.1:" + freePort, loggerDelegate: Console.Write);
186 |
187 | // Forces the publisher to be initialized. Subscriber not set up yet, so this message will never get
188 | // delivered to the subscriber, which is what is should do.
189 | pubSub.OnNext(1);
190 |
191 | pubSub.Subscribe(o =>
192 | {
193 | Assert.IsTrue(o != 1);
194 | Console.Write("Test 1: {0}\n", o);
195 | cd.Signal();
196 | },
197 | ex => { Console.WriteLine("Exception! {0}", ex.Message); });
198 |
199 | pubSub.OnNext(38);
200 | pubSub.OnNext(39);
201 | pubSub.OnNext(40);
202 | pubSub.OnNext(41);
203 | pubSub.OnNext(42);
204 | }
205 |
206 | if (cd.Wait(GlobalTimeout.Timeout) == false) // Blocks until _countdown.Signal has been called.
207 | {
208 | Assert.Fail("Timed out, this test should complete in {0} seconds.", GlobalTimeout.Timeout.TotalSeconds);
209 | }
210 |
211 | NUnitUtils.PrintElapsedTime(sw.Elapsed);
212 | }
213 |
214 | [Test]
215 | public void OnCompleted_Should_Get_Passed_To_Subscribers()
216 | {
217 | NUnitUtils.PrintTestName();
218 |
219 | var sw = Stopwatch.StartNew();
220 |
221 | List timeMilliseconds = new List();
222 |
223 | var weAreDone = new CountdownEvent(1);
224 | {
225 | timeMilliseconds.Add(sw.Elapsed);
226 | var freePort = NUnitUtils.TcpPortFree();
227 | timeMilliseconds.Add(sw.Elapsed);
228 | var pubSub = new SubjectNetMQ("tcp://127.0.0.1:" + freePort, loggerDelegate: Console.Write);
229 | timeMilliseconds.Add(sw.Elapsed);
230 | pubSub.Subscribe(
231 | o =>
232 | {
233 | // If this gets called more than max times, it will throw an exception as it is going through 0.
234 | //Console.Write("FAIL!");
235 | //Assert.Fail();
236 | },
237 | ex =>
238 | {
239 | Console.Write("FAIL!");
240 | Assert.Fail();
241 | },
242 | () =>
243 | {
244 | Console.Write("Pass!");
245 | weAreDone.Signal();
246 | });
247 | timeMilliseconds.Add(sw.Elapsed);
248 | pubSub.OnCompleted();
249 | timeMilliseconds.Add(sw.Elapsed);
250 | }
251 |
252 | for (int i = 0; i < timeMilliseconds.Count; i++)
253 | {
254 | var t = timeMilliseconds[i];
255 | Console.WriteLine("- Stage {0}: {1:0,000} milliseconds", i, t.TotalMilliseconds);
256 | }
257 |
258 | if (weAreDone.Wait(GlobalTimeout.Timeout) == false) // Blocks until _countdown.Signal has been called.
259 | {
260 | Assert.Fail("Timed out, this test should complete in {0} seconds.", GlobalTimeout.Timeout.TotalSeconds);
261 | }
262 |
263 | NUnitUtils.PrintElapsedTime(sw.Elapsed);
264 | }
265 |
266 | [Test]
267 | public void OnException_Should_Get_Passed_To_Subscribers()
268 | {
269 | NUnitUtils.PrintTestName();
270 |
271 | var sw = Stopwatch.StartNew();
272 |
273 | var weAreDone = new CountdownEvent(1);
274 | var passed = false;
275 | {
276 | var freePort = NUnitUtils.TcpPortFree();
277 | var pubSub = new SubjectNetMQ("tcp://127.0.0.1:" + freePort, loggerDelegate: Console.Write);
278 | pubSub.Subscribe(
279 | o =>
280 | {
281 | // If this gets called more than max times, it will throw an exception as it is going through 0.
282 | Assert.Fail();
283 | },
284 | ex =>
285 | {
286 | Console.Write("Exception: {0}", ex.Message);
287 | if (ex.Message.Contains("passed") == true)
288 | {
289 | passed = true;
290 | }
291 | weAreDone.Signal();
292 | },
293 | () => { Assert.Fail(); });
294 |
295 | pubSub.OnError(new Exception("passed"));
296 | }
297 |
298 | if (weAreDone.Wait(GlobalTimeout.Timeout) == false || passed == false) // Blocks until _countdown.Signal has been called.
299 | {
300 | Assert.Fail("Timed out, this test should complete in {0} seconds.", GlobalTimeout.Timeout.TotalSeconds);
301 | }
302 |
303 | if (passed == false) // Blocks until _countdown.Signal has been called.
304 | {
305 | Assert.Fail("Expected exception text did not arrive back.");
306 | }
307 |
308 | NUnitUtils.PrintElapsedTime(sw.Elapsed);
309 | }
310 |
311 | [Test]
312 | public void Simplest_Fanout_Sub()
313 | {
314 | NUnitUtils.PrintTestName();
315 |
316 | var sw = Stopwatch.StartNew();
317 |
318 | var cd = new CountdownEvent(3);
319 | {
320 | var freePort = NUnitUtils.TcpPortFree();
321 | var pubSub = new SubjectNetMQ("tcp://127.0.0.1:" + freePort, loggerDelegate: Console.Write);
322 | pubSub.Subscribe(o =>
323 | {
324 | Assert.AreEqual(o, 42);
325 | Console.Write("PubTwoThreadFanoutSub1: {0}\n", o);
326 | cd.Signal();
327 | });
328 | pubSub.Subscribe(o =>
329 | {
330 | Assert.AreEqual(o, 42);
331 | Console.Write("PubTwoThreadFanoutSub2: {0}\n", o);
332 | cd.Signal();
333 | });
334 | pubSub.Subscribe(o =>
335 | {
336 | Assert.AreEqual(o, 42);
337 | Console.Write("PubTwoThreadFanoutSub3: {0}\n", o);
338 | cd.Signal();
339 | });
340 |
341 | pubSub.OnNext(42);
342 | }
343 |
344 | if (cd.Wait(GlobalTimeout.Timeout) == false) // Blocks until _countdown.Signal has been called.
345 | {
346 | Assert.Fail("Timed out, this test should complete in {0} seconds.", GlobalTimeout.Timeout.TotalSeconds);
347 | }
348 |
349 | NUnitUtils.PrintElapsedTime(sw.Elapsed);
350 | }
351 |
352 | [Test]
353 | public void Simplest_Test_Publisher_To_Subscriber()
354 | {
355 | NUnitUtils.PrintTestName();
356 |
357 | var sw = Stopwatch.StartNew();
358 |
359 | var cd = new CountdownEvent(5);
360 | {
361 | var freePort = NUnitUtils.TcpPortFree();
362 |
363 | var publisher = new PublisherNetMq("tcp://127.0.0.1:" + freePort, loggerDelegate: Console.Write);
364 | var subscriber = new SubscriberNetMq("tcp://127.0.0.1:" + freePort, loggerDelegate: Console.Write);
365 |
366 | subscriber.Subscribe(o =>
367 | {
368 | Console.Write("Test 1: {0}\n", o);
369 | cd.Signal();
370 | },
371 | ex => { Console.WriteLine("Exception! {0}", ex.Message); });
372 |
373 | publisher.OnNext(38);
374 | publisher.OnNext(39);
375 | publisher.OnNext(40);
376 | publisher.OnNext(41);
377 | publisher.OnNext(42);
378 | }
379 |
380 | if (cd.Wait(GlobalTimeout.Timeout) == false) // Blocks until _countdown.Signal has been called.
381 | {
382 | Assert.Fail("Timed out, this test should complete in {0} seconds.", GlobalTimeout.Timeout.TotalSeconds);
383 | }
384 |
385 | NUnitUtils.PrintElapsedTime(sw.Elapsed);
386 | }
387 |
388 | [Test]
389 | public void Simplest_Test_Subject()
390 | {
391 | NUnitUtils.PrintTestName();
392 |
393 | var sw = Stopwatch.StartNew();
394 |
395 | var cd = new CountdownEvent(5);
396 | {
397 | var freePort = NUnitUtils.TcpPortFree();
398 |
399 | var pubSub = new SubjectNetMQ("tcp://127.0.0.1:" + freePort, loggerDelegate: Console.Write);
400 | pubSub.Subscribe(o =>
401 | {
402 | Console.Write("Test 1: {0}\n", o);
403 | cd.Signal();
404 | },
405 | ex => { Console.WriteLine("Exception! {0}", ex.Message); });
406 |
407 | pubSub.OnNext(38);
408 | pubSub.OnNext(39);
409 | pubSub.OnNext(40);
410 | pubSub.OnNext(41);
411 | pubSub.OnNext(42);
412 | }
413 |
414 | if (cd.Wait(GlobalTimeout.Timeout) == false) // Blocks until _countdown.Signal has been called.
415 | {
416 | Assert.Fail("Timed out, this test should complete in {0} seconds.", GlobalTimeout.Timeout.TotalSeconds);
417 | }
418 |
419 | NUnitUtils.PrintElapsedTime(sw.Elapsed);
420 | }
421 | }
422 | }
--------------------------------------------------------------------------------
/NetMQ.ReactiveExtensions.Tests/NetMQ_ReactiveExtensions_Test_Batch_2.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.Threading;
4 | using NetMQ.Sockets;
5 | using NUnit.Framework;
6 | // ReSharper disable InconsistentNaming
7 |
8 | namespace NetMQ.ReactiveExtensions.Tests
9 | {
10 | [TestFixture]
11 | public class NetMQ_ReactiveExtensions_Test_Batch_2
12 | {
13 | [Test]
14 | public void If_Message_Not_Serializable_By_Protobuf_Throw_A_Meaningful_Error()
15 | {
16 | NUnitUtils.PrintTestName();
17 | var sw = Stopwatch.StartNew();
18 |
19 | var freePort = NUnitUtils.TcpPortFree();
20 | var pubSub = new SubjectNetMQ("tcp://127.0.0.1:" + freePort,
21 | loggerDelegate: Console.Write);
22 | try
23 | {
24 | pubSub.OnNext(new MessageNotSerializableByProtobuf());
25 |
26 | // We should have thrown an exception if the class was not serializable by ProtoBuf-Net.
27 | Assert.Fail();
28 | }
29 | catch (InvalidOperationException ex)
30 | {
31 | Assert.True(ex.Message.ToLower().Contains("protobuf"));
32 | Console.Write("Pass - meaningful message thrown if class was not serializable by ProtoBuf-Net.");
33 | }
34 | catch (Exception)
35 | {
36 | Assert.Fail();
37 | }
38 |
39 | NUnitUtils.PrintElapsedTime(sw.Elapsed);
40 | }
41 |
42 | [Test]
43 | public void PubSub_Should_Not_Crash_If_No_Thread_Sleep()
44 | {
45 | NUnitUtils.PrintTestName();
46 | var swAll = Stopwatch.StartNew();
47 |
48 | using (var pub = new PublisherSocket())
49 | {
50 | using (var sub = new SubscriberSocket())
51 | {
52 | var freePort = NUnitUtils.TcpPortFree();
53 | pub.Bind("tcp://127.0.0.1:" + freePort);
54 | sub.Connect("tcp://127.0.0.1:" + freePort);
55 |
56 | sub.Subscribe("*");
57 |
58 | var sw = Stopwatch.StartNew();
59 | {
60 | for (var i = 0; i < 50; i++)
61 | {
62 | pub.SendFrame("*"); // Ping.
63 |
64 | Console.Write("*");
65 | string topic;
66 | var gotTopic = sub.TryReceiveFrameString(TimeSpan.FromMilliseconds(100), out topic);
67 | string ping;
68 | var gotPing = sub.TryReceiveFrameString(TimeSpan.FromMilliseconds(100), out ping);
69 | if (gotTopic)
70 | {
71 | Console.Write("\n");
72 | break;
73 | }
74 | }
75 | }
76 | Console.WriteLine("Connected in {0} ms.", sw.ElapsedMilliseconds);
77 | }
78 | }
79 | NUnitUtils.PrintElapsedTime(swAll.Elapsed);
80 | }
81 |
82 | [Test]
83 | public void Send_Two_Types_Simultaneously_Over_Same_Transport()
84 | {
85 | NUnitUtils.PrintTestName();
86 | var sw = Stopwatch.StartNew();
87 |
88 | var cd1 = new CountdownEvent(5);
89 | var cd2 = new CountdownEvent(5);
90 | {
91 | var freePort = NUnitUtils.TcpPortFree();
92 |
93 | var pubSub1 = new SubjectNetMQ("tcp://127.0.0.1:" + freePort,
94 | loggerDelegate: Console.Write);
95 | var pubSub2 = new SubjectNetMQ("tcp://127.0.0.1:" + freePort,
96 | loggerDelegate: Console.Write);
97 | pubSub1.Subscribe(o =>
98 | {
99 | Assert.IsTrue(o.Name == "Bob");
100 | Console.Write("Test 1: Num={0}, Name={1}\n", o.Num, o.Name);
101 | cd1.Signal();
102 | },
103 | ex => { Console.WriteLine("Exception! {0}", ex.Message); });
104 |
105 | pubSub2.Subscribe(o =>
106 | {
107 | Assert.IsTrue(o.Name == "Bob");
108 | Console.Write("Test 2: Num={0}, Name={1}\n", o.Num, o.Name);
109 | cd2.Signal();
110 | },
111 | ex => { Console.WriteLine("Exception! {0}", ex.Message); });
112 |
113 | pubSub1.OnNext(new MyMessageStructType1(38, "Bob"));
114 | pubSub1.OnNext(new MyMessageStructType1(39, "Bob"));
115 | pubSub1.OnNext(new MyMessageStructType1(40, "Bob"));
116 | pubSub1.OnNext(new MyMessageStructType1(41, "Bob"));
117 | pubSub1.OnNext(new MyMessageStructType1(42, "Bob"));
118 |
119 | pubSub2.OnNext(new MyMessageStructType2(38, "Bob"));
120 | pubSub2.OnNext(new MyMessageStructType2(39, "Bob"));
121 | pubSub2.OnNext(new MyMessageStructType2(40, "Bob"));
122 | pubSub2.OnNext(new MyMessageStructType2(41, "Bob"));
123 | pubSub2.OnNext(new MyMessageStructType2(42, "Bob"));
124 | }
125 |
126 | if (cd1.Wait(GlobalTimeout.Timeout) == false) // Blocks until _countdown.Signal has been called.
127 | {
128 | Assert.Fail("Timed out, this test should complete in {0} seconds.", GlobalTimeout.Timeout.TotalSeconds);
129 | }
130 |
131 | if (cd2.Wait(GlobalTimeout.Timeout) == false) // Blocks until _countdown.Signal has been called.
132 | {
133 | Assert.Fail("Timed out, this test should complete in {0} seconds.", GlobalTimeout.Timeout.TotalSeconds);
134 | }
135 |
136 | NUnitUtils.PrintElapsedTime(sw.Elapsed);
137 | }
138 |
139 | [Test]
140 | public static void Speed_Test_Publisher_Subscriber()
141 | {
142 | NUnitUtils.PrintTestName();
143 |
144 | var sw = Stopwatch.StartNew();
145 | {
146 | var max = 100*1000;
147 |
148 | var cd = new CountdownEvent(max);
149 | var receivedNum = 0;
150 | {
151 | Console.Write("Speed test with {0} messages:\n", max);
152 |
153 | var freePort = NUnitUtils.TcpPortFree();
154 | var publisher = new PublisherNetMq("tcp://127.0.0.1:" + freePort,
155 | loggerDelegate: Console.Write);
156 | var subscriber = new SubscriberNetMq("tcp://127.0.0.1:" + freePort,
157 | loggerDelegate: Console.Write);
158 |
159 | subscriber.Subscribe(i =>
160 | {
161 | receivedNum++;
162 | cd.Signal();
163 | if (i%10000 == 0)
164 | {
165 | //Console.Write("*");
166 | }
167 | });
168 |
169 | sw.Start();
170 | for (var i = 0; i < max; i++)
171 | {
172 | publisher.OnNext(i);
173 | }
174 | }
175 |
176 | if (cd.Wait(GlobalTimeout.Timeout) == false) // Blocks until _countdown.Signal has been called.
177 | {
178 | Assert.Fail("\nTimed out, this test should complete in {0} seconds. receivedNum={1}",
179 | GlobalTimeout.Timeout.TotalSeconds,
180 | receivedNum);
181 | }
182 |
183 | // On my machine, achieved >120,000 messages per second.
184 | NUnitUtils.PrintElapsedTime(sw.Elapsed, max);
185 | }
186 | }
187 |
188 | [Test]
189 | public static void Speed_Test_Subject()
190 | {
191 | NUnitUtils.PrintTestName();
192 |
193 | var sw = Stopwatch.StartNew();
194 | {
195 | var max = 100*1000;
196 |
197 | var cd = new CountdownEvent(max);
198 | var receivedNum = 0;
199 | {
200 | Console.Write("Speed test with {0} messages:\n", max);
201 |
202 | var freePort = NUnitUtils.TcpPortFree();
203 | var pubSub = new SubjectNetMQ("tcp://127.0.0.1:" + freePort, loggerDelegate: Console.Write);
204 |
205 | pubSub.Subscribe(i =>
206 | {
207 | receivedNum++;
208 | cd.Signal();
209 | if (i%10000 == 0)
210 | {
211 | //Console.Write("*");
212 | }
213 | });
214 |
215 | sw.Start();
216 | for (var i = 0; i < max; i++)
217 | {
218 | pubSub.OnNext(i);
219 | }
220 | }
221 |
222 | if (cd.Wait(GlobalTimeout.Timeout) == false) // Blocks until _countdown.Signal has been called.
223 | {
224 | Assert.Fail("\nTimed out, this test should complete in {0} seconds. receivedNum={1}",
225 | GlobalTimeout.Timeout.TotalSeconds, receivedNum);
226 | }
227 |
228 | // On my machine, achieved >120,000 messages per second.
229 | NUnitUtils.PrintElapsedTime(sw.Elapsed, max);
230 | }
231 | }
232 |
233 | [Test]
234 | public void Test_Two_Subscribers()
235 | {
236 | NUnitUtils.PrintTestName();
237 | var sw = Stopwatch.StartNew();
238 |
239 | using (var pub = new PublisherSocket())
240 | {
241 | using (var sub1 = new SubscriberSocket())
242 | {
243 | using (var sub2 = new SubscriberSocket())
244 | {
245 | var freePort = NUnitUtils.TcpPortFree();
246 | pub.Bind("tcp://127.0.0.1:" + freePort);
247 | sub1.Connect("tcp://127.0.0.1:" + freePort);
248 | sub1.Subscribe("A");
249 | sub2.Connect("tcp://127.0.0.1:" + freePort);
250 | sub2.Subscribe("B");
251 |
252 | Thread.Sleep(500);
253 |
254 | var swInner = Stopwatch.StartNew();
255 | {
256 | pub.SendFrame("A\n"); // Ping.
257 | {
258 | string topic;
259 | var pass1 = sub1.TryReceiveFrameString(TimeSpan.FromMilliseconds(250), out topic);
260 | if (pass1)
261 | {
262 | Console.Write(topic);
263 | }
264 | else
265 | {
266 | Assert.Fail();
267 | }
268 | }
269 | pub.SendFrame("B\n"); // Ping.
270 | {
271 | string topic;
272 | var pass2 = sub2.TryReceiveFrameString(TimeSpan.FromMilliseconds(250), out topic);
273 | if (pass2)
274 | {
275 | Console.Write(topic);
276 | }
277 | else
278 | {
279 | Assert.Fail();
280 | }
281 | }
282 | }
283 | Console.WriteLine("Connected in {0} ms.", swInner.ElapsedMilliseconds);
284 | }
285 | }
286 | }
287 |
288 | NUnitUtils.PrintElapsedTime(sw.Elapsed);
289 | }
290 | }
291 | }
--------------------------------------------------------------------------------
/NetMQ.ReactiveExtensions.Tests/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 |
4 | // General Information about an assembly is controlled through the following
5 | // set of attributes. Change these attribute values to modify the information
6 | // associated with an assembly.
7 |
8 | [assembly: AssemblyTitle("NetMQ.ReactiveExtensions.Tests")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("NetMQ.ReactiveExtensions.Tests")]
13 | [assembly: AssemblyCopyright("Copyright ©Shane Tolmie 2016")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 |
21 | [assembly: ComVisible(false)]
22 |
23 | // The following GUID is for the ID of the typelib if this project is exposed to COM
24 |
25 | [assembly: Guid("e1e07a78-7831-4136-9d8d-c5f3ecb0224b")]
26 |
27 | // Version information for an assembly consists of the following four values:
28 | //
29 | // Major Version
30 | // Minor Version
31 | // Build Number
32 | // Revision
33 | //
34 | // You can specify all the values or you can default the Build and Revision Numbers
35 | // by using the '*' as shown below:
36 | // [assembly: AssemblyVersion("1.0.*")]
37 |
38 | [assembly: AssemblyVersion("0.9.4.7")]
39 | [assembly: AssemblyFileVersion("0.9.4.7")]
40 |
--------------------------------------------------------------------------------
/NetMQ.ReactiveExtensions.Tests/RouterDealer_Test1.cs:
--------------------------------------------------------------------------------
1 | // Behavior changed from 3.4.4.4 -> 4.0.0.0-rc5.
2 | // In any case, this code is exploratory, as router/dealer is not used in this project.
3 | /*using System;
4 | using System.Text;
5 | using System.Threading;
6 | using System.Threading.Tasks;
7 | using NetMQ.Sockets;
8 | using NUnit.Framework;
9 | // ReSharper disable InconsistentNaming
10 |
11 | namespace NetMQ.ReactiveExtensions.Tests
12 | {
13 | [Ignore]
14 | [TestFixture]
15 | public class RouterDealer_Test1
16 | {
17 | private void PrintFrames(string operationType, NetMQMessage message)
18 | {
19 | for (var i = 0; i < message.FrameCount; i++)
20 | {
21 | Console.WriteLine("{0} Socket : Frame[{1}] = {2}", operationType, i,
22 | message[i].ConvertToString());
23 | }
24 | }
25 |
26 | private void Client_ReceiveReady(object sender, NetMQSocketEventArgs e)
27 | {
28 | var hasmore = false;
29 | Msg msg;
30 | e.Socket.Receive(out hasmore);
31 | if (hasmore)
32 | {
33 | var result = e.Socket.ReceiveFrameString(out hasmore);
34 | Console.WriteLine("REPLY {0}", result);
35 | }
36 | }
37 |
38 | ///
39 | /// From Router-Dealer docs, see: https://github.com/zeromq/netmq/blob/master/docs/router-dealer.md
40 | ///
41 | [Test]
42 | public void Router_Dealer_Demonstrating_Messages_From_Subscribers_To_Publisher()
43 | {
44 | // NOTES
45 | // 1. Use ThreadLocal where each thread has
46 | // its own client DealerSocket to talk to server
47 | // 2. Each thread can send using it own socket
48 | // 3. Each thread socket is added to poller
49 |
50 | const int delay = 500; // millis
51 |
52 | var clientSocketPerThread = new ThreadLocal();
53 |
54 | using (var server = new RouterSocket("@tcp://127.0.0.1:5556"))
55 | {
56 | using (var poller = new NetMQPoller())
57 | {
58 | // Start some threads, each with its own DealerSocket
59 | // to talk to the server socket. Creates lots of sockets,
60 | // but no nasty race conditions no shared state, each
61 | // thread has its own socket, happy days.
62 | for (var i = 0; i < 4; i++)
63 | {
64 | Task.Factory.StartNew(state =>
65 | {
66 | DealerSocket client = null;
67 |
68 | if (!clientSocketPerThread.IsValueCreated)
69 | {
70 | client = new DealerSocket();
71 | client.Options.Identity =
72 | Encoding.Unicode.GetBytes(state.ToString());
73 | client.Connect("tcp://127.0.0.1:5556");
74 | client.ReceiveReady += Client_ReceiveReady;
75 | clientSocketPerThread.Value = client;
76 | poller.Add(client);
77 | }
78 | else
79 | {
80 | client = clientSocketPerThread.Value;
81 | }
82 |
83 | while (true)
84 | {
85 | var messageToServer = new NetMQMessage();
86 | messageToServer.AppendEmptyFrame();
87 | messageToServer.Append(state.ToString());
88 | Console.WriteLine("======================================");
89 | Console.WriteLine(" OUTGOING MESSAGE TO SERVER ");
90 | Console.WriteLine("======================================");
91 | PrintFrames("Client Sending", messageToServer);
92 | client.SendMultipartMessage(messageToServer);
93 | Thread.Sleep(delay);
94 | }
95 | },
96 | string.Format("client {0}", i),
97 | TaskCreationOptions.LongRunning);
98 | }
99 |
100 | // start the poller
101 | poller.RunAsync();
102 |
103 | // server loop
104 | for (var i = 0; i < 6; i++)
105 | {
106 | var clientMessage = server.ReceiveMessage();
107 | Console.WriteLine("======================================");
108 | Console.WriteLine(" INCOMING CLIENT MESSAGE FROM CLIENT ");
109 | Console.WriteLine("======================================");
110 | PrintFrames("Server receiving", clientMessage);
111 | if (clientMessage.FrameCount == 3)
112 | {
113 | var clientAddress = clientMessage[0];
114 | var clientOriginalMessage = clientMessage[2].ConvertToString();
115 | var response = string.Format("{0} back from server {1}",
116 | clientOriginalMessage,
117 | DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
118 | var messageToClient = new NetMQMessage();
119 | messageToClient.Append(clientAddress);
120 | messageToClient.AppendEmptyFrame();
121 | messageToClient.Append(response);
122 | server.SendMultipartMessage(messageToClient);
123 | }
124 | }
125 | }
126 | }
127 | }
128 | }
129 | }*/
--------------------------------------------------------------------------------
/NetMQ.ReactiveExtensions.Tests/RouterDealer_Test2.cs:
--------------------------------------------------------------------------------
1 | // Behavior changed from 3.4.4.4 -> 4.0.0.0-rc5.
2 | // In any case, this code is exploratory, as router/dealer is not used in this project.
3 | /*using System;
4 | using System.Text;
5 | using System.Threading;
6 | using System.Threading.Tasks;
7 | using NetMQ.Sockets;
8 | using NUnit.Framework;
9 | // ReSharper disable InconsistentNaming
10 |
11 | namespace NetMQ.ReactiveExtensions.Tests
12 | {
13 | [Ignore]
14 | [TestFixture]
15 | public class RouterDealer_Test2
16 | {
17 | private void PrintFrames(string operationType, NetMQMessage message)
18 | {
19 | for (var i = 0; i < message.FrameCount; i++)
20 | {
21 | Console.WriteLine("{0} Socket : Frame[{1}] = {2}", operationType, i,
22 | message[i].ConvertToString());
23 | }
24 | }
25 |
26 | private void Client_ReceiveReady(object sender, NetMQSocketEventArgs e)
27 | {
28 | var hasmore = false;
29 | e.Socket.Receive(out hasmore);
30 | if (hasmore)
31 | {
32 | var result = e.Socket.ReceiveFrameString(out hasmore);
33 | Console.WriteLine("REPLY {0}", result);
34 | }
35 | }
36 |
37 | [Test]
38 | public void Messages_From_Dealer_To_Router()
39 | {
40 | var maxMessage = 5;
41 | var cd = new CountdownEvent(maxMessage);
42 |
43 | Console.Write("Test sending message from subscribers (dealer) to publisher(router).\n");
44 |
45 | using (var publisher = new RouterSocket())
46 | using (var subscriber = new DealerSocket())
47 | using (var poller = new NetMQPoller {subscriber})
48 | {
49 | var port = publisher.BindRandomPort("tcp://*");
50 | subscriber.Connect("tcp://127.0.0.1:" + port);
51 | subscriber.ReceiveReady += (sender, e) =>
52 | {
53 | var strs = e.Socket.ReceiveMultipartStrings();
54 | foreach (var str in strs)
55 | {
56 | Console.WriteLine(str);
57 | }
58 | cd.Signal();
59 | };
60 | var clientId = Encoding.Unicode.GetBytes("ClientId");
61 | subscriber.Options.Identity = clientId;
62 |
63 | const string request = "GET /\r\n";
64 |
65 | const string response = "HTTP/1.0 200 OK\r\n" +
66 | "Content-Type: text/plain\r\n" +
67 | "\r\n" +
68 | "Hello, World!";
69 |
70 | subscriber.SendFrame(request);
71 |
72 | var serverId = publisher.ReceiveFrameBytes();
73 | Assert.AreEqual(request, publisher.ReceiveFrameString());
74 |
75 | for (var i = 0; i < maxMessage; i++)
76 | {
77 | publisher.SendMoreFrame(serverId).SendFrame(response);
78 | }
79 |
80 | poller.RunAsync();
81 |
82 | if (cd.Wait(TimeSpan.FromSeconds(10)) == false) // Blocks until _countdown.Signal has been called.
83 | {
84 | Assert.Fail("Timed out, this test should complete in less than 10 seconds.");
85 | }
86 | }
87 | }
88 |
89 | [Test]
90 | public void Messages_From_Router_To_Dealer()
91 | {
92 | Console.Write("Test sending message from publisher(router) to subscribers (dealer).\n");
93 |
94 | var maxMessage = 5;
95 | var cd = new CountdownEvent(maxMessage);
96 |
97 | string endpoint;
98 |
99 | using (var publisher = new RouterSocket())
100 | using (var subscriber = new DealerSocket())
101 | using (var poller = new NetMQPoller {subscriber})
102 | {
103 | publisher.Bind("tcp://127.0.0.1:0");
104 | endpoint = publisher.Options.LastEndpoint;
105 |
106 | subscriber.Connect(endpoint);
107 | subscriber.ReceiveReady += (sender, e) =>
108 | {
109 | var strs = e.Socket.ReceiveMultipartStrings();
110 | foreach (var str in strs)
111 | {
112 | Console.WriteLine("Subscribe: " + str);
113 | }
114 | cd.Signal();
115 | };
116 | var clientId = Encoding.Unicode.GetBytes("ClientId");
117 | subscriber.Options.Identity = clientId;
118 |
119 | const string request = "Ping";
120 |
121 | // Work around "feature" of router/dealer: the publisher does not know the subscriber exists, until it
122 | // sends at least one message which makes it necessary to open the connection. I believe this is a
123 | // low-level feature of the TCP/IP transport.
124 | subscriber.SendFrame(request); // Ping.
125 |
126 | var serverId = publisher.ReceiveFrameBytes();
127 | Assert.AreEqual(request, publisher.ReceiveFrameString());
128 |
129 | for (var i = 0; i < maxMessage; i++)
130 | {
131 | var msg = string.Format("[message: {0}]", i);
132 | Console.Write("Publish: {0}\n", msg);
133 | publisher.SendMoreFrame(serverId).SendFrame(msg);
134 | }
135 |
136 | poller.RunAsync();
137 |
138 | if (cd.Wait(TimeSpan.FromSeconds(10)) == false) // Blocks until _countdown.Signal has been called.
139 | {
140 | Assert.Fail("Timed out, this test should complete in less than 10 seconds.");
141 | }
142 | }
143 | }
144 |
145 | [Test]
146 | public void Messages_From_Router_To_Dealer_With_Subscription()
147 | {
148 | Console.Write("Test sending message from publisher(router) to subscribers (dealer).\n");
149 |
150 | var maxMessage = 5;
151 | var cd = new CountdownEvent(maxMessage);
152 |
153 | string endpoint;
154 |
155 | using (var publisher = new RouterSocket())
156 | using (var subscriber = new DealerSocket())
157 | using (var poller = new NetMQPoller {subscriber})
158 | {
159 | publisher.Bind("tcp://127.0.0.1:0");
160 | endpoint = publisher.Options.LastEndpoint;
161 |
162 | subscriber.Connect(endpoint);
163 | subscriber.ReceiveReady += (sender, e) =>
164 | {
165 | var strs = e.Socket.ReceiveMultipartStrings();
166 | foreach (var str in strs)
167 | {
168 | Console.WriteLine("Subscribe: " + str);
169 | }
170 | cd.Signal();
171 | };
172 | var clientId = Encoding.Unicode.GetBytes("ClientIdTheIsLongerThen32BytesForSureAbsolutelySure");
173 | subscriber.Options.Identity = clientId;
174 |
175 | const string request = "Ping";
176 |
177 | // Work around "feature" of router/dealer: the publisher does not know the subscriber exists, until it
178 | // sends at least one message which makes it necessary to open the connection. I believe this is a
179 | // low-level feature of the TCP/IP transport.
180 | subscriber.SendFrame(request); // Ping.
181 |
182 | var serverId = publisher.ReceiveFrameBytes();
183 | //Assert.AreEqual(request, publisher.ReceiveFrameString());
184 |
185 | for (var i = 0; i < maxMessage; i++)
186 | {
187 | var msg = string.Format("[message: {0}]", i);
188 | Console.Write("Publish: {0}\n", msg);
189 | publisher.SendMoreFrame(serverId).SendFrame(msg);
190 | //publisher.SendMoreFrame("").SendFrame(msg);
191 | }
192 |
193 | poller.RunAsync();
194 |
195 | if (cd.Wait(TimeSpan.FromSeconds(10)) == false) // Blocks until _countdown.Signal has been called.
196 | {
197 | Assert.Fail("Timed out, this test should complete in less than 10 seconds.");
198 | }
199 | }
200 | }
201 |
202 | ///
203 | /// Intent: Modified from example in Router-Dealer docs, see:
204 | /// https://github.com/zeromq/netmq/blob/master/docs/router-dealer.md
205 | ///
206 | [Test]
207 | public void Router_Dealer_Demonstrating_Messages_From_Publisher_To_Subscribers()
208 | {
209 | // NOTES
210 | // 1. Use ThreadLocal where each thread has
211 | // its own client DealerSocket to talk to server
212 | // 2. Each thread can send using it own socket
213 | // 3. Each thread socket is added to poller
214 |
215 | const int delay = 500; // millis
216 |
217 | var clientSocketPerThread = new ThreadLocal();
218 |
219 | string endpoint;
220 |
221 | using (var server = new RouterSocket("@tcp://127.0.0.1:0"))
222 | // If we specify 0, it will choose a random port for us.
223 | {
224 | endpoint = server.Options.LastEndpoint; // Lets us know which port was chosen.
225 | Console.Write("Last endpoint, including port: {0}\n", server.Options.LastEndpoint);
226 | using (var poller = new NetMQPoller())
227 | {
228 | // Start some threads, each with its own DealerSocket
229 | // to talk to the server socket. Creates lots of sockets,
230 | // but no nasty race conditions no shared state, each
231 | // thread has its own socket, happy days.
232 | for (var i = 0; i < 4; i++)
233 | {
234 | Task.Factory.StartNew(state =>
235 | {
236 | DealerSocket client = null;
237 |
238 | if (!clientSocketPerThread.IsValueCreated)
239 | {
240 | client = new DealerSocket();
241 | client.Options.Identity =
242 | Encoding.Unicode.GetBytes(state.ToString());
243 | client.Connect(endpoint);
244 | //client.ReceiveReady += Client_ReceiveReady;
245 | clientSocketPerThread.Value = client;
246 | poller.Add(client);
247 | }
248 | else
249 | {
250 | client = clientSocketPerThread.Value;
251 | }
252 |
253 | Thread.Sleep(3000); // Wait until server is up.
254 | client.SendFrame("Ping");
255 |
256 | while (true)
257 | {
258 | Console.Write("Client {0}: Waiting for ping...\n", i);
259 | // Work around "feature" of router/dealer: the publisher does not know the subscriber exists, until it
260 | // sends at least one message which makes it necessary to open the connection. I believe this is a
261 | // low-level feature of the TCP/IP transport.
262 |
263 | var clientMessage = client.ReceiveMultipartMessage();
264 | Console.WriteLine("======================================");
265 | Console.WriteLine(" INCOMING CLIENT MESSAGE FROM SERVER");
266 | Console.WriteLine("======================================");
267 | PrintFrames("Server receiving", clientMessage);
268 | }
269 | },
270 | string.Format("client {0}", i),
271 | TaskCreationOptions.LongRunning);
272 | }
273 |
274 | // start the poller
275 | poller.RunAsync();
276 |
277 | // server loop
278 | var sequenceNo = 0;
279 | for (var i = 0; i < 10; i++)
280 | {
281 | var messageToServer = new NetMQMessage();
282 | messageToServer.AppendEmptyFrame();
283 | messageToServer.Append(sequenceNo.ToString());
284 | sequenceNo++;
285 | Console.WriteLine("======================================");
286 | Console.WriteLine(" OUTGOING MESSAGE {0} TO CLIENTS ", sequenceNo);
287 | Console.WriteLine("======================================");
288 | PrintFrames("Client Sending", messageToServer);
289 | server.SendMultipartMessage(messageToServer);
290 | Thread.Sleep(delay);
291 | }
292 |
293 | Console.WriteLine("Finished.");
294 | }
295 | }
296 | }
297 | }
298 | }*/
--------------------------------------------------------------------------------
/NetMQ.ReactiveExtensions.Tests/SerializeExceptionToXml_Tests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Xml;
7 | using System.Xml.Linq;
8 | using NUnit.Framework;
9 |
10 | // ReSharper disable InconsistentNaming
11 |
12 | namespace NetMQ.ReactiveExtensions.Tests
13 | {
14 | public class SerializeExceptionToXml_Tests
15 | {
16 |
17 | [TestFixture]
18 | public static class Serialize_Exception_To_Xml_Tests
19 | {
20 | [Test]
21 | public static void Convert_Exception_To_XML()
22 | {
23 | DivideByZeroException outerException = new DivideByZeroException();
24 | ExceptionXElement xmlRaw;
25 | {
26 | try
27 | {
28 | var path = File.ReadAllLines("file does not exist");
29 | }
30 | catch (Exception ex)
31 | {
32 | outerException = new DivideByZeroException("Outer exception", ex);
33 | }
34 |
35 | xmlRaw = new ExceptionXElement(outerException);
36 | }
37 |
38 | var xml = xmlRaw.ToString();
39 |
40 | Assert.IsTrue(xml.ToLower().Contains("exception"));
41 | // TODO: Comment this in once .NET Core 1.1 is released.
42 | // Assert.IsTrue(xml.ToLower().Contains("DivideByZeroException"));
43 | Assert.IsTrue(xml.Contains("<"));
44 | Assert.IsTrue(xml.Contains(">"));
45 | }
46 | }
47 | }
48 | }
49 |
50 |
--------------------------------------------------------------------------------
/NetMQ.ReactiveExtensions.Tests/SerializeViaProtoBuf_Tests.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 | using NUnit.Framework;
3 | // ReSharper disable InconsistentNaming
4 |
5 | namespace NetMQ.ReactiveExtensions.Tests
6 | {
7 | [TestFixture]
8 | public static class SerializeViaProtoBuf_Tests
9 | {
10 | [Test]
11 | public static void Should_be_able_to_serialize_a_decimal()
12 | {
13 | NUnitUtils.PrintTestName();
14 |
15 | Stopwatch sw = Stopwatch.StartNew();
16 |
17 | const decimal x = 123.4m;
18 | var rawBytes = x.SerializeProtoBuf();
19 | var original = rawBytes.DeserializeProtoBuf();
20 | Assert.AreEqual(x, original);
21 |
22 | NUnitUtils.PrintElapsedTime(sw.Elapsed);
23 | }
24 |
25 | [Test]
26 | public static void Should_be_able_to_serialize_a_string()
27 | {
28 | NUnitUtils.PrintTestName();
29 |
30 | Stopwatch sw = Stopwatch.StartNew();
31 |
32 | const string x = "Hello";
33 | var rawBytes = x.SerializeProtoBuf();
34 | var original = rawBytes.DeserializeProtoBuf();
35 | Assert.AreEqual(x, original);
36 |
37 | NUnitUtils.PrintElapsedTime(sw.Elapsed);
38 | }
39 |
40 | [Test]
41 | public static void Should_be_able_to_serialize_an_int()
42 | {
43 | NUnitUtils.PrintTestName();
44 |
45 | Stopwatch sw = Stopwatch.StartNew();
46 |
47 | const int x = 42;
48 | var rawBytes = x.SerializeProtoBuf();
49 | var original = rawBytes.DeserializeProtoBuf();
50 | Assert.AreEqual(x, original);
51 |
52 | NUnitUtils.PrintElapsedTime(sw.Elapsed);
53 | }
54 | }
55 | }
--------------------------------------------------------------------------------
/NetMQ.ReactiveExtensions.Tests/app.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/NetMQ.ReactiveExtensions.Tests/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/NetMQ.ReactiveExtensions.Tests/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1.0.0-*",
3 | "dependencies": {
4 | "NetMQ.ReactiveExtensions": "1.0.0-*"
5 | },
6 |
7 | "testRunner": "nunit",
8 |
9 | "frameworks": {
10 | "netcoreapp1.0": {
11 | "dependencies": {
12 | "Microsoft.NETCore.App": {
13 | "type": "platform",
14 | "version": "1.0.1"
15 | },
16 | "NUnit": "3.5.0",
17 | "dotnet-test-nunit": "3.4.0-beta-3"
18 | },
19 | "imports": "dnxcore50"
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/NetMQ.ReactiveExtensions.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 14
4 | VisualStudioVersion = 14.0.25420.1
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "NetMQ.ReactiveExtensions", "NetMQ.ReactiveExtensions\NetMQ.ReactiveExtensions.xproj", "{1F0D6712-7390-446F-9DCC-E86480360496}"
7 | EndProject
8 | Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "NetMQ.ReactiveExtensions.SamplePublisher", "NetMQ.ReactiveExtensions.SamplePublisher\NetMQ.ReactiveExtensions.SamplePublisher.xproj", "{72BBF61C-B5AC-49C5-B4F6-0EEEB97A98CF}"
9 | EndProject
10 | Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "NetMQ.ReactiveExtensions.SampleSubscriber", "NetMQ.ReactiveExtensions.SampleSubscriber\NetMQ.ReactiveExtensions.SampleSubscriber.xproj", "{F8CAD08F-532C-438B-9B7E-218DD6904840}"
11 | EndProject
12 | Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "NetMQ.ReactiveExtensions.Tests", "NetMQ.ReactiveExtensions.Tests\NetMQ.ReactiveExtensions.Tests.xproj", "{7D1CAE81-2573-427B-A493-41D02A11EB30}"
13 | EndProject
14 | Global
15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
16 | Debug|Any CPU = Debug|Any CPU
17 | Release|Any CPU = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
20 | {1F0D6712-7390-446F-9DCC-E86480360496}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {1F0D6712-7390-446F-9DCC-E86480360496}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {1F0D6712-7390-446F-9DCC-E86480360496}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {1F0D6712-7390-446F-9DCC-E86480360496}.Release|Any CPU.Build.0 = Release|Any CPU
24 | {72BBF61C-B5AC-49C5-B4F6-0EEEB97A98CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
25 | {72BBF61C-B5AC-49C5-B4F6-0EEEB97A98CF}.Debug|Any CPU.Build.0 = Debug|Any CPU
26 | {72BBF61C-B5AC-49C5-B4F6-0EEEB97A98CF}.Release|Any CPU.ActiveCfg = Release|Any CPU
27 | {72BBF61C-B5AC-49C5-B4F6-0EEEB97A98CF}.Release|Any CPU.Build.0 = Release|Any CPU
28 | {F8CAD08F-532C-438B-9B7E-218DD6904840}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
29 | {F8CAD08F-532C-438B-9B7E-218DD6904840}.Debug|Any CPU.Build.0 = Debug|Any CPU
30 | {F8CAD08F-532C-438B-9B7E-218DD6904840}.Release|Any CPU.ActiveCfg = Release|Any CPU
31 | {F8CAD08F-532C-438B-9B7E-218DD6904840}.Release|Any CPU.Build.0 = Release|Any CPU
32 | {7D1CAE81-2573-427B-A493-41D02A11EB30}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33 | {7D1CAE81-2573-427B-A493-41D02A11EB30}.Debug|Any CPU.Build.0 = Debug|Any CPU
34 | {7D1CAE81-2573-427B-A493-41D02A11EB30}.Release|Any CPU.ActiveCfg = Release|Any CPU
35 | {7D1CAE81-2573-427B-A493-41D02A11EB30}.Release|Any CPU.Build.0 = Release|Any CPU
36 | EndGlobalSection
37 | GlobalSection(SolutionProperties) = preSolution
38 | HideSolutionNode = FALSE
39 | EndGlobalSection
40 | EndGlobal
41 |
--------------------------------------------------------------------------------
/NetMQ.ReactiveExtensions/.gitignore:
--------------------------------------------------------------------------------
1 | *.exe
2 | /NuGet.Build.bat
3 |
--------------------------------------------------------------------------------
/NetMQ.ReactiveExtensions/AnonymousDisposable.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 |
4 | namespace NetMQ.ReactiveExtensions
5 | {
6 | public sealed class AnonymousDisposable : IDisposable
7 | {
8 | private readonly Action _action;
9 | private int _disposed = 0;
10 |
11 | public AnonymousDisposable(Action action)
12 | {
13 | _action = action;
14 | }
15 |
16 | public void Dispose()
17 | {
18 | if (Interlocked.Exchange(ref _disposed, 1) == 0)
19 | {
20 | _action();
21 | }
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/NetMQ.ReactiveExtensions/INetMqTransportShared.cs:
--------------------------------------------------------------------------------
1 | using NetMQ.Sockets;
2 |
3 | namespace NetMQ.ReactiveExtensions
4 | {
5 | public interface INetMqTransportShared
6 | {
7 | ///
8 | /// Intent: Get a publisher socket. If the same port is already opened, return it.
9 | ///
10 | /// ZeroMQ address, e.g. "tcp://127.0.0.1:56001".
11 | /// A publisher socket. If we already have one open, it will return the existing one.
12 | PublisherSocket GetSharedPublisherSocket(string addressZeroMq);
13 |
14 | ///
15 | /// Intent: Get a subscriber socket. If the same port is already opened, return it.
16 | ///
17 | /// ZeroMQ address, e.g. "tcp://127.0.0.1:56001".
18 | /// A subscriber socket. If we already have one open, it will return the existing one.
19 | SubscriberSocket GetSharedSubscriberSocket(string addressZeroMq);
20 | }
21 | }
--------------------------------------------------------------------------------
/NetMQ.ReactiveExtensions/IPublisherNetMq.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace NetMQ.ReactiveExtensions
4 | {
5 | public interface IPublisherNetMq
6 | {
7 | string AddressZeroMq { get; set; }
8 | string SubscriberFilterName { get; set; }
9 |
10 | void Dispose();
11 | void OnCompleted();
12 | void OnError(Exception exception);
13 | void OnNext(T message);
14 | }
15 | }
--------------------------------------------------------------------------------
/NetMQ.ReactiveExtensions/ISubjectNetMQ.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace NetMQ.ReactiveExtensions
4 | {
5 | ///
6 | /// Intent: Send messages anywhere on the network using Reactive Extensions (RX). Uses NetMQ as the transport layer.
7 | /// The API is a drop-in replacement for ISubject of T from Reactive Extensions, see
8 | /// https://github.com/NetMQ/NetMQ.ReactiveExtensions.
9 | ///
10 | ///
11 | public interface ISubjectNetMQ : IDisposable
12 | {
13 | ///
14 | /// Intent: Subscriber Filter name. Defaults to the type name T. Allows many types to get sent over the same
15 | /// transport connection.
16 | ///
17 | string SubscriberFilterName { get; }
18 |
19 | ///
20 | /// Intent: The current endpoint address specified in the constructor, e.g. "tcp://127.0.0.1:56001".
21 | ///
22 | string AddressZeroMq { get; }
23 | }
24 |
25 | ///
26 | /// Intent: Control when the network connection is created.
27 | ///
28 | public enum WhenToCreateNetworkConnection
29 | {
30 | ///
31 | /// Intent: Default mode. The network connection is lazily created on first use.
32 | ///
33 | LazyConnectOnFirstUse = 0,
34 |
35 | ///
36 | /// Intent: Bind to the publisher right now.
37 | ///
38 | SetupPublisherTransportNow = 1,
39 |
40 | ///
41 | /// Intent: Connect to the subscriber right now.
42 | ///
43 | SetupSubscriberTransportNow = 2,
44 | }
45 | }
--------------------------------------------------------------------------------
/NetMQ.ReactiveExtensions/NetMQ.ReactiveExtensions.xproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 14.0
5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
6 |
7 |
8 |
9 | 1f0d6712-7390-446f-9dcc-e86480360496
10 | NetMQ.ReactiveExtensions
11 | ..\artifacts\obj\$(MSBuildProjectName)
12 | .\bin\
13 |
14 |
15 | 2.0
16 |
17 |
18 |
--------------------------------------------------------------------------------
/NetMQ.ReactiveExtensions/NetMqTransportShared.cs:
--------------------------------------------------------------------------------
1 | #region
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Diagnostics;
5 | using System.Threading;
6 | using NetMQ.Monitoring;
7 | using NetMQ.Sockets;
8 | // ReSharper disable ConvertToAutoProperty
9 | #endregion
10 |
11 | // ReSharper disable ConvertIfStatementToNullCoalescingExpression
12 | // ReSharper disable InvertIf
13 |
14 | namespace NetMQ.ReactiveExtensions
15 | {
16 | ///
17 | /// Intent: We want to have one transport shared among all publishers and subscribers, in this process, if they
18 | /// happen to use the same TCP/IP port.
19 | ///
20 | public class NetMqTransportShared : INetMqTransportShared
21 | {
22 | private static volatile NetMqTransportShared _instance;
23 | private static readonly object _syncRoot = new object();
24 |
25 | private NetMqTransportShared(Action loggerDelegate = null)
26 | {
27 | this._loggerDelegate = loggerDelegate;
28 | }
29 |
30 | #region HighwaterMark
31 | private int _highwaterMark = 2000 * 1000;
32 |
33 | ///
34 | /// Intent: Default amount of messages to queue in memory before discarding more.
35 | ///
36 | public int HighwaterMark
37 | {
38 | get { return _highwaterMark; }
39 | set { _highwaterMark = value; }
40 | }
41 | #endregion
42 |
43 | private readonly Action _loggerDelegate;
44 |
45 | ///
46 | /// Intent: Singleton.
47 | ///
48 | public static NetMqTransportShared Instance(Action loggerDelegate = null)
49 | {
50 | if (_instance == null)
51 | {
52 | lock (_syncRoot)
53 | {
54 | if (_instance == null)
55 | {
56 | _instance = new NetMqTransportShared(loggerDelegate);
57 | }
58 | }
59 | }
60 |
61 | return _instance;
62 | }
63 |
64 | #region Get Publisher Socket (if it's already been opened, we reuse it).
65 | ///
66 | /// Intent: See interface.
67 | ///
68 | public PublisherSocket GetSharedPublisherSocket(string addressZeroMq)
69 | {
70 | lock (_initializePublisherLock)
71 | {
72 | if (_dictAddressZeroMqToPublisherSocket.ContainsKey(addressZeroMq) == false)
73 | {
74 | _dictAddressZeroMqToPublisherSocket[addressZeroMq] = GetNewPublisherSocket(addressZeroMq);
75 | }
76 | return _dictAddressZeroMqToPublisherSocket[addressZeroMq];
77 | }
78 | }
79 |
80 | #region Get publisher socket.
81 | private readonly object _initializePublisherLock = new object();
82 | private readonly ManualResetEvent _publisherReadySignal = new ManualResetEvent(false);
83 | readonly Dictionary _dictAddressZeroMqToPublisherSocket = new Dictionary();
84 |
85 | private PublisherSocket GetNewPublisherSocket(string addressZeroMq)
86 | {
87 | PublisherSocket publisherSocket;
88 | {
89 | _loggerDelegate?.Invoke(string.Format("Publisher socket binding to: {0}\n", addressZeroMq));
90 |
91 | publisherSocket = new PublisherSocket();
92 |
93 | // Corner case: wait until publisher socket is ready (see code below that waits for
94 | // "_publisherReadySignal").
95 | NetMQMonitor monitor;
96 | {
97 | // Must ensure that we have a unique monitor name for every instance of this class.
98 | string endPoint = string.Format("inproc://#SubjectNetMQ#Publisher#{0}#{1}", addressZeroMq, Guid.NewGuid().ToString());
99 | monitor = new NetMQMonitor(publisherSocket,
100 | endPoint,
101 | SocketEvents.Accepted | SocketEvents.Listening
102 | );
103 | monitor.Accepted += Publisher_Event_Accepted;
104 | monitor.Listening += Publisher_Event_Listening;
105 | monitor.StartAsync();
106 | }
107 |
108 | publisherSocket.Options.SendHighWatermark = this.HighwaterMark;
109 | try
110 | {
111 | publisherSocket.Bind(addressZeroMq);
112 | }
113 | catch (NetMQException ex)
114 | {
115 | // This is usually because the address is in use.
116 | throw new Exception(string.Format("Error E56874. Cannot bind publisher to '{0}'. 95% probability that this is caused by trying to bind a publisher to a port already in use by another process. To fix, choose a unique publisher port for this process. For more on this error, see 'Readme.md' (or the GitHub homepage for NetMQ.ReactiveExtensions).", addressZeroMq), ex);
117 | }
118 |
119 | // Corner case: wait until publisher socket is ready (see code below that sets "_publisherReadySignal").
120 | {
121 | Stopwatch sw = Stopwatch.StartNew();
122 | _publisherReadySignal.WaitOne(TimeSpan.FromMilliseconds(3000));
123 | _loggerDelegate?.Invoke(string.Format("Publisher: Waited {0} ms for binding.\n", sw.ElapsedMilliseconds));
124 | }
125 | {
126 | monitor.Accepted -= Publisher_Event_Accepted;
127 | monitor.Listening -= Publisher_Event_Listening;
128 | // Current issue with NegMQ: Cannot stop or dispose monitor, or else it stops the parent socket.
129 | //monitor.Stop();
130 | //monitor.Dispose();
131 | }
132 | }
133 |
134 | // Otherwise, the first item we publish may get missed by the subscriber. 500 milliseconds consistently works
135 | // locally, but occasionally fails on the AppVeyor build server. 650 milliseconds is optimal.
136 | using (EventWaitHandle wait = new ManualResetEvent(false))
137 | {
138 | // Cannot use Thread.Sleep() here, as this is incompatible with .NET Core 1.0, Windows 8.0, 8.1, and 10.
139 | wait.WaitOne(TimeSpan.FromMilliseconds(650));
140 | }
141 |
142 | return publisherSocket;
143 | }
144 |
145 | private void Publisher_Event_Listening(object sender, NetMQMonitorSocketEventArgs e)
146 | {
147 | _loggerDelegate?.Invoke(string.Format("Publisher event: {0}\n", e.SocketEvent));
148 | _publisherReadySignal.Set();
149 | }
150 |
151 | private void Publisher_Event_Accepted(object sender, NetMQMonitorSocketEventArgs e)
152 | {
153 | _loggerDelegate?.Invoke(string.Format("Publisher event: {0}\n", e.SocketEvent));
154 | _publisherReadySignal.Set();
155 | }
156 | #endregion
157 | #endregion
158 |
159 | #region Get Subscriber socket (if it's already been opened, we reuse it).
160 | private readonly object _initializeSubscriberLock = new object();
161 | ///
162 | /// Intent: See interface.
163 | ///
164 | public SubscriberSocket GetSharedSubscriberSocket(string addressZeroMq)
165 | {
166 | lock (_initializeSubscriberLock)
167 | {
168 | // Must return a unique subscriber for every new ISubject of T.
169 | var subscriberSocket = new SubscriberSocket();
170 | subscriberSocket.Options.ReceiveHighWatermark = this.HighwaterMark;
171 | subscriberSocket.Connect(addressZeroMq);
172 | return subscriberSocket;
173 | }
174 | }
175 | #endregion
176 | }
177 | }
--------------------------------------------------------------------------------
/NetMQ.ReactiveExtensions/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following set of attributes. Change these attribute
6 | // values to modify the information associated with an assembly.
7 | [assembly: AssemblyTitle("NetMQ.ReactiveExtensions")]
8 | [assembly: AssemblyDescription("Effortlessly send messages anywhere on the network using Reactive Extensions (RX). Uses NetMQ as the transport layer. See https://github.com/NetMQ/NetMQ.ReactiveExtensions/.")]
9 | [assembly: AssemblyConfiguration("")]
10 | [assembly: AssemblyCompany("")]
11 | [assembly: AssemblyProduct("NetMQ.ReactiveExtensions")]
12 | [assembly: AssemblyCopyright("Copyright ©Shane Tolmie and Contributors 2016")]
13 | [assembly: AssemblyTrademark("")]
14 | [assembly: AssemblyCulture("")]
15 |
16 | // Setting ComVisible to false makes the types in this assembly not visible
17 | // to COM components. If you need to access a type in this assembly from
18 | // COM, set the ComVisible attribute to true on that type.
19 | [assembly: ComVisible(false)]
20 |
21 | // The following GUID is for the ID of the typelib if this project is exposed to COM
22 | [assembly: Guid("53c7e7c2-0c41-4123-a555-27ec4aa7f275")]
23 |
24 | // Version information for an assembly consists of the following four values:
25 | //
26 | // Major Version
27 | // Minor Version
28 | // Build Number
29 | // Revision
30 | //
31 | // You can specify all the values or you can default the Build and Revision Numbers
32 | // by using the '*' as shown below:
33 | // [assembly: AssemblyVersion("1.0.*")]
34 | [assembly: AssemblyVersion("0.9.4.7")]
35 | [assembly: AssemblyFileVersion("0.9.4.7")]
36 |
--------------------------------------------------------------------------------
/NetMQ.ReactiveExtensions/PublisherNetMq.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading;
6 | using NetMQ.Sockets;
7 |
8 | namespace NetMQ.ReactiveExtensions
9 | {
10 | ///
11 | /// Intent: Publisher.
12 | ///
13 | public class PublisherNetMq : ISubjectNetMQ, IObserver, IPublisherNetMq
14 | {
15 | private readonly CancellationTokenSource _cancellationTokenSource;
16 | private readonly Action _loggerDelegate;
17 | private readonly WhenToCreateNetworkConnection _whenToCreateNetworkConnection;
18 |
19 | public string SubscriberFilterName { get; set; }
20 |
21 | public string AddressZeroMq { get; set; }
22 |
23 | ///
24 | /// Intent: Create a new publisher, using NetMQ as the transport layer.
25 | ///
26 | /// ZeroMq address to bind to, e.g. "tcp://localhost:56001
27 | /// Filter name on receiver. If you do not set this, it will default to the
28 | /// type name of T, and everything will just work.
29 | ///
30 | ///
31 | ///
32 | ///
33 | public PublisherNetMq(string addressZeroMq, string subscriberFilterName = null, WhenToCreateNetworkConnection whenToCreateNetworkConnection = WhenToCreateNetworkConnection.SetupPublisherTransportNow, CancellationTokenSource cancellationTokenSource = default(CancellationTokenSource), Action loggerDelegate = null)
34 | {
35 | _cancellationTokenSource = cancellationTokenSource;
36 | _loggerDelegate = loggerDelegate;
37 | _whenToCreateNetworkConnection = whenToCreateNetworkConnection;
38 | _cancellationTokenSource = cancellationTokenSource;
39 |
40 | if (subscriberFilterName == null)
41 | {
42 | // Unfortunately, the subscriber never scans more than the first 32 characters of the filter, so we must
43 | // trim to less than this length, but also ensure that it's unique. This ensures that if we get two
44 | // classnames of 50 characters that only differ by the last character, everything will still work and we
45 | // won't get crossed subscriptions.
46 | SubscriberFilterName = typeof(T).Name;
47 |
48 | if (SubscriberFilterName.Length > 32)
49 | {
50 | string uniqueHashCode = string.Format("{0:X8}", SubscriberFilterName.GetHashCode());
51 |
52 | SubscriberFilterName = SubscriberFilterName.Substring(0, Math.Min(SubscriberFilterName.Length, 24));
53 | SubscriberFilterName += uniqueHashCode;
54 | }
55 | if (SubscriberFilterName.Length > 32)
56 | {
57 | throw new Exception("Error E38742. Internal error. Logically, at this point subscription length could never be longer than 32 characters.");
58 | }
59 | }
60 |
61 | if (string.IsNullOrEmpty(Thread.CurrentThread.Name) == true)
62 | {
63 | // Cannot set the thread name twice.
64 | Thread.CurrentThread.Name = subscriberFilterName;
65 | }
66 |
67 | AddressZeroMq = addressZeroMq;
68 |
69 | if (string.IsNullOrEmpty(AddressZeroMq))
70 | {
71 | throw new Exception("Error E76244. Must define the endpoint address for ZeroMQ to connect (or bind) to.");
72 | }
73 |
74 | switch (whenToCreateNetworkConnection)
75 | {
76 | case WhenToCreateNetworkConnection.LazyConnectOnFirstUse:
77 | // (default) Do nothing; will be instantiated on first use.
78 | break;
79 | case WhenToCreateNetworkConnection.SetupPublisherTransportNow:
80 | InitializePublisherOnFirstUse(addressZeroMq);
81 | break;
82 | case WhenToCreateNetworkConnection.SetupSubscriberTransportNow:
83 | throw new Exception("Error E34875. In the publisher, cannot initialize the subscriber on demand.");
84 | default:
85 | throw new Exception("Error E38745. Internal error; at least one publisher or subscriber must be instantiated.");
86 | }
87 | }
88 |
89 | #region Initialize publisher on demand.
90 | private PublisherSocket _publisherSocket;
91 | private volatile bool _initializePublisherDone = false;
92 | private readonly object _initializePublisherLock = new object();
93 | private void InitializePublisherOnFirstUse(string addressZeroMq)
94 | {
95 | if (_initializePublisherDone == false) // Double checked locking.
96 | {
97 | lock (_initializePublisherLock)
98 | {
99 | if (_initializePublisherDone == false)
100 | {
101 | _loggerDelegate?.Invoke(string.Format("Publisher socket binding to: {0}\n", AddressZeroMq));
102 | _publisherSocket = NetMqTransportShared.Instance(_loggerDelegate).GetSharedPublisherSocket(addressZeroMq);
103 | _initializePublisherDone = true;
104 | }
105 | }
106 | }
107 | }
108 | #endregion
109 |
110 | #region Implement IObserver (i.e. the publisher).
111 | public void OnNext(T message)
112 | {
113 | try
114 | {
115 | InitializePublisherOnFirstUse(this.AddressZeroMq);
116 |
117 | byte[] serialized = message.SerializeProtoBuf();
118 |
119 | // Publish message using ZeroMQ as the transport mechanism.
120 | _publisherSocket.SendMoreFrame(SubscriberFilterName)
121 | .SendMoreFrame("N") // "N", "E" or "C" for "OnNext", "OnError" or "OnCompleted".
122 | .SendFrame(serialized);
123 |
124 | // Comment in the remaining code for the standard pub/sub pattern.
125 |
126 | //if (this.HasObservers == false)
127 | //{
128 | //throw new QxNoSubscribers("Error E23444. As there are no subscribers to this publisher, this event will be lost.");
129 | //}
130 |
131 | //lock (_subscribersLock)
132 | //{
133 | //this._subscribers.ForEach(msg => msg.OnNext(message));
134 | //}
135 | }
136 | catch (InvalidOperationException ex)
137 | {
138 | if (ex.Source.ToLower().Contains("protobuf-net"))
139 | {
140 | var exWithDocs = new InvalidOperationException("Error: Message must be serializable by Protobuf-Net. To fix, annotate message with [ProtoContract] and [ProtoMember(N)]. See help on web.");
141 | this.OnError(exWithDocs);
142 | throw exWithDocs;
143 | }
144 | }
145 | catch (Exception ex)
146 | {
147 | _loggerDelegate?.Invoke(string.Format("Exception: {0}.", ex.Message));
148 | this.OnError(ex);
149 | throw;
150 | }
151 | }
152 |
153 | public void OnError(Exception exception)
154 | {
155 | InitializePublisherOnFirstUse(this.AddressZeroMq);
156 |
157 | string exceptionAsXml = new ExceptionXElement(exception).ToString();
158 | _publisherSocket.SendMoreFrame(SubscriberFilterName)
159 | .SendMoreFrame("E") // "N", "E" or "C" for "OnNext", "OnError" or "OnCompleted".
160 | .SendMoreFrame(exceptionAsXml) // Human readable exception. Added for 100% cross-platform debugging, so we can read
161 | // the error on the wire. Visual Studio has a nice viewer for XML in any variable during
162 | // debugging.
163 | .SendFrame(new byte[0]); // Future expansion for machine readable exception.
164 |
165 | // Comment in the remaining code for the standard pub/sub pattern.
166 |
167 | //if (this.HasObservers == false)
168 | //{
169 | //throw new QxNoSubscribers("Error E28244. As there are no subscribers to this publisher, this published exception will be lost.");
170 | //}
171 |
172 | //lock (_subscribersLock)
173 | //{
174 | //this._subscribers.ForEach(msg => msg.OnError(exception));
175 | //}
176 | }
177 |
178 | public void OnCompleted()
179 | {
180 | InitializePublisherOnFirstUse(this.AddressZeroMq);
181 |
182 | _publisherSocket.SendMoreFrame(SubscriberFilterName)
183 | .SendFrame("C"); // "N", "E" or "C" for "OnNext", "OnError" or "OnCompleted".
184 | }
185 | #endregion
186 |
187 | public void Dispose()
188 | {
189 | _cancellationTokenSource.Cancel();
190 | }
191 | }
192 | }
193 |
--------------------------------------------------------------------------------
/NetMQ.ReactiveExtensions/SerializeExceptionViaXml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace NetMQ.ReactiveExtensions
7 | {
8 | using System;
9 | using System.Collections;
10 | using System.Linq;
11 | using System.Xml.Linq;
12 |
13 | ///
14 | /// Intent: Represent an exception as XML data. In the Visual Studio debugger view for a variable, we can "View as XML" to see a formatted version.
15 | /// https://seattlesoftware.wordpress.com/2008/08/22/serializing-exceptions-to-xml/
16 | ///
17 | public class ExceptionXElement : XElement
18 | {
19 | /// Create an instance of ExceptionXElement.
20 | /// The Exception to serialize.
21 | public ExceptionXElement(Exception exception)
22 | : this(exception, false)
23 | { }
24 |
25 | /// Create an instance of ExceptionXElement.
26 | /// The Exception to serialize.
27 | ///
28 | /// Whether or not to serialize the Exception.StackTrace member
29 | /// if it's not null.
30 | ///
31 | public ExceptionXElement(Exception exception, bool omitStackTrace)
32 | : base(new Func(() =>
33 | {
34 | // Validate arguments.
35 | if (exception == null)
36 | {
37 | throw new ArgumentNullException(nameof(exception));
38 | }
39 |
40 | // The root element is the Exception's type.
41 | // TODO: When .NET Core 1.1 is available, fix these lines.
42 | string type = nameof(exception).ToString(); // Compatible with .NET Core 1.0.
43 | //string type = exception.GetType().ToString(); // Compatible with .NET Core 1.1+.
44 | XElement root = new XElement(type);
45 |
46 | root.Add(new XElement("Message", exception.Message));
47 |
48 | // StackTrace can be null, e.g. "new ExceptionAsXml(new Exception())".
49 | if (!omitStackTrace && exception.StackTrace != null)
50 | {
51 | root.Add
52 | (
53 | new XElement("StackTrace",
54 | from frame in exception.StackTrace.Split('\n')
55 | let prettierFrame = frame.Substring(6).Trim()
56 | select new XElement("Frame", prettierFrame))
57 | );
58 | }
59 |
60 | // Data is never null; it's empty if there is no data.
61 | if (exception.Data.Count > 0)
62 | {
63 | root.Add
64 | (
65 | new XElement("Data",
66 | from entry in
67 | exception.Data.Cast()
68 | let key = entry.Key.ToString()
69 | let value = entry.Value?.ToString() ?? "null"
70 | select new XElement(key, value))
71 | );
72 | }
73 |
74 | // Recursively add any InnerExceptions if they exist.
75 | if (exception.InnerException != null)
76 | {
77 | root.Add
78 | (
79 | new ExceptionXElement
80 | (exception.InnerException, omitStackTrace)
81 | );
82 | }
83 |
84 | return root;
85 | })())
86 | { }
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/NetMQ.ReactiveExtensions/SerializeViaProtoBuf.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using ProtoBuf;
4 |
5 | namespace NetMQ.ReactiveExtensions
6 | {
7 | ///
8 | /// Intent: Allow us to serialize using ProtoBuf.
9 | ///
10 | public static class SerializeViaProtoBuf
11 | {
12 | public static byte[] SerializeProtoBuf(this T message)
13 | {
14 | byte[] result;
15 | using (var stream = new MemoryStream())
16 | {
17 | Serializer.Serialize(stream, message);
18 | result = stream.ToArray();
19 | }
20 | return result;
21 | }
22 |
23 | public static T DeserializeProtoBuf(this byte[] bytes)
24 | {
25 | T result;
26 | using (var stream = new MemoryStream(bytes))
27 | {
28 | result = Serializer.Deserialize(stream);
29 | }
30 | return result;
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/NetMQ.ReactiveExtensions/SubjectNetMQ.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.Threading;
5 | using NetMQ.Monitoring;
6 | using NetMQ.Sockets;
7 | // ReSharper disable SuggestVarOrType_SimpleTypes
8 | // ReSharper disable InvertIf
9 | #pragma warning disable 649
10 |
11 | namespace NetMQ.ReactiveExtensions
12 | {
13 | public class SubjectNetMQ : IDisposable, ISubjectNetMQ, IObservable, IObserver
14 | {
15 | private readonly PublisherNetMq _publisher;
16 | private readonly SubscriberNetMq _subscriber;
17 |
18 | public SubjectNetMQ(string addressZeroMq, string subscriberFilterName = null, WhenToCreateNetworkConnection whenToCreateNetworkConnection = WhenToCreateNetworkConnection.LazyConnectOnFirstUse, CancellationTokenSource cancellationTokenSource = default(CancellationTokenSource), Action loggerDelegate = null)
19 | {
20 | _publisher = new PublisherNetMq(addressZeroMq, subscriberFilterName, WhenToCreateNetworkConnection.LazyConnectOnFirstUse, cancellationTokenSource, loggerDelegate);
21 | _subscriber = new SubscriberNetMq(addressZeroMq, subscriberFilterName, WhenToCreateNetworkConnection.LazyConnectOnFirstUse, cancellationTokenSource, loggerDelegate);
22 | }
23 |
24 | public string SubscriberFilterName
25 | {
26 | get { return _publisher.SubscriberFilterName; }
27 | }
28 |
29 | public string AddressZeroMq
30 | {
31 | get { return _publisher.SubscriberFilterName; }
32 | }
33 |
34 | public IDisposable Subscribe(IObserver observer)
35 | {
36 | return _subscriber.Subscribe(observer);
37 | }
38 |
39 | public void OnNext(T value)
40 | {
41 | _publisher.OnNext(value);
42 | }
43 |
44 | public void OnError(Exception error)
45 | {
46 | _publisher.OnError(error);
47 | }
48 |
49 | public void OnCompleted()
50 | {
51 | _publisher.OnCompleted();
52 | }
53 |
54 | public void Dispose()
55 | {
56 | _publisher.Dispose();
57 | _subscriber.Dispose();
58 | }
59 | }
60 | }
61 |
62 |
63 |
--------------------------------------------------------------------------------
/NetMQ.ReactiveExtensions/SubscriberNetMq.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Threading;
4 | using NetMQ.Sockets;
5 |
6 | namespace NetMQ.ReactiveExtensions
7 | {
8 | ///
9 | /// Intent: Create a new subscriber of type T, using NetMQ as the transport layer.
10 | ///
11 | /// Type we want to create to.
12 | public class SubscriberNetMq : ISubjectNetMQ, IObservable
13 | {
14 | private CancellationTokenSource _cancellationTokenSource;
15 | private readonly Action _loggerDelegate;
16 | private readonly List> _subscribers = new List>();
17 |
18 | #region Public properties.
19 |
20 | public string SubscriberFilterName { get; private set; }
21 | public string AddressZeroMq { get; private set; }
22 |
23 | #endregion
24 |
25 | ///
26 | /// Intent: Create a new subscriber, using NetMQ as the transport layer.
27 | ///
28 | /// ZeroMq address to bind to, e.g. "tcp://localhost:56001".
29 | /// Filter name on receiver. If you do not set this, it will default to the
30 | /// type name of T, and everything will just work.
31 | /// When to create the network connection.
32 | /// Allows graceful termination of all internal threads associated with this subject.
33 | /// (optional) If we want to look at messages generated within this class, specify a logger here.
34 | public SubscriberNetMq(string addressZeroMq, string subscriberFilterName = null,
35 | WhenToCreateNetworkConnection whenToCreateNetworkConnection =
36 | WhenToCreateNetworkConnection.SetupSubscriberTransportNow,
37 | CancellationTokenSource cancellationTokenSource = default(CancellationTokenSource),
38 | Action loggerDelegate = null)
39 | {
40 | AddressZeroMq = addressZeroMq;
41 | _cancellationTokenSource = cancellationTokenSource;
42 | _loggerDelegate = loggerDelegate;
43 |
44 | if (subscriberFilterName == null)
45 | {
46 | // Unfortunately, the subscriber never scans more than the first 32 characters of the filter, so we must
47 | // trim to less than this length, but also ensure that it's unique. This ensures that if we get two
48 | // classnames of 50 characters that only differ by the last character, everything will still work and we
49 | // won't get crossed subscriptions.
50 | SubscriberFilterName = typeof(T).Name;
51 |
52 | if (SubscriberFilterName.Length > 32)
53 | {
54 | string uniqueHashCode = string.Format("{0:X8}", SubscriberFilterName.GetHashCode());
55 |
56 | SubscriberFilterName = SubscriberFilterName.Substring(0, Math.Min(SubscriberFilterName.Length, 24));
57 | SubscriberFilterName += uniqueHashCode;
58 | }
59 | if (SubscriberFilterName.Length > 32)
60 | {
61 | throw new Exception(
62 | "Error E38742. Internal error; subscription length can never be longer than 32 characters.");
63 | }
64 | }
65 |
66 | if (string.IsNullOrEmpty(Thread.CurrentThread.Name) == true)
67 | {
68 | // Cannot set the thread name twice.
69 | Thread.CurrentThread.Name = subscriberFilterName;
70 | }
71 |
72 | AddressZeroMq = addressZeroMq;
73 |
74 | if (string.IsNullOrEmpty(AddressZeroMq))
75 | {
76 | throw new Exception("Error E76244. Must define the endpoint address for ZeroMQ to connect (or bind) to.");
77 | }
78 |
79 | switch (whenToCreateNetworkConnection)
80 | {
81 | case WhenToCreateNetworkConnection.LazyConnectOnFirstUse:
82 | // (default) Do nothing; will be instantiated on first use.
83 | break;
84 | case WhenToCreateNetworkConnection.SetupPublisherTransportNow:
85 | throw new Exception(
86 | "Error E77238. Internal error. Cannot instantate the publisher in the subscriber.");
87 | case WhenToCreateNetworkConnection.SetupSubscriberTransportNow:
88 | InitializeSubscriberOnFirstUse(addressZeroMq);
89 | break;
90 | default:
91 | throw new Exception(
92 | "Error E38745. Internal error. At least one publisher or subscriber must be instantiated.");
93 | }
94 |
95 | }
96 |
97 | #region Initialize subscriber on demand.
98 |
99 | private SubscriberSocket _subscriberSocket;
100 | private readonly object _subscribersLock = new object();
101 | private volatile bool _initializeSubscriberDone = false;
102 | private Thread _thread;
103 | private ManualResetEvent _threadWaitExitHandle = new ManualResetEvent(false);
104 |
105 | private void InitializeSubscriberOnFirstUse(string addressZeroMq)
106 | {
107 | if (_initializeSubscriberDone == false) // Double checked locking.
108 | {
109 | lock (_subscribersLock)
110 | {
111 | if (_initializeSubscriberDone == false)
112 | {
113 | if (_cancellationTokenSource == null)
114 | {
115 | _cancellationTokenSource = new CancellationTokenSource();
116 | }
117 |
118 | _subscriberSocket =
119 | NetMqTransportShared.Instance(_loggerDelegate).GetSharedSubscriberSocket(addressZeroMq);
120 |
121 | ManualResetEvent threadReadySignal = new ManualResetEvent(false);
122 |
123 | _thread = new Thread(() =>
124 | {
125 | try
126 | {
127 | _loggerDelegate?.Invoke(string.Format("Thread initialized.\n"));
128 | threadReadySignal.Set();
129 | while (_cancellationTokenSource.IsCancellationRequested == false)
130 | {
131 | //_loggerDelegate?.Invoke(string.Format("Received message for {0}.\n", typeof(T)));
132 |
133 | string messageTopicReceived = _subscriberSocket.ReceiveFrameString();
134 | if (messageTopicReceived != SubscriberFilterName)
135 | {
136 | // This message is for another subscriber. This should never occur.
137 | #if DEBUG
138 | throw new Exception(
139 | "Error E38444. Internal exception, this should never occur, as the ZeroMQ lib automaticlaly filters by subject name.");
140 | #else
141 | return;
142 | #endif
143 | }
144 | var type = _subscriberSocket.ReceiveFrameString();
145 | switch (type)
146 | {
147 | // Originated from "OnNext".
148 | case "N":
149 | T messageReceived =
150 | _subscriberSocket.ReceiveFrameBytes().DeserializeProtoBuf();
151 | lock (_subscribersLock)
152 | {
153 | try
154 | {
155 | _subscribers.ForEach(o => o.OnNext(messageReceived));
156 | }
157 | catch (Exception ex)
158 | {
159 | // If an unhandled exception is thrown inside the clients OnNext() event, we don't want this thread to die!
160 | _loggerDelegate?.Invoke(string.Format("Exception thrown on Subscription OnNext handler. Suggest adding an exception handler. Ignoring. Exception: {0}\n", ex));
161 | }
162 | }
163 | break;
164 | // Originated from "OnCompleted".
165 | case "C":
166 | lock (_subscribersLock)
167 | {
168 | try
169 | {
170 | _subscribers.ForEach(o => o.OnCompleted());
171 | }
172 | catch (Exception ex)
173 | {
174 | // If an unhandled exception is thrown inside the clients OnCompleted() event, we don't want this thread to die!
175 | _loggerDelegate?.Invoke(string.Format("Exception thrown on Subscription OnCompleted handler. Suggest adding an exception handler. Ignoring. Exception: {0}\n", ex));
176 | }
177 |
178 | // We are done! We don't want to send any more messages to subscribers, and we
179 | // want to close the listening socket.
180 | _cancellationTokenSource.Cancel();
181 | }
182 | break;
183 | // Originated from "OnException".
184 | case "E":
185 | Exception exception = null;
186 | try
187 | {
188 | // Not used, but useful for cross-platform debugging: we can read the error straight off the wire.
189 | string exceptionAsXml = _subscriberSocket.ReceiveFrameString();
190 | exception = new Exception(exceptionAsXml);
191 | _subscriberSocket.ReceiveFrameBytes(); // For future expansion for a machine readable exception.
192 | }
193 | catch (Exception ex)
194 | {
195 | // If we had trouble deserializing the exception (probably due to a
196 | // different version of .NET), then do the next best thing: (1) The
197 | // inner exception is the error we got when deserializing, and (2) the
198 | // main exception is the human-readable "exception.ToString()" that we
199 | // originally captured.
200 | exception = new Exception("Error deserializing exception. To fix, recompile the remote sender with the same version as this subscriber.", ex);
201 | }
202 |
203 | lock (_subscribersLock)
204 | {
205 | try
206 | {
207 | _subscribers.ForEach(o => o.OnError(exception));
208 | }
209 | catch (Exception ex)
210 | {
211 | // If an unhandled exception is thrown inside the clients OnCompleted() event, we don't want this thread to die!
212 | _loggerDelegate?.Invoke(string.Format("Exception thrown in Subscription OnError handler. Suggest adding an exception handler. Ignoring. Exception: {0}\n", ex));
213 | }
214 | }
215 | break;
216 | // Originated from a "Ping" request.
217 | case "P":
218 | // Do nothing, this is a ping command used to wait until sockets are initialized properly.
219 | _loggerDelegate?.Invoke(string.Format("Received ping.\n"));
220 | break;
221 | default:
222 | throw new Exception(string.Format("Error E28734. Something is wrong - received '{0}' when we expected \"N\", \"C\" or \"E\" - are we out of sync?", type));
223 | }
224 | }
225 | }
226 | catch (Exception ex)
227 | {
228 | _loggerDelegate?.Invoke(string.Format("Error E23844. Exception in threadName \"{0}\". Thread exiting. Exception: \"{1}\".\n", SubscriberFilterName, ex.Message));
229 | lock (_subscribersLock)
230 | {
231 | this._subscribers.ForEach((ob) => ob.OnError(ex));
232 | }
233 | }
234 | finally
235 | {
236 | lock (_subscribersLock)
237 | {
238 | _subscribers.Clear();
239 | }
240 | _cancellationTokenSource.Dispose();
241 |
242 | // Disconnect from the socket.
243 | _subscriberSocket.Dispose();
244 |
245 | _threadWaitExitHandle.Set();
246 | }
247 | })
248 | {
249 | Name = this.SubscriberFilterName,
250 | IsBackground = true // Have to set it to background, or else it will not exit when the program exits.
251 | };
252 | _thread.Start();
253 |
254 | // Wait for subscriber thread to properly spin up.
255 | threadReadySignal.WaitOne(TimeSpan.FromMilliseconds(3000));
256 |
257 | // Intent: Now connect to the socket.
258 | {
259 | _loggerDelegate?.Invoke(string.Format("Subscriber socket connecting to: {0}\n", addressZeroMq));
260 |
261 | // this.SubscriberFilterName is set to the type T of the incoming class by default, so we can
262 | // have many types on the same transport.
263 | _subscriberSocket.Subscribe(this.SubscriberFilterName);
264 | }
265 |
266 | _loggerDelegate?.Invoke(string.Format("Subscriber: finished setup.\n"));
267 |
268 | _initializeSubscriberDone = true;
269 | }
270 | } // lock
271 | Thread.Sleep(500); // Otherwise, the first item we subscribe to may get missed by the subscriber.
272 | }
273 | }
274 | #endregion
275 |
276 | #region IObservable (i.e. the subscriber)
277 | ///
278 | /// Intent: Exactly the same as "Subscribe" in Reactive Extensions (RX).
279 | ///
280 | ///
281 | ///
282 | public IDisposable Subscribe(IObserver observer)
283 | {
284 | lock (_subscribersLock)
285 | {
286 | this._subscribers.Add(observer);
287 | }
288 |
289 | InitializeSubscriberOnFirstUse(this.AddressZeroMq);
290 |
291 | // Could return ".this", but this would introduce an issue: if one subscriber unsubscribed, it would
292 | // unsubscribe all subscribers.
293 | return new AnonymousDisposable(() =>
294 | {
295 | lock (_subscribersLock)
296 | {
297 | this._subscribers.Remove(observer);
298 | }
299 | });
300 | }
301 | #endregion
302 |
303 | public void Dispose()
304 | {
305 | lock (_subscribersLock)
306 | {
307 | _subscribers.Clear();
308 | }
309 | _cancellationTokenSource.Cancel();
310 |
311 | // Wait until the thread has exited.
312 | bool threadExitedProperly = _threadWaitExitHandle.WaitOne(TimeSpan.FromSeconds(30));
313 | if (threadExitedProperly == false)
314 | {
315 | throw new Exception("Error E62724. Thread did not exit when requested.");
316 | }
317 | }
318 | }
319 | }
--------------------------------------------------------------------------------
/NetMQ.ReactiveExtensions/app.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/NetMQ.ReactiveExtensions/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/NetMQ.ReactiveExtensions/project.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1.0.0",
3 | "dependencies": {
4 | "NetMQ": "4.0.0-rc5",
5 | "protobuf-net": "2.1.0",
6 | "System.Reactive.Core": "3.1.0"
7 | },
8 | "runtimes": {
9 | "win7-x64": {}
10 | },
11 | "frameworks": {
12 | "netstandard1.6": {},
13 | "net45": {
14 | "dependencies": {
15 | "System.Xml.Linq": "3.5.*"
16 | }
17 | },
18 | "net46": {
19 | "dependencies": {
20 |
21 | },
22 | "frameworkAssemblies": {
23 | "System.Xml.Linq": "4.0.*"
24 | }
25 | }
26 | },
27 | "buildOptions": {
28 | "xmlDoc": true
29 | }
30 | }
31 |
32 |
33 |
--------------------------------------------------------------------------------
/NuGet/.gitignore:
--------------------------------------------------------------------------------
1 | *.exe
2 | /NuGet.Build.bat
3 |
--------------------------------------------------------------------------------
/NuGet/NetMQ.ReactiveExtensions.nuspec:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | NetMQ.ReactiveExtensions
5 | $version$
6 | NetMQ Reactive Extensions
7 | NetMQ
8 | NetMQ
9 | https://github.com/NetMQ/NetMQ.ReactiveExtensions/blob/master/LICENSE
10 | https://github.com/NetMQ/NetMQ.ReactiveExtensions
11 | https://raw.githubusercontent.com/NetMQ/NetMQ.ReactiveExtensions/img/NetMQ.ico
12 | false
13 | Reactive Extensions (RX) with NetMQ as the transport layer. Send messages anywhere on the network!
14 |
15 | Now compatible with .NET Core 1.0. Feedback welcome!
16 |
17 | Copyright 2016
18 | Reactive Extensions RX .NET C# ZeroMQ 0MQ CLRZMQ NetMQ Messaging ZMQ transport distributed
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/NuGet/nuget.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # NetMQ.ReactiveExtensions
2 |
3 | [](https://ci.appveyor.com/project/drewnoakes/netmq-reactiveextensions) [](https://www.nuget.org/packages/NetMQ.ReactiveExtensions/) [](https://www.nuget.org/packages/NetMQ.ReactiveExtensions/)
4 |
5 | Effortlessly send messages anywhere on the network using Reactive Extensions (RX). Uses NetMQ as the transport layer.
6 |
7 | Fast! Runs at >120,000 messages per second on localhost (by comparison, Tibco runs at 100,000 on the same machine).
8 |
9 | ## Sample Code
10 |
11 | The API is a drop-in replacement for `Subject` from Reactive Extensions (RX).
12 |
13 | As a refresher, to use `Subject` in Reactive Extensions (RX):
14 |
15 | ```csharp
16 | var subject = new Subject();
17 | subject.Subscribe(message =>
18 | {
19 | // If we get an error "Cannot convert lambda ... not a delegate type", install Reactive Extensions from NuGet.
20 | Console.Write(message); // Prints "42".
21 | });
22 | subject.OnNext(42);
23 | ```
24 |
25 | The new API starts with a drop-in replacement for `Subject`:
26 |
27 | ```csharp
28 | var subject = new SubjectNetMQ("tcp://127.0.0.1:56001");
29 | subject.Subscribe(message =>
30 | {
31 | Console.Write(message); // Prints "42".
32 | });
33 | subject.OnNext(42); // Sends 42.
34 | ```
35 |
36 | This is great for a demo, but is not recommended for any real life application.
37 |
38 | For those of us familiar with Reactive Extensions (RX), `Subject` is a combination of a publisher and a subscriber. If we are running a real-life application, we should separate out the publisher and the subscriber, because this means we can create the connection earlier which makes the transport setup more deterministic:
39 |
40 | ```csharp
41 | var publisher = new PublisherNetMq("tcp://127.0.0.1:56001");
42 | var subscriber = new SubscriberNetMq("tcp://127.0.0.1:56001");
43 | subscriber.Subscribe(message =>
44 | {
45 | Console.Write(message); // Prints "42".
46 | });
47 | publisher.OnNext(42); // Sends 42.
48 | ```
49 |
50 | If we want to run in separate applications:
51 |
52 | ```csharp
53 | // Application 1 (subscriber)
54 | var subscriber1 = new SubscriberNetMq("tcp://127.0.0.1:56001");
55 | subscriber1.Subscribe(message =>
56 | {
57 | Console.Write(message); // Prints "42".
58 | });
59 |
60 | // Application 2 (subscriber)
61 | var subscriber2 = new SubscriberNetMq("tcp://127.0.0.1:56001");
62 | subscriber2.Subscribe(message =>
63 | {
64 | Console.Write(message); // Prints "42".
65 | });
66 |
67 | // Application 3 (publisher)
68 | var publisher = new PublisherNetMq("tcp://127.0.0.1:56001");
69 | publisher.OnNext(42); // Sends 42.
70 | ```
71 |
72 | Currently, serialization is performed using [ProtoBuf](https://github.com/mgravell/protobuf-net "ProtoBuf"). It will handle simple types such as `int` without annotation, but if we want to send more complex classes, we have to annotate like this:
73 |
74 | ```csharp
75 | // For Protobuf support, include NuGet package protobuf-net from Marc Gravell.
76 | [ProtoContract]
77 | public struct MyMessage
78 | {
79 | [ProtoMember(1)]
80 | public int Num { get; set; }
81 | [ProtoMember(2)]
82 | public string Name { get; set; }
83 | }
84 |
85 | var publisher = new PublisherNetMq("tcp://127.0.0.1:56001");
86 | var subscriber = new SubscriberNetMq("tcp://127.0.0.1:56001");
87 | subscriber.Subscribe(message =>
88 | {
89 | Console.Write(message.Num); // Prints "42".
90 | Console.Write(message.Name); // Prints "Bill".
91 | });
92 | publisher.OnNext(new MyMessage(42, "Bill");
93 | ```
94 |
95 | ## NuGet Package
96 |
97 | [](https://ci.appveyor.com/project/drewnoakes/netmq-reactiveextensions) [](https://www.nuget.org/packages/NetMQ.ReactiveExtensions/) [](https://www.nuget.org/packages/NetMQ.ReactiveExtensions/)
98 |
99 | See [NetMQ.ReactiveExtensions](https://www.nuget.org/packages/NetMQ.ReactiveExtensions/).
100 |
101 | The NuGet package 0.9.3 is designed for .NET 4. It depends on Reactive Extensions v2.2.5 (this is difficult to find, can download the packages manually from NuGet).
102 |
103 | The NuGet package 0.9.4-rc7 is designed .NET Core 1.1, .NET 4.5, and .NET Standard 1.6. If you want to build it for other platforms, please let me know. If you have trouble loading this, load the Git branch for the 0.9.3 release.
104 |
105 | ## .NET Core 1.1 Ready
106 |
107 | As of v0.9.4-rc7, this package will build for:
108 | - .NET 4.5 and up
109 | - [.NET Core 1.1](https://www.microsoft.com/net/download/core)
110 | - .NET Standard 1.6 and up
111 |
112 | As this library supports .NET Standard 1.6 (which is a subset of .NET Core 1.1), this library should be compatible with:
113 | - Windows
114 | - Linux
115 | - Mac
116 |
117 | This library is tested on Window and Linux. If it passes it's unit tests on any given platform, then it should perform nicely on different architectures such as Mac.
118 |
119 | ## Compiling from source
120 |
121 | - Install [Visual Studio 2015 Update 3](https://www.visualstudio.com/en-us/news/releasenotes/vs2015-update3-vs).
122 | - Install "[.NET Core 1.1 SDK - Installer](https://www.microsoft.com/net/download/core)" from https://www.microsoft.com/net/download/core.
123 | - NuGet Restore. It may not compile until a manual "[nuget restore](https://docs.nuget.org/ndocs/consume-packages/package-restore)" is performed for each project (this also rebuilds the `project.lock.json` file). You can either do this from the command line, or by right clicking on the solution and choosing `Restore NuGet packages`.
124 | - If the project does not compile on your machine, raise an issue here on GitHub.
125 |
126 | NOTE: Not compatible with .NET Core 1.0 or .NET Core 1.0.1. Must install .NET Core 1.1 and above to avoid potential compile errors.
127 |
128 | ## Demos
129 |
130 | To check out the demos, see:
131 | - Publisher: Project `NetMQ.ReactiveExtensions.SamplePublisher`
132 | - Subscriber: Project `NetMQ.ReactiveExtensions.SampleSubscriber`
133 | - Sample unit tests: Project `NetMQ.ReactiveExtensions.Tests`
134 |
135 | ## Performance
136 |
137 | - Runs at >120,000 messages per second on localhost.
138 |
139 | ## 100% compatible with Reactive Extensions (RX)
140 |
141 | - Compatible with all existing Reactive Extensions code, as it implements IObservable and IObserver from Microsoft.
142 | - Can use `.Where()`, `.Select()`, `.Buffer()`, `.Throttle()`, etc.
143 | - Supports `.OnNext()`, `.OnException()`, and `.OnCompleted()`.
144 | - Properly passes exceptions across the wire.
145 |
146 | ## Unit tests
147 |
148 | - Supported by a full suite of unit tests.
149 |
150 | ## Projects like this one that do messaging
151 |
152 | - See [Obvs](https://github.com/inter8ection/Obvs), an fantastic RX wrapper which supports many transport layers including NetMQ, RabbitMQ and Azure, and many serialization methods including ProtoBuf and MsgPack.
153 | - See [Obvs.NetMQ](https://github.com/inter8ection/Obvs.Netmq), the RX wrapper with NetMQ as the transport layer.
154 | - Search for [all packages on NuGet that depend on RX](http://nugetmusthaves.com/Dependencies/Rx-Linq), and pick out the ones that are related to message buses.
155 | - Check out Kafka. It provides many-to-many messaging, with persistance, and multi-node redundancy. And its blindingly fast.
156 |
157 | ## Wiki
158 |
159 | See the [Wiki with more documentation](https://github.com/NetMQ/NetMQ.ReactiveExtensions/wiki).
160 |
161 |
162 |
163 |
--------------------------------------------------------------------------------
/img/NetMQ.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NetMQ/NetMQ.ReactiveExtensions/db9004a40ee465d166ebb619cfee97306c75fd81/img/NetMQ.ico
--------------------------------------------------------------------------------
/img/NetMQLogo-128px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NetMQ/NetMQ.ReactiveExtensions/db9004a40ee465d166ebb619cfee97306c75fd81/img/NetMQLogo-128px.png
--------------------------------------------------------------------------------
/img/NetMQLogo-32px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NetMQ/NetMQ.ReactiveExtensions/db9004a40ee465d166ebb619cfee97306c75fd81/img/NetMQLogo-32px.png
--------------------------------------------------------------------------------
/img/NetMQLogo-400px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NetMQ/NetMQ.ReactiveExtensions/db9004a40ee465d166ebb619cfee97306c75fd81/img/NetMQLogo-400px.png
--------------------------------------------------------------------------------
/img/NetMQLogo-48px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NetMQ/NetMQ.ReactiveExtensions/db9004a40ee465d166ebb619cfee97306c75fd81/img/NetMQLogo-48px.png
--------------------------------------------------------------------------------
/img/NetMQLogo-64px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NetMQ/NetMQ.ReactiveExtensions/db9004a40ee465d166ebb619cfee97306c75fd81/img/NetMQLogo-64px.png
--------------------------------------------------------------------------------
/img/NetMQLogo.svg:
--------------------------------------------------------------------------------
1 |
2 |
23 |
--------------------------------------------------------------------------------