├── .github └── FUNDING.yml ├── .gitignore ├── .vs └── SimpleUdp │ └── v16 │ ├── .suo │ └── Server │ └── sqlite3 │ ├── db.lock │ └── storage.ide ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── DONATIONS.md ├── LICENSE.md ├── README.md ├── assets ├── icon.ico └── icon.png └── src ├── Node ├── Node.csproj └── Program.cs ├── SimpleUdp.sln └── SimpleUdp ├── Common.cs ├── Datagram.cs ├── EndpointMetadata.cs ├── LICENSE.md ├── SimpleUdp.csproj ├── SimpleUdp.xml ├── UdpEndpoint.cs └── assets ├── icon.ico └── icon.png /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [jchristn] 2 | custom: ["https://paypal.me/joelchristner"] -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### 2 | ### 3 | ### Node 4 | ### 5 | ### 6 | 7 | # Node modules: 8 | node_modules 9 | node_modules/ 10 | 11 | ### 12 | ### 13 | ### JetBrains 14 | ### 15 | ### 16 | 17 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 18 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 19 | 20 | # User-specific stuff: 21 | .idea 22 | .idea/ 23 | .idea/**/workspace.xml 24 | .idea/**/tasks.xml 25 | .idea/dictionaries 26 | 27 | # Sensitive or high-churn files: 28 | .idea/**/dataSources/ 29 | .idea/**/dataSources.ids 30 | .idea/**/dataSources.xml 31 | .idea/**/dataSources.local.xml 32 | .idea/**/sqlDataSources.xml 33 | .idea/**/dynamic.xml 34 | .idea/**/uiDesigner.xml 35 | 36 | # Gradle: 37 | .idea/**/gradle.xml 38 | .idea/**/libraries 39 | 40 | # CMake 41 | cmake-build-debug/ 42 | cmake-build-release/ 43 | 44 | # Mongo Explorer plugin: 45 | .idea/**/mongoSettings.xml 46 | 47 | ## File-based project format: 48 | *.iws 49 | 50 | ## Plugin-specific files: 51 | 52 | # IntelliJ 53 | out/ 54 | 55 | # mpeltonen/sbt-idea plugin 56 | .idea_modules/ 57 | 58 | # JIRA plugin 59 | atlassian-ide-plugin.xml 60 | 61 | # Cursive Clojure plugin 62 | .idea/replstate.xml 63 | 64 | # Crashlytics plugin (for Android Studio and IntelliJ) 65 | com_crashlytics_export_strings.xml 66 | crashlytics.properties 67 | crashlytics-build.properties 68 | fabric.properties 69 | 70 | ### 71 | ### 72 | ### Visual Studio 73 | ### 74 | ### 75 | 76 | ## Ignore Visual Studio temporary files, build results, and 77 | ## files generated by popular Visual Studio add-ons. 78 | 79 | # User-specific files 80 | *.suo 81 | *.user 82 | *.userosscache 83 | *.sln.docstates 84 | 85 | # User-specific files (MonoDevelop/Xamarin Studio) 86 | *.userprefs 87 | 88 | # Build results 89 | [Dd]ebug/ 90 | [Dd]ebugPublic/ 91 | [Rr]elease/ 92 | [Rr]eleases/ 93 | x64/ 94 | x86/ 95 | bld/ 96 | [Bb]in/ 97 | [Oo]bj/ 98 | [Ll]og/ 99 | 100 | # Visual Studio 2015 cache/options directory 101 | .vs/ 102 | # Uncomment if you have tasks that create the project's static files in wwwroot 103 | #wwwroot/ 104 | 105 | # MSTest test Results 106 | [Tt]est[Rr]esult*/ 107 | [Bb]uild[Ll]og.* 108 | 109 | # NUNIT 110 | *.VisualState.xml 111 | TestResult.xml 112 | 113 | # Build Results of an ATL Project 114 | [Dd]ebugPS/ 115 | [Rr]eleasePS/ 116 | dlldata.c 117 | 118 | # DNX 119 | project.lock.json 120 | project.fragment.lock.json 121 | artifacts/ 122 | 123 | *_i.c 124 | *_p.c 125 | *_i.h 126 | *.ilk 127 | *.meta 128 | *.obj 129 | *.pch 130 | *.pdb 131 | *.pgc 132 | *.pgd 133 | *.rsp 134 | *.sbr 135 | *.tlb 136 | *.tli 137 | *.tlh 138 | *.tmp 139 | *.tmp_proj 140 | *.log 141 | *.vspscc 142 | *.vssscc 143 | .builds 144 | *.pidb 145 | *.svclog 146 | *.scc 147 | 148 | # Chutzpah Test files 149 | _Chutzpah* 150 | 151 | # Visual C++ cache files 152 | ipch/ 153 | *.aps 154 | *.ncb 155 | *.opendb 156 | *.opensdf 157 | *.sdf 158 | *.cachefile 159 | *.VC.db 160 | *.VC.VC.opendb 161 | 162 | # Visual Studio profiler 163 | *.psess 164 | *.vsp 165 | *.vspx 166 | *.sap 167 | 168 | # TFS 2012 Local Workspace 169 | $tf/ 170 | 171 | # Guidance Automation Toolkit 172 | *.gpState 173 | 174 | # ReSharper is a .NET coding add-in 175 | _ReSharper*/ 176 | *.[Rr]e[Ss]harper 177 | *.DotSettings.user 178 | 179 | # JustCode is a .NET coding add-in 180 | .JustCode 181 | 182 | # TeamCity is a build add-in 183 | _TeamCity* 184 | 185 | # DotCover is a Code Coverage Tool 186 | *.dotCover 187 | 188 | # Visual Studio code coverage results 189 | *.coverage 190 | *.coveragexml 191 | 192 | # NCrunch 193 | _NCrunch_* 194 | .*crunch*.local.xml 195 | nCrunchTemp_* 196 | 197 | # MightyMoose 198 | *.mm.* 199 | AutoTest.Net/ 200 | 201 | # Web workbench (sass) 202 | .sass-cache/ 203 | 204 | # Installshield output folder 205 | [Ee]xpress/ 206 | 207 | # DocProject is a documentation generator add-in 208 | DocProject/buildhelp/ 209 | DocProject/Help/*.HxT 210 | DocProject/Help/*.HxC 211 | DocProject/Help/*.hhc 212 | DocProject/Help/*.hhk 213 | DocProject/Help/*.hhp 214 | DocProject/Help/Html2 215 | DocProject/Help/html 216 | 217 | # Click-Once directory 218 | publish/ 219 | 220 | # Publish Web Output 221 | *.[Pp]ublish.xml 222 | *.azurePubxml 223 | # TODO: Comment the next line if you want to checkin your web deploy settings 224 | # but database connection strings (with potential passwords) will be unencrypted 225 | *.pubxml 226 | *.publishproj 227 | 228 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 229 | # checkin your Azure Web App publish settings, but sensitive information contained 230 | # in these scripts will be unencrypted 231 | PublishScripts/ 232 | 233 | # NuGet Packages 234 | *.nupkg 235 | # The packages folder can be ignored because of Package Restore 236 | **/packages/* 237 | # except build/, which is used as an MSBuild target. 238 | !**/packages/build/ 239 | # Uncomment if necessary however generally it will be regenerated when needed 240 | #!**/packages/repositories.config 241 | # NuGet v3's project.json files produces more ignoreable files 242 | *.nuget.props 243 | *.nuget.targets 244 | 245 | # Microsoft Azure Build Output 246 | csx/ 247 | *.build.csdef 248 | 249 | # Microsoft Azure Emulator 250 | ecf/ 251 | rcf/ 252 | 253 | # Windows Store app package directories and files 254 | AppPackages/ 255 | BundleArtifacts/ 256 | Package.StoreAssociation.xml 257 | _pkginfo.txt 258 | 259 | # Visual Studio cache files 260 | # files ending in .cache can be ignored 261 | *.[Cc]ache 262 | # but keep track of directories ending in .cache 263 | !*.[Cc]ache/ 264 | 265 | # Others 266 | ClientBin/ 267 | ~$* 268 | *~ 269 | *.dbmdl 270 | *.dbproj.schemaview 271 | *.jfm 272 | *.publishsettings 273 | node_modules/ 274 | orleans.codegen.cs 275 | 276 | # Since there are multiple workflows, uncomment next line to ignore bower_components 277 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 278 | #bower_components/ 279 | 280 | # RIA/Silverlight projects 281 | Generated_Code/ 282 | 283 | # Backup & report files from converting an old project file 284 | # to a newer Visual Studio version. Backup files are not needed, 285 | # because we have git ;-) 286 | _UpgradeReport_Files/ 287 | Backup*/ 288 | UpgradeLog*.XML 289 | UpgradeLog*.htm 290 | 291 | # SQL Server files 292 | *.mdf 293 | *.ldf 294 | 295 | # Business Intelligence projects 296 | *.rdl.data 297 | *.bim.layout 298 | *.bim_*.settings 299 | 300 | # Microsoft Fakes 301 | FakesAssemblies/ 302 | 303 | # GhostDoc plugin setting file 304 | *.GhostDoc.xml 305 | 306 | # Node.js Tools for Visual Studio 307 | .ntvs_analysis.dat 308 | 309 | # Visual Studio 6 build log 310 | *.plg 311 | 312 | # Visual Studio 6 workspace options file 313 | *.opt 314 | 315 | # Visual Studio LightSwitch build output 316 | **/*.HTMLClient/GeneratedArtifacts 317 | **/*.DesktopClient/GeneratedArtifacts 318 | **/*.DesktopClient/ModelManifest.xml 319 | **/*.Server/GeneratedArtifacts 320 | **/*.Server/ModelManifest.xml 321 | _Pvt_Extensions 322 | 323 | # Paket dependency manager 324 | .paket/paket.exe 325 | paket-files/ 326 | 327 | # FAKE - F# Make 328 | .fake/ 329 | 330 | # JetBrains Rider 331 | .idea/ 332 | *.sln.iml 333 | 334 | # CodeRush 335 | .cr/ 336 | 337 | # Python Tools for Visual Studio (PTVS) 338 | __pycache__/ 339 | *.pyc 340 | -------------------------------------------------------------------------------- /.vs/SimpleUdp/v16/.suo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jchristn/SimpleUdp/f38294479821c2c3b79e2cf7e07194a9069ef4af/.vs/SimpleUdp/v16/.suo -------------------------------------------------------------------------------- /.vs/SimpleUdp/v16/Server/sqlite3/db.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jchristn/SimpleUdp/f38294479821c2c3b79e2cf7e07194a9069ef4af/.vs/SimpleUdp/v16/Server/sqlite3/db.lock -------------------------------------------------------------------------------- /.vs/SimpleUdp/v16/Server/sqlite3/storage.ide: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jchristn/SimpleUdp/f38294479821c2c3b79e2cf7e07194a9069ef4af/.vs/SimpleUdp/v16/Server/sqlite3/storage.ide -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## Current Version 4 | 5 | v2.0.x 6 | 7 | - Retarget to .NET 8.0 8 | - Removal of `Start`, `Stop` APIs, and, the started event 9 | - Better multi-platform compatibility (Windows, Mac OSX, Ubuntu) 10 | 11 | ## Previous Versions 12 | 13 | v1.2.x 14 | - Support for broadcast endpoints (set IP to null), thank you @charleypeng 15 | - Resolve issue associated with rapid send operations, thank you @seatrix 16 | 17 | v1.1.x 18 | 19 | - ```Events.Started``` and ```Events.Stopped``` 20 | - ```UdpEndpoint.Start``` and ```UdpEndpoint.Stop``` APIs 21 | 22 | 23 | v1.0.0 24 | 25 | - Initial release 26 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | - Using welcoming and inclusive language 12 | - Being respectful of differing viewpoints and experiences 13 | - Gracefully accepting constructive criticism 14 | - Focusing on what is best for the community 15 | - Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | - The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | - Trolling, insulting/derogatory comments, and personal or political attacks 21 | - Public or private harassment 22 | - Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | - Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the Contributor Covenant, version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 44 | 45 | For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq 46 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Thank you for your interest in contributing! 4 | 5 | The following is a set of guidelines for contributing to our project on Github. These are mostly guidelines, not rules. 6 | 7 | ## Code of Conduct 8 | 9 | This project and everyone participating in it is governed by the Code of Conduct. By participating, you are expected to uphold this code. Please report unacceptable behavior to project moderators. 10 | 11 | ## Pull Requests 12 | 13 | Please follow these guidelines when submitting pull requests (PRs): 14 | 15 | - PRs should be manageable in size to make it easy for us to validate and integrate 16 | - Each PR should be contained to a single fix or a single feature 17 | - Describe the motivation for the PR 18 | - Describe a methodology to test and validate, if appropriate 19 | 20 | Please ensure that the code in your PR follows a style similar to that of the project. If you find a material discrepancy between the style followed by the project and a de facto standard style given the project language and framework, please let us know so we can amend and make our code more maintainable. 21 | 22 | ## Asking Questions 23 | 24 | Prior to asking questions, please review closed issues and wiki pages. If your question is not answered in either of those places, please feel free to file an issue! This will also help us to build out documentation. 25 | 26 | ## Reporting Bugs 27 | 28 | If you encounter an issue, please let us know! We kindly ask that you supply the following information with your bug report when you file an issue. Feel free to copy/paste from the below and use as a template. 29 | 30 | --- Bug Report --- 31 | 32 | Operating system and version: Windows 10 33 | Framework and runtime: .NET Core 2.0 34 | Issue encountered: The widget shifted left 35 | Expected behavior: The widget should have shifted right 36 | Steps to reproduce: Instantiate the widget and call .ShiftRight() 37 | Sample code encapsulating the problem: 38 | ``` 39 | Widget widget = new Widget(); 40 | widget.ShiftRight(); 41 | ``` 42 | Exception details: [insert exception output here] 43 | Stack trace: [if appropriate] 44 | 45 | --- End --- 46 | 47 | ## Suggesting Enhancements 48 | 49 | Should there be a way that you feel we could improve upon this project, please feel free to file an issue and use the template below to provide the necessary details to support the request. 50 | 51 | Some basic guidelines for suggesting enhancements: 52 | 53 | - Use a clear and descriptive title for the issue to identify the suggestion. 54 | - Provide a step-by-step description of the suggested enhancement in as many details as possible. 55 | - Provide specific examples to demonstrate the steps including copy/pasteable snippets where possible 56 | - Describe the current behavior and the behavior you would like to see 57 | - Describe the usefulness of the enhancement to yourself and potentially to others 58 | 59 | --- Enhancement Request --- 60 | 61 | Enhancement request title: Widgets should have a color attribute 62 | Use case: I want to specify what color a widget is 63 | Current behavior: Widgets don't have a color 64 | Requested behavior: Allow me to specify a widget's color 65 | Recommended implementation: Add a Color attribute to the Widget class 66 | Usefulness of the enhancement: All widgets have color, and everyone has to build their own implementation to set this 67 | 68 | --- End --- 69 | -------------------------------------------------------------------------------- /DONATIONS.md: -------------------------------------------------------------------------------- 1 | ## Donations 2 | 3 | If you're interested in financially supporting this work on other open source projects I manage, first of all, thank you! It brings me delight to know that this software has helped you in some way. Please find below address details for donations using 4 | 5 | ### Traditional 6 | 7 | | Method | Address | 8 | |--------|---------| 9 | | PayPal | @joelchristner - https://paypal.me/joelchristner?country.x=US&locale.x=en_US | 10 | | Venmo | @Joel-Christner - https://account.venmo.com/u/Joel-Christner | 11 | 12 | ### Cryptocurrency (Mainstream) 13 | 14 | | Method | Address | 15 | |----------|---------| 16 | | Bitcoin | 3HRgnvEWQBDdWDp75CFDsz3PirCYmftDwU | 17 | | Ethereum | 0xE064dC84270e17e2Ac34b2552461d672BdBC5e36 | 18 | 19 | ### Cryptocurrency (Altcoins) 20 | 21 | | Method | Address | 22 | |--------|---------| 23 | | XRP | Tag 1765608084 Address rw2ciyaNshpHe7bCHo4bRWq6pqqynnWKQg | 24 | | Shiba Inu SHIB | 0xdA58D4ba0d5823d80a0C42C69E139124B889c69a | 25 | | Algorand ALGO | FFKA23KC4BHEU5HM4OQHLEILVIYDLRXYK6WD6UI573JPUGHZR43JVHAF7A | 26 | | Stellar Lumens XML | Memo 2264929895 Address GDQP2KPQGKIHYJGXNUIYOMHARUARCA7DJT5FO2FFOOKY3B2WSQHG4W37 | 27 | | Dogecoin DOGE | DQ2dn4UifpYA8RyuNF1t112Y1XH5L42Rxv | 28 | | Cardano ADA | addr1vxrxyrv0phgr2xcl08dj0sfy425mhqehuu4dy99ja4nhtwqfsvgjk | 29 | 30 | If you have a coin you prefer to use, please let me know! 31 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![alt tag](https://github.com/jchristn/simpleudp/blob/master/assets/icon.ico) 2 | 3 | # SimpleUdp 4 | 5 | ## Simple wrapper for UDP client and server in C# 6 | 7 | [![NuGet Version](https://img.shields.io/nuget/v/SimpleUdp.svg?style=flat)](https://www.nuget.org/packages/SimpleUdp/) [![NuGet](https://img.shields.io/nuget/dt/SimpleUdp.svg)](https://www.nuget.org/packages/SimpleUdp) 8 | 9 | SimpleUdp provides simple methods for creating your own UDP-based sockets application, enabling easy integration of sending data, receiving data, and building state machines. 10 | 11 | ## New in v2.0.x 12 | 13 | - Retarget to .NET 8.0 14 | - Removal of `Start`, `Stop` APIs, and, the started event 15 | - Better multi-platform compatibility (Windows, Mac OSX, Ubuntu) 16 | 17 | ## Help or Feedback 18 | 19 | Need help or have feedback? Please file an issue here! 20 | 21 | ## Special Thanks 22 | 23 | Thanks to community members that have helped improve this library! @jholzer @charleypeng @seatrix 24 | 25 | ## Need TCP Instead? 26 | 27 | I have you covered. 28 | 29 | - WatsonTcp - easiest way to build TCP-based applications with built-in framing 30 | - Github: https://github.com/jchristn/watsontcp 31 | - NuGet: https://www.nuget.org/packages/WatsonTcp/ 32 | - [![NuGet Version](https://img.shields.io/nuget/v/WatsonTcp.svg?style=flat)](https://www.nuget.org/packages/WatsonTcp/) 33 | - SimpleTcp - lightweight TCP wrapper without framing 34 | - Github: https://github.com/jchristn/simpletcp 35 | - NuGet: https://www.nuget.org/packages/SuperSimpleTcp/ 36 | - [![NuGet Version](https://img.shields.io/nuget/v/SuperSimpleTcp.svg?style=flat)](https://www.nuget.org/packages/SuperSimpleTcp/) 37 | - CavemanTcp - TCP wrapper exposing controls for sending and receiving data to build state machines 38 | - Github: https://github.com/jchristn/cavemantcp 39 | - NuGet: https://www.nuget.org/packages/CavemanTcp/ 40 | - [![NuGet Version](https://img.shields.io/nuget/v/CavemanTcp.svg?style=flat)](https://www.nuget.org/packages/CavemanTcp/) 41 | 42 | Don't know what to use? Just ask! File an issue, I'll be happy to help. 43 | 44 | ## Simple Example 45 | 46 | Start a node. 47 | ``` 48 | using SimpleUdp; 49 | 50 | UdpEndpoint udp = new UdpEndpoint("127.0.0.1", 8000); 51 | udp.EndpointDetected += EndpointDetected; 52 | 53 | // only if you want to receive messages... 54 | udp.DatagramReceived += DatagramReceived; 55 | 56 | // send a message... 57 | udp.Send("127.0.0.1", 8001, "Hello to my friend listening on port 8001!"); 58 | 59 | static void EndpointDetected(object sender, EndpointMetadata md) 60 | { 61 | Console.WriteLine("Endpoint detected: " + md.Ip + ":" + md.Port); 62 | } 63 | 64 | static void DatagramReceived(object sender, Datagram dg) 65 | { 66 | Console.WriteLine("[" + dg.Ip + ":" + dg.Port + "]: " + Encoding.UTF8.GetString(dg.Data)); 67 | } 68 | ``` 69 | 70 | ## The Node Project 71 | 72 | Start node 1. 73 | ``` 74 | Node\bin\Debug\netcoreapp3.1> node 127.0.0.1 8000 75 | ``` 76 | 77 | Start node 2. 78 | ``` 79 | Node\bin\Debug\netcoreapp3.1> node 127.0.0.1 8001 80 | ``` 81 | 82 | Send message from node 1 to node 2. To do this, enter a command as follows: 83 | ``` 84 | [ip:port] [msg] 85 | ``` 86 | i.e. 87 | ``` 88 | 127.0.0.1:8001 hello to my friend running on port 8001! 89 | ``` 90 | 91 | ``` 92 | [127.0.0.1:8000 Command/? for help]: 127.0.0.1:8001 hello to my friend on port 8001! 93 | ``` 94 | 95 | Send message from node 2 to node 1. 96 | ``` 97 | [127.0.0.1:8001 Command/? for help]: Endpoint detected: 127.0.0.1:8000 98 | [127.0.0.1:8000]: hello to my friend on port 8001! 99 | 127.0.0.1:8000 hello back to you my friend! 100 | ``` 101 | 102 | ## Version History 103 | 104 | Please refer to CHANGELOG.md. 105 | -------------------------------------------------------------------------------- /assets/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jchristn/SimpleUdp/f38294479821c2c3b79e2cf7e07194a9069ef4af/assets/icon.ico -------------------------------------------------------------------------------- /assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jchristn/SimpleUdp/f38294479821c2c3b79e2cf7e07194a9069ef4af/assets/icon.png -------------------------------------------------------------------------------- /src/Node/Node.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net6.0;net7.0;net8.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/Node/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using SimpleUdp; 5 | 6 | namespace Node 7 | { 8 | class Program 9 | { 10 | static string _Ip = null; 11 | static int _Port = 0; 12 | static UdpEndpoint _UdpEndpoint; 13 | 14 | static void Main(string[] args) 15 | { 16 | if (args == null || args.Length < 2) 17 | { 18 | Usage(); 19 | return; 20 | } 21 | /* 22 | * 23 | * 24 | * Usage: 25 | * node 127.0.0.1 8000 26 | * 27 | * Starts the endpoint on IP address 127.0.0.1 port 8000. 28 | * 29 | * 30 | * 31 | */ 32 | 33 | _Ip = args[0]; 34 | _Port = Convert.ToInt32(args[1]); 35 | 36 | _UdpEndpoint = new UdpEndpoint(_Ip, _Port); 37 | _UdpEndpoint.EndpointDetected += EndpointDetected; 38 | _UdpEndpoint.DatagramReceived += DatagramReceived; 39 | _UdpEndpoint.ServerStopped += NodeStopped; 40 | 41 | while (true) 42 | { 43 | Console.Write("[" + _Ip + ":" + _Port + " Command/? for help]: "); 44 | string userInput = Console.ReadLine(); 45 | if (String.IsNullOrEmpty(userInput)) continue; 46 | 47 | if (userInput.Equals("?")) 48 | { 49 | Menu(); 50 | } 51 | else if (userInput.Equals("q")) 52 | { 53 | break; 54 | } 55 | else if (userInput.Equals("cls")) 56 | { 57 | Console.Clear(); 58 | } 59 | else if (userInput.Equals("list")) 60 | { 61 | List recents = _UdpEndpoint.Endpoints; 62 | if (recents != null) 63 | { 64 | Console.WriteLine("Recent endpoints"); 65 | foreach (string endpoint in recents) Console.WriteLine(" " + endpoint); 66 | } 67 | else 68 | { 69 | Console.WriteLine("None"); 70 | } 71 | } 72 | else 73 | { 74 | string[] parts = userInput.Split(new char[] { ' ' }, 2); 75 | if (parts.Length != 2) continue; 76 | 77 | string msg = parts[1]; 78 | string ipPort = parts[0]; 79 | int colonIndex = parts[0].LastIndexOf(':'); 80 | string ip = ipPort.Substring(0, colonIndex); 81 | int port = Convert.ToInt32(ipPort.Substring(colonIndex + 1)); 82 | 83 | _UdpEndpoint.Send(ip, port, msg); 84 | } 85 | } 86 | 87 | Console.ReadLine(); 88 | } 89 | 90 | static void Usage() 91 | { 92 | Console.WriteLine("Usage:"); 93 | Console.WriteLine("> node 127.0.0.1 8000"); 94 | Console.WriteLine("Starts the endpoint on IP address 127.0.0.1 port 8000"); 95 | } 96 | 97 | static void Menu() 98 | { 99 | Console.WriteLine(""); 100 | Console.WriteLine("Available commands"); 101 | Console.WriteLine(" q quit"); 102 | Console.WriteLine(" ? help, this menu"); 103 | Console.WriteLine(" cls clear the screen"); 104 | Console.WriteLine(" list list recent endpoints"); 105 | Console.WriteLine(""); 106 | Console.WriteLine("To send a message, use the form 'ip:port message', i.e."); 107 | Console.WriteLine("127.0.0.1:8001 hello world!"); 108 | Console.WriteLine(""); 109 | } 110 | 111 | static void EndpointDetected(object sender, EndpointMetadata md) 112 | { 113 | Console.WriteLine("Endpoint detected: " + md.Ip + ":" + md.Port); 114 | } 115 | 116 | static void DatagramReceived(object sender, Datagram dg) 117 | { 118 | Console.WriteLine("[" + dg.Ip + ":" + dg.Port + "]: " + Encoding.UTF8.GetString(dg.Data)); 119 | } 120 | 121 | private static void NodeStarted(object sender, EventArgs e) 122 | { 123 | Console.WriteLine("*** Node started"); 124 | } 125 | 126 | private static void NodeStopped(object sender, EventArgs e) 127 | { 128 | Console.WriteLine("*** Node stopped"); 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/SimpleUdp.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29411.108 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SimpleUdp", "SimpleUdp\SimpleUdp.csproj", "{13B36B6B-6B6A-430E-A631-B75A8C87AB22}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Node", "Node\Node.csproj", "{C75CD2A7-FCBB-48A5-B0FC-98A06CA8B6CB}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {13B36B6B-6B6A-430E-A631-B75A8C87AB22}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {13B36B6B-6B6A-430E-A631-B75A8C87AB22}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {13B36B6B-6B6A-430E-A631-B75A8C87AB22}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {13B36B6B-6B6A-430E-A631-B75A8C87AB22}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {C75CD2A7-FCBB-48A5-B0FC-98A06CA8B6CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {C75CD2A7-FCBB-48A5-B0FC-98A06CA8B6CB}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {C75CD2A7-FCBB-48A5-B0FC-98A06CA8B6CB}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {C75CD2A7-FCBB-48A5-B0FC-98A06CA8B6CB}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {5A2BCD14-57CA-4605-B694-66332F0B910A} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /src/SimpleUdp/Common.cs: -------------------------------------------------------------------------------- 1 | namespace SimpleUdp 2 | { 3 | using System; 4 | 5 | /// 6 | /// Commonly used static methods for SimpleUdp. 7 | /// 8 | public static class Common 9 | { 10 | /// 11 | /// Parse an IP:port string to its parts. 12 | /// 13 | /// IP:port. 14 | /// IP address. 15 | /// Port number. 16 | public static void ParseIpPort(string ipPort, out string ip, out int port) 17 | { 18 | if (String.IsNullOrEmpty(ipPort)) throw new ArgumentNullException(nameof(ipPort)); 19 | 20 | ip = null; 21 | port = -1; 22 | 23 | int colonIndex = ipPort.LastIndexOf(':'); 24 | if (colonIndex != -1) 25 | { 26 | ip = ipPort.Substring(0, colonIndex); 27 | port = Convert.ToInt32(ipPort.Substring(colonIndex + 1)); 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/SimpleUdp/Datagram.cs: -------------------------------------------------------------------------------- 1 | namespace SimpleUdp 2 | { 3 | using System; 4 | 5 | /// 6 | /// Datagram received by this endpoint. 7 | /// 8 | public class Datagram 9 | { 10 | #region Public-Members 11 | 12 | /// 13 | /// IP address of the remote endpoint. 14 | /// 15 | public string Ip 16 | { 17 | get 18 | { 19 | return _Ip; 20 | } 21 | set 22 | { 23 | if (String.IsNullOrEmpty(value)) throw new ArgumentNullException(nameof(Ip)); 24 | _Ip = value; 25 | } 26 | } 27 | 28 | /// 29 | /// Port number of the remote endpoint. 30 | /// 31 | public int Port 32 | { 33 | get 34 | { 35 | return _Port; 36 | } 37 | set 38 | { 39 | if (value < 0 || value > 65535) throw new ArgumentOutOfRangeException(nameof(Port)); 40 | _Port = value; 41 | } 42 | } 43 | 44 | /// 45 | /// Data received from the remote endpoint. 46 | /// 47 | public byte[] Data 48 | { 49 | get 50 | { 51 | return _Data; 52 | } 53 | set 54 | { 55 | if (value == null) value = Array.Empty(); 56 | _Data = value; 57 | } 58 | } 59 | 60 | #endregion 61 | 62 | #region Private-Members 63 | 64 | private string _Ip = "127.0.0.1"; 65 | private int _Port = 0; 66 | private byte[] _Data = Array.Empty(); 67 | 68 | #endregion 69 | 70 | #region Constructors-and-Factories 71 | 72 | /// 73 | /// Instantiate. 74 | /// 75 | public Datagram() 76 | { 77 | 78 | } 79 | 80 | /// 81 | /// Instantiate. 82 | /// 83 | /// IP address of the remote endpoint. 84 | /// Port of the remote endpoint. 85 | /// Data. 86 | internal Datagram(string ip, int port, byte[] data) 87 | { 88 | Ip = ip; 89 | Port = port; 90 | Data = data; 91 | } 92 | 93 | #endregion 94 | 95 | #region Public-Methods 96 | 97 | #endregion 98 | 99 | #region Private-Methods 100 | 101 | #endregion 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/SimpleUdp/EndpointMetadata.cs: -------------------------------------------------------------------------------- 1 | namespace SimpleUdp 2 | { 3 | using System; 4 | 5 | /// 6 | /// Remote endpoint metadata. 7 | /// 8 | public class EndpointMetadata 9 | { 10 | #region Public-Members 11 | 12 | /// 13 | /// IP address of the remote endpoint. 14 | /// 15 | public string Ip 16 | { 17 | get 18 | { 19 | return _Ip; 20 | } 21 | set 22 | { 23 | if (String.IsNullOrEmpty(value)) throw new ArgumentNullException(nameof(Ip)); 24 | _Ip = value; 25 | } 26 | } 27 | 28 | /// 29 | /// Port number of the remote endpoint. 30 | /// 31 | public int Port 32 | { 33 | get 34 | { 35 | return _Port; 36 | } 37 | set 38 | { 39 | if (value < 0 || value > 65535) throw new ArgumentOutOfRangeException(nameof(Port)); 40 | _Port = value; 41 | } 42 | } 43 | 44 | #endregion 45 | 46 | #region Private-Members 47 | 48 | private string _Ip = "127.0.0.1"; 49 | private int _Port = 0; 50 | 51 | #endregion 52 | 53 | #region Constructors-and-Factories 54 | 55 | /// 56 | /// Instantiate. 57 | /// 58 | public EndpointMetadata() 59 | { 60 | 61 | } 62 | 63 | /// 64 | /// Instantiate. 65 | /// 66 | /// IP address of the remote endpoint. 67 | /// Port of the remote endpoint. 68 | internal EndpointMetadata(string ip, int port) 69 | { 70 | Ip = ip; 71 | Port = port; 72 | } 73 | 74 | #endregion 75 | 76 | #region Public-Methods 77 | 78 | #endregion 79 | 80 | #region Private-Methods 81 | 82 | #endregion 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/SimpleUdp/LICENSE.md: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /src/SimpleUdp/SimpleUdp.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1;net6.0;net7.0;net8.0 5 | 2.0.2 6 | Library 7 | assets\icon.ico 8 | 9 | Joel Christner 10 | SimpleUdp is a super simple way of building UDP clients and servers in C#. 11 | (c)2024 Joel Christner 12 | LICENSE.md 13 | https://github.com/jchristn/SimpleUdp 14 | https://github.com/jchristn/SimpleUdp 15 | Github 16 | udp messaging socket message sockets api rpc datagram 17 | Breaking changes to accommodate multi-platform 18 | true 19 | https://raw.githubusercontent.com/jchristn/SimpleUdp/master/assets/icon.ico 20 | icon.png 21 | README.md 22 | 23 | 24 | 25 | SimpleUdp.xml 26 | 27 | 28 | 29 | 30 | True 31 | \ 32 | 33 | 34 | True 35 | \ 36 | 37 | 38 | True 39 | 40 | Always 41 | 42 | 43 | 44 | True 45 | \ 46 | Always 47 | 48 | 49 | True 50 | \ 51 | Always 52 | 53 | 54 | Always 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /src/SimpleUdp/SimpleUdp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | SimpleUdp 5 | 6 | 7 | 8 | 9 | Commonly used static methods for SimpleUdp. 10 | 11 | 12 | 13 | 14 | Parse an IP:port string to its parts. 15 | 16 | IP:port. 17 | IP address. 18 | Port number. 19 | 20 | 21 | 22 | Datagram received by this endpoint. 23 | 24 | 25 | 26 | 27 | IP address of the remote endpoint. 28 | 29 | 30 | 31 | 32 | Port number of the remote endpoint. 33 | 34 | 35 | 36 | 37 | Data received from the remote endpoint. 38 | 39 | 40 | 41 | 42 | Instantiate. 43 | 44 | 45 | 46 | 47 | Instantiate. 48 | 49 | IP address of the remote endpoint. 50 | Port of the remote endpoint. 51 | Data. 52 | 53 | 54 | 55 | Remote endpoint metadata. 56 | 57 | 58 | 59 | 60 | IP address of the remote endpoint. 61 | 62 | 63 | 64 | 65 | Port number of the remote endpoint. 66 | 67 | 68 | 69 | 70 | Instantiate. 71 | 72 | 73 | 74 | 75 | Instantiate. 76 | 77 | IP address of the remote endpoint. 78 | Port of the remote endpoint. 79 | 80 | 81 | 82 | UDP endpoint, both client and server. 83 | 84 | 85 | 86 | 87 | Event to fire when a new endpoint is detected. 88 | 89 | 90 | 91 | 92 | Event to fire when a datagram is received. 93 | 94 | 95 | 96 | 97 | Event to fire when the server is stopped. 98 | 99 | 100 | 101 | 102 | Retrieve a list of (up to) the 100 most recently seen endpoints. 103 | 104 | 105 | 106 | 107 | Maximum datagram size, must be greater than zero and less than or equal to 65507. 108 | 109 | 110 | 111 | 112 | Instantiate the UDP endpoint. 113 | If you wish to also receive datagrams, set the 'DatagramReceived' event and call 'StartServer()'. 114 | 115 | IP address on which to listen. 116 | Port number on which to listen. 117 | 118 | 119 | 120 | Dispose. 121 | 122 | 123 | 124 | 125 | Dispose. 126 | 127 | Disposing. 128 | 129 | 130 | 131 | Send a datagram to the specific IP address and UDP port. 132 | This will throw a SocketException if the report UDP port is unreachable. 133 | 134 | IP address. 135 | Port. 136 | Text to send. 137 | Time to live, the maximum number of routers the packet is allowed to traverse. Minimum is 0, default is 64. 138 | 139 | 140 | 141 | Send a datagram to the specific IP address and UDP port. 142 | This will throw a SocketException if the report UDP port is unreachable. 143 | 144 | IP address. 145 | Port. 146 | Bytes. 147 | Time to live, the maximum number of routers the packet is allowed to traverse. Minimum is 0, default is 64. 148 | 149 | 150 | 151 | Send a datagram asynchronously to the specific IP address and UDP port. 152 | This will throw a SocketException if the report UDP port is unreachable. 153 | 154 | IP address. 155 | Port. 156 | Text to send. 157 | Time to live, the maximum number of routers the packet is allowed to traverse. Minimum is 0, default is 64. 158 | 159 | 160 | 161 | Send a datagram asynchronously to the specific IP address and UDP port. 162 | This will throw a SocketException if the report UDP port is unreachable. 163 | 164 | IP address. 165 | Port. 166 | Bytes. 167 | Time to live, the maximum number of routers the packet is allowed to traverse. Minimum is 0, default is 64. 168 | 169 | 170 | 171 | -------------------------------------------------------------------------------- /src/SimpleUdp/UdpEndpoint.cs: -------------------------------------------------------------------------------- 1 | namespace SimpleUdp 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Net; 6 | using System.Net.Sockets; 7 | using System.Text; 8 | using System.Threading; 9 | using System.Threading.Tasks; 10 | using Caching; 11 | 12 | /// 13 | /// UDP endpoint, both client and server. 14 | /// 15 | public class UdpEndpoint : IDisposable 16 | { 17 | #region Public-Members 18 | 19 | /// 20 | /// Event to fire when a new endpoint is detected. 21 | /// 22 | public event EventHandler EndpointDetected; 23 | 24 | /// 25 | /// Event to fire when a datagram is received. 26 | /// 27 | public event EventHandler DatagramReceived; 28 | 29 | /// 30 | /// Event to fire when the server is stopped. 31 | /// 32 | public event EventHandler ServerStopped; 33 | 34 | /// 35 | /// Retrieve a list of (up to) the 100 most recently seen endpoints. 36 | /// 37 | public List Endpoints 38 | { 39 | get 40 | { 41 | return _RemoteSockets.GetKeys(); 42 | } 43 | } 44 | 45 | /// 46 | /// Maximum datagram size, must be greater than zero and less than or equal to 65507. 47 | /// 48 | public int MaxDatagramSize 49 | { 50 | get 51 | { 52 | return _MaxDatagramSize; 53 | } 54 | set 55 | { 56 | if (value < 1 || value > 65507) throw new ArgumentException("MaxDatagramSize must be greater than zero and less than or equal to 65507."); 57 | _MaxDatagramSize = value; 58 | } 59 | } 60 | 61 | #endregion 62 | 63 | #region Private-Members 64 | 65 | private bool _Disposed = false; 66 | private string _Ip = null; 67 | private int _Port = 0; 68 | private IPAddress _IPAddress; 69 | private Socket _Socket = null; 70 | private int _MaxDatagramSize = 65507; 71 | private EndPoint _Endpoint = new IPEndPoint(IPAddress.Any, 0); 72 | private UdpClient _UdpClient = null; 73 | private AsyncCallback _ReceiveCallback = null; 74 | 75 | private LRUCache _RemoteSockets = new LRUCache(100, 1); 76 | 77 | private SemaphoreSlim _SendLock = new SemaphoreSlim(1, 1); 78 | 79 | #endregion 80 | 81 | #region Internal-Classes 82 | 83 | internal class State 84 | { 85 | internal State(int bufferSize) 86 | { 87 | Buffer = new byte[bufferSize]; 88 | } 89 | 90 | internal byte[] Buffer = null; 91 | } 92 | 93 | #endregion 94 | 95 | #region Constructors-and-Factories 96 | 97 | /// 98 | /// Instantiate the UDP endpoint. 99 | /// If you wish to also receive datagrams, set the 'DatagramReceived' event and call 'StartServer()'. 100 | /// 101 | /// IP address on which to listen. 102 | /// Port number on which to listen. 103 | public UdpEndpoint(string ip, int port) 104 | { 105 | if (port < 0 || port > 65535) throw new ArgumentException("Port must be greater than or equal to zero and less than or equal to 65535."); 106 | _Ip = ip; 107 | _Port = port; 108 | 109 | if (String.IsNullOrEmpty(ip)) _IPAddress = IPAddress.Any; 110 | else _IPAddress = IPAddress.Parse(ip); 111 | 112 | _UdpClient = new UdpClient(); 113 | _UdpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); 114 | _UdpClient.ExclusiveAddressUse = false; 115 | 116 | State state = new State(_MaxDatagramSize); 117 | 118 | _Socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); 119 | _Socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.ReuseAddress, true); 120 | _Socket.Bind(new IPEndPoint(_IPAddress, _Port)); 121 | 122 | _Socket.BeginReceiveFrom(state.Buffer, 0, _MaxDatagramSize, SocketFlags.None, ref _Endpoint, _ReceiveCallback = (ar) => 123 | { 124 | try 125 | { 126 | State so = (State)ar.AsyncState; 127 | int bytes = _Socket.EndReceiveFrom(ar, ref _Endpoint); 128 | 129 | string senderIpPort = _Endpoint.ToString(); 130 | string senderIp = null; 131 | int senderPort = 0; 132 | Common.ParseIpPort(senderIpPort, out senderIp, out senderPort); 133 | 134 | if (!_RemoteSockets.Contains(senderIpPort)) 135 | { 136 | _RemoteSockets.AddReplace(senderIpPort, _Socket); 137 | EndpointDetected?.Invoke(this, new EndpointMetadata(senderIp, senderPort)); 138 | } 139 | 140 | if (bytes == so.Buffer.Length) 141 | { 142 | DatagramReceived?.Invoke(this, new Datagram(senderIp, senderPort, so.Buffer)); 143 | } 144 | else 145 | { 146 | byte[] buffer = new byte[bytes]; 147 | Buffer.BlockCopy(so.Buffer, 0, buffer, 0, bytes); 148 | DatagramReceived?.Invoke(this, new Datagram(senderIp, senderPort, buffer)); 149 | } 150 | 151 | _Socket.BeginReceiveFrom(so.Buffer, 0, _MaxDatagramSize, SocketFlags.None, ref _Endpoint, _ReceiveCallback, so); 152 | } 153 | catch (Exception) 154 | { 155 | ServerStopped?.Invoke(this, EventArgs.Empty); 156 | } 157 | }, state); 158 | } 159 | 160 | #endregion 161 | 162 | #region Public-Methods 163 | 164 | /// 165 | /// Dispose. 166 | /// 167 | public void Dispose() 168 | { 169 | Dispose(true); 170 | GC.SuppressFinalize(this); 171 | } 172 | 173 | /// 174 | /// Dispose. 175 | /// 176 | /// Disposing. 177 | protected virtual void Dispose(bool disposing) 178 | { 179 | if (_Disposed) return; 180 | 181 | if (disposing) 182 | { 183 | _UdpClient?.Dispose(); 184 | 185 | if (_Socket != null) 186 | { 187 | _Socket.Close(); 188 | } 189 | } 190 | 191 | _Disposed = true; 192 | } 193 | 194 | /// 195 | /// Send a datagram to the specific IP address and UDP port. 196 | /// This will throw a SocketException if the report UDP port is unreachable. 197 | /// 198 | /// IP address. 199 | /// Port. 200 | /// Text to send. 201 | /// Time to live, the maximum number of routers the packet is allowed to traverse. Minimum is 0, default is 64. 202 | public void Send(string ip, int port, string text, short ttl = 64) 203 | { 204 | if (String.IsNullOrEmpty(ip)) throw new ArgumentNullException(nameof(ip)); 205 | if (port < 0 || port > 65535) throw new ArgumentException("Port is out of range; must be greater than or equal to zero and less than or equal to 65535."); 206 | if (String.IsNullOrEmpty(text)) throw new ArgumentNullException(nameof(text)); 207 | if (ttl < 0) throw new ArgumentOutOfRangeException(nameof(ttl)); 208 | byte[] data = Encoding.UTF8.GetBytes(text); 209 | if (data.Length > _MaxDatagramSize) throw new ArgumentException("Data exceed maximum datagram size (" + data.Length + " data bytes, " + _MaxDatagramSize + " bytes)."); 210 | SendInternal(ip, port, data, ttl); 211 | } 212 | 213 | /// 214 | /// Send a datagram to the specific IP address and UDP port. 215 | /// This will throw a SocketException if the report UDP port is unreachable. 216 | /// 217 | /// IP address. 218 | /// Port. 219 | /// Bytes. 220 | /// Time to live, the maximum number of routers the packet is allowed to traverse. Minimum is 0, default is 64. 221 | public void Send(string ip, int port, byte[] data, short ttl = 64) 222 | { 223 | if (String.IsNullOrEmpty(ip)) throw new ArgumentNullException(nameof(ip)); 224 | if (port < 0 || port > 65535) throw new ArgumentException("Port is out of range; must be greater than or equal to zero and less than or equal to 65535."); 225 | if (data == null || data.Length < 1) throw new ArgumentNullException(nameof(data)); 226 | if (data.Length > _MaxDatagramSize) throw new ArgumentException("Data exceed maximum datagram size (" + data.Length + " data bytes, " + _MaxDatagramSize + " bytes)."); 227 | if (ttl < 0) throw new ArgumentOutOfRangeException(nameof(ttl)); 228 | SendInternal(ip, port, data, ttl); 229 | } 230 | 231 | /// 232 | /// Send a datagram asynchronously to the specific IP address and UDP port. 233 | /// This will throw a SocketException if the report UDP port is unreachable. 234 | /// 235 | /// IP address. 236 | /// Port. 237 | /// Text to send. 238 | /// Time to live, the maximum number of routers the packet is allowed to traverse. Minimum is 0, default is 64. 239 | public async Task SendAsync(string ip, int port, string text, short ttl = 64) 240 | { 241 | if (String.IsNullOrEmpty(ip)) throw new ArgumentNullException(nameof(ip)); 242 | if (port < 0 || port > 65535) throw new ArgumentException("Port is out of range; must be greater than or equal to zero and less than or equal to 65535."); 243 | if (String.IsNullOrEmpty(text)) throw new ArgumentNullException(nameof(text)); 244 | byte[] data = Encoding.UTF8.GetBytes(text); 245 | if (data.Length > _MaxDatagramSize) throw new ArgumentException("Data exceed maximum datagram size (" + data.Length + " data bytes, " + _MaxDatagramSize + " bytes)."); 246 | if (ttl < 0) throw new ArgumentOutOfRangeException(nameof(ttl)); 247 | await SendInternalAsync(ip, port, data, ttl).ConfigureAwait(false); 248 | } 249 | 250 | /// 251 | /// Send a datagram asynchronously to the specific IP address and UDP port. 252 | /// This will throw a SocketException if the report UDP port is unreachable. 253 | /// 254 | /// IP address. 255 | /// Port. 256 | /// Bytes. 257 | /// Time to live, the maximum number of routers the packet is allowed to traverse. Minimum is 0, default is 64. 258 | public async Task SendAsync(string ip, int port, byte[] data, short ttl = 64) 259 | { 260 | if (String.IsNullOrEmpty(ip)) throw new ArgumentNullException(nameof(ip)); 261 | if (port < 0 || port > 65535) throw new ArgumentException("Port is out of range; must be greater than or equal to zero and less than or equal to 65535."); 262 | if (data == null || data.Length < 1) throw new ArgumentNullException(nameof(data)); 263 | if (data.Length > _MaxDatagramSize) throw new ArgumentException("Data exceed maximum datagram size (" + data.Length + " data bytes, " + _MaxDatagramSize + " bytes)."); 264 | if (ttl < 0) throw new ArgumentOutOfRangeException(nameof(ttl)); 265 | await SendInternalAsync(ip, port, data, ttl).ConfigureAwait(false); 266 | } 267 | 268 | #endregion 269 | 270 | #region Private-Methods 271 | 272 | private void SendInternal(string ip, int port, byte[] data, short ttl) 273 | { 274 | _SendLock.Wait(); 275 | 276 | IPEndPoint ipe = new IPEndPoint(IPAddress.Parse(ip), port); 277 | 278 | try 279 | { 280 | _UdpClient.Ttl = ttl; 281 | _UdpClient.Send(data, data.Length, ipe); 282 | } 283 | finally 284 | { 285 | _SendLock.Release(); 286 | } 287 | } 288 | 289 | private async Task SendInternalAsync(string ip, int port, byte[] data, short ttl) 290 | { 291 | await _SendLock.WaitAsync(); 292 | 293 | IPEndPoint ipe = new IPEndPoint(IPAddress.Parse(ip), port); 294 | 295 | try 296 | { 297 | _UdpClient.Ttl = ttl; 298 | await _UdpClient.SendAsync(data, data.Length, ipe).ConfigureAwait(false); 299 | } 300 | catch (TaskCanceledException) 301 | { 302 | 303 | } 304 | catch (OperationCanceledException) 305 | { 306 | 307 | } 308 | finally 309 | { 310 | _SendLock.Release(); 311 | } 312 | } 313 | 314 | #endregion 315 | } 316 | } 317 | -------------------------------------------------------------------------------- /src/SimpleUdp/assets/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jchristn/SimpleUdp/f38294479821c2c3b79e2cf7e07194a9069ef4af/src/SimpleUdp/assets/icon.ico -------------------------------------------------------------------------------- /src/SimpleUdp/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jchristn/SimpleUdp/f38294479821c2c3b79e2cf7e07194a9069ef4af/src/SimpleUdp/assets/icon.png --------------------------------------------------------------------------------