├── .gitignore ├── LICENSE ├── README.md ├── TcpUdpTool.sln ├── TcpUdpTool ├── App.config ├── App.xaml ├── App.xaml.cs ├── Model │ ├── Data │ │ ├── Transmission.cs │ │ └── TransmissionResult.cs │ ├── EventArg │ │ └── ReceivedEventArgs.cs │ ├── Formatter │ │ ├── HexFormatter.cs │ │ ├── IFormatter.cs │ │ └── PlainTextFormatter.cs │ ├── Parser │ │ ├── HexParser.cs │ │ ├── IParser.cs │ │ └── PlainTextParser.cs │ ├── RateMonitor.cs │ ├── TcpClient.cs │ ├── TcpServer.cs │ ├── UdpClientServer.cs │ ├── UdpMulticastClient.cs │ └── Util │ │ ├── DialogUtils.cs │ │ ├── NetworkUtils.cs │ │ ├── SettingsUtils.cs │ │ └── StringFormatUtils.cs ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings ├── Resources │ ├── Icons.xaml │ ├── Styles.xaml │ └── icon.ico ├── TcpUdpTool.csproj ├── View │ ├── HelpView.xaml │ ├── HelpView.xaml.cs │ ├── HistoryView.xaml │ ├── HistoryView.xaml.cs │ ├── MainWindow.xaml │ ├── MainWindow.xaml.cs │ ├── SettingsView.xaml │ ├── SettingsView.xaml.cs │ ├── TcpClientView.xaml │ ├── TcpClientView.xaml.cs │ ├── TcpServerView.xaml │ ├── TcpServerView.xaml.cs │ ├── UdpAsmView.xaml │ ├── UdpAsmView.xaml.cs │ ├── UdpSsmView.xaml │ ├── UdpSsmView.xaml.cs │ ├── UdpView.xaml │ └── UdpView.xaml.cs └── ViewModel │ ├── Base │ ├── BatchObservableCollection.cs │ ├── DelegateCommand.cs │ └── ObservableObject.cs │ ├── Extension │ ├── ListViewExtension.cs │ └── WindowExtension.cs │ ├── Helper │ ├── DispatchHelper.cs │ └── IContentChangedHelper.cs │ ├── HistoryViewModel.cs │ ├── Item │ ├── ConversationItemViewModel.cs │ └── InterfaceAddress.cs │ ├── MainViewModel.cs │ ├── SendViewModel.cs │ ├── SettingsViewModel.cs │ ├── TcpClientViewModel.cs │ ├── TcpServerViewModel.cs │ ├── UdpAsmViewModel.cs │ ├── UdpSsmViewModel.cs │ └── UdpViewModel.cs └── icon ├── tcp-udp-tool-icon.ico ├── tcp-udp-tool-icon.png └── tcp-udp-tool-icon.psd /.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 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opendb 80 | *.opensdf 81 | *.sdf 82 | *.cachefile 83 | *.VC.db 84 | *.VC.VC.opendb 85 | 86 | # Visual Studio profiler 87 | *.psess 88 | *.vsp 89 | *.vspx 90 | *.sap 91 | 92 | # TFS 2012 Local Workspace 93 | $tf/ 94 | 95 | # Guidance Automation Toolkit 96 | *.gpState 97 | 98 | # ReSharper is a .NET coding add-in 99 | _ReSharper*/ 100 | *.[Rr]e[Ss]harper 101 | *.DotSettings.user 102 | 103 | # JustCode is a .NET coding add-in 104 | .JustCode 105 | 106 | # TeamCity is a build add-in 107 | _TeamCity* 108 | 109 | # DotCover is a Code Coverage Tool 110 | *.dotCover 111 | 112 | # NCrunch 113 | _NCrunch_* 114 | .*crunch*.local.xml 115 | nCrunchTemp_* 116 | 117 | # MightyMoose 118 | *.mm.* 119 | AutoTest.Net/ 120 | 121 | # Web workbench (sass) 122 | .sass-cache/ 123 | 124 | # Installshield output folder 125 | [Ee]xpress/ 126 | 127 | # DocProject is a documentation generator add-in 128 | DocProject/buildhelp/ 129 | DocProject/Help/*.HxT 130 | DocProject/Help/*.HxC 131 | DocProject/Help/*.hhc 132 | DocProject/Help/*.hhk 133 | DocProject/Help/*.hhp 134 | DocProject/Help/Html2 135 | DocProject/Help/html 136 | 137 | # Click-Once directory 138 | publish/ 139 | 140 | # Publish Web Output 141 | *.[Pp]ublish.xml 142 | *.azurePubxml 143 | # TODO: Comment the next line if you want to checkin your web deploy settings 144 | # but database connection strings (with potential passwords) will be unencrypted 145 | *.pubxml 146 | *.publishproj 147 | 148 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 149 | # checkin your Azure Web App publish settings, but sensitive information contained 150 | # in these scripts will be unencrypted 151 | PublishScripts/ 152 | 153 | # NuGet Packages 154 | *.nupkg 155 | # The packages folder can be ignored because of Package Restore 156 | **/packages/* 157 | # except build/, which is used as an MSBuild target. 158 | !**/packages/build/ 159 | # Uncomment if necessary however generally it will be regenerated when needed 160 | #!**/packages/repositories.config 161 | # NuGet v3's project.json files produces more ignoreable files 162 | *.nuget.props 163 | *.nuget.targets 164 | 165 | # Microsoft Azure Build Output 166 | csx/ 167 | *.build.csdef 168 | 169 | # Microsoft Azure Emulator 170 | ecf/ 171 | rcf/ 172 | 173 | # Windows Store app package directories and files 174 | AppPackages/ 175 | BundleArtifacts/ 176 | Package.StoreAssociation.xml 177 | _pkginfo.txt 178 | 179 | # Visual Studio cache files 180 | # files ending in .cache can be ignored 181 | *.[Cc]ache 182 | # but keep track of directories ending in .cache 183 | !*.[Cc]ache/ 184 | 185 | # Others 186 | ClientBin/ 187 | ~$* 188 | *~ 189 | *.dbmdl 190 | *.dbproj.schemaview 191 | *.pfx 192 | *.publishsettings 193 | node_modules/ 194 | orleans.codegen.cs 195 | 196 | # Since there are multiple workflows, uncomment next line to ignore bower_components 197 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 198 | #bower_components/ 199 | 200 | # RIA/Silverlight projects 201 | Generated_Code/ 202 | 203 | # Backup & report files from converting an old project file 204 | # to a newer Visual Studio version. Backup files are not needed, 205 | # because we have git ;-) 206 | _UpgradeReport_Files/ 207 | Backup*/ 208 | UpgradeLog*.XML 209 | UpgradeLog*.htm 210 | 211 | # SQL Server files 212 | *.mdf 213 | *.ldf 214 | 215 | # Business Intelligence projects 216 | *.rdl.data 217 | *.bim.layout 218 | *.bim_*.settings 219 | 220 | # Microsoft Fakes 221 | FakesAssemblies/ 222 | 223 | # GhostDoc plugin setting file 224 | *.GhostDoc.xml 225 | 226 | # Node.js Tools for Visual Studio 227 | .ntvs_analysis.dat 228 | 229 | # Visual Studio 6 build log 230 | *.plg 231 | 232 | # Visual Studio 6 workspace options file 233 | *.opt 234 | 235 | # Visual Studio LightSwitch build output 236 | **/*.HTMLClient/GeneratedArtifacts 237 | **/*.DesktopClient/GeneratedArtifacts 238 | **/*.DesktopClient/ModelManifest.xml 239 | **/*.Server/GeneratedArtifacts 240 | **/*.Server/ModelManifest.xml 241 | _Pvt_Extensions 242 | 243 | # Paket dependency manager 244 | .paket/paket.exe 245 | paket-files/ 246 | 247 | # FAKE - F# Make 248 | .fake/ 249 | 250 | # JetBrains Rider 251 | .idea/ 252 | *.sln.iml 253 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tcp-udp-tool 2 | 3 | ![ScreenShot](https://user-images.githubusercontent.com/5458667/35699691-16be5620-0791-11e8-91e9-e54df849fa50.png) 4 | -------------------------------------------------------------------------------- /TcpUdpTool.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25123.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TcpUdpTool", "TcpUdpTool\TcpUdpTool.csproj", "{C3FF0B8C-0EAF-41EF-AEF6-C8C597CF0A7F}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {C3FF0B8C-0EAF-41EF-AEF6-C8C597CF0A7F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {C3FF0B8C-0EAF-41EF-AEF6-C8C597CF0A7F}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {C3FF0B8C-0EAF-41EF-AEF6-C8C597CF0A7F}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {C3FF0B8C-0EAF-41EF-AEF6-C8C597CF0A7F}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /TcpUdpTool/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 65001 15 | 16 | 17 | False 18 | 19 | 20 | 100 21 | 22 | 23 | True 24 | 25 | 26 | True 27 | 28 | 29 | True 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /TcpUdpTool/App.xaml: -------------------------------------------------------------------------------- 1 |  6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /TcpUdpTool/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Configuration; 4 | using System.Data; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | 9 | namespace TcpUdpTool 10 | { 11 | /// 12 | /// Interaction logic for App.xaml 13 | /// 14 | public partial class App : Application 15 | { 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /TcpUdpTool/Model/Data/Transmission.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | 4 | namespace TcpUdpTool.Model.Data 5 | { 6 | public class Transmission : IEquatable, IComparable 7 | { 8 | private static ulong _counter = 0; 9 | private static object _lock = new object(); 10 | 11 | 12 | public enum EType { Sent, Received }; 13 | 14 | private ulong _sequenceNr; 15 | private byte[] _data; 16 | private EType _type; 17 | private DateTime _timestamp; 18 | private IPEndPoint _origin; 19 | private IPEndPoint _destination; 20 | 21 | 22 | public ulong SequenceNr 23 | { 24 | get { return _sequenceNr; } 25 | } 26 | 27 | public byte[] Data 28 | { 29 | get { return _data; } 30 | } 31 | 32 | public EType Type 33 | { 34 | get { return _type; } 35 | } 36 | 37 | public IPEndPoint Origin 38 | { 39 | get { return _origin; } 40 | set{ _origin = value; } 41 | } 42 | 43 | public IPEndPoint Destination 44 | { 45 | get { return _destination; } 46 | set { _destination = value; } 47 | } 48 | 49 | 50 | public DateTime Timestamp 51 | { 52 | get { return _timestamp; } 53 | } 54 | 55 | public bool IsReceived 56 | { 57 | get { return _type == EType.Received; } 58 | } 59 | 60 | public bool IsSent 61 | { 62 | get { return _type == EType.Sent; } 63 | } 64 | 65 | public int Length 66 | { 67 | get { return _data.Length; } 68 | } 69 | 70 | 71 | public Transmission(byte[] data, EType type) 72 | { 73 | lock(_lock) 74 | { 75 | _sequenceNr = _counter++; 76 | } 77 | 78 | _data = data; 79 | _type = type; 80 | _timestamp = DateTime.Now; 81 | } 82 | 83 | public bool Equals(Transmission other) 84 | { 85 | if(other != null) 86 | { 87 | return SequenceNr == other.SequenceNr; 88 | } 89 | 90 | return false; 91 | } 92 | 93 | public override bool Equals(object obj) 94 | { 95 | return Equals(obj as Transmission); 96 | } 97 | 98 | public override int GetHashCode() 99 | { 100 | return (int)SequenceNr; 101 | } 102 | 103 | public int CompareTo(Transmission other) 104 | { 105 | if(SequenceNr > other.SequenceNr) 106 | { 107 | return 1; 108 | } 109 | else if(SequenceNr < other.SequenceNr) 110 | { 111 | return -1; 112 | } 113 | 114 | return 0; 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /TcpUdpTool/Model/Data/TransmissionResult.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | 3 | namespace TcpUdpTool.Model.Data 4 | { 5 | public class TransmissionResult 6 | { 7 | public IPEndPoint From { get; set; } 8 | public IPEndPoint To { get; set; } 9 | 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /TcpUdpTool/Model/EventArg/ReceivedEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using TcpUdpTool.Model.Data; 3 | 4 | namespace TcpUdpTool.Model.EventArg 5 | { 6 | public class ReceivedEventArgs : EventArgs 7 | { 8 | 9 | public Transmission Message { get; private set; } 10 | 11 | public ReceivedEventArgs(Transmission message) 12 | { 13 | Message = message; 14 | } 15 | 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /TcpUdpTool/Model/Formatter/HexFormatter.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using TcpUdpTool.Model.Data; 3 | 4 | namespace TcpUdpTool.Model.Formatter 5 | { 6 | public class HexFormatter : IFormatter 7 | { 8 | private StringBuilder _builder = new StringBuilder(); 9 | 10 | public string Format(Transmission msg) 11 | { 12 | _builder.Clear(); 13 | 14 | int count = 0; 15 | foreach (byte b in msg.Data) 16 | { 17 | _builder.Append(b.ToString("X2")); 18 | 19 | if (++count % 16 == 0) 20 | { 21 | _builder.AppendLine(); 22 | } 23 | else 24 | { 25 | _builder.Append(' '); 26 | } 27 | } 28 | 29 | return _builder.ToString(); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /TcpUdpTool/Model/Formatter/IFormatter.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using TcpUdpTool.Model.Data; 3 | 4 | namespace TcpUdpTool.Model.Formatter 5 | { 6 | public interface IFormatter 7 | { 8 | string Format(Transmission msg); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /TcpUdpTool/Model/Formatter/PlainTextFormatter.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using TcpUdpTool.Model.Data; 3 | 4 | namespace TcpUdpTool.Model.Formatter 5 | { 6 | public class PlainTextFormatter : IFormatter 7 | { 8 | private Encoding _encoding; 9 | 10 | public PlainTextFormatter(Encoding encoding = null) 11 | { 12 | _encoding = encoding; 13 | if (_encoding == null) 14 | { 15 | _encoding = Encoding.Default; 16 | } 17 | } 18 | 19 | public string Format(Transmission msg) 20 | { 21 | return _encoding.GetString(msg.Data); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /TcpUdpTool/Model/Parser/HexParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Text; 4 | 5 | namespace TcpUdpTool.Model.Parser 6 | { 7 | public class HexParser : IParser 8 | { 9 | public byte[] Parse(string text, Encoding encoding = null) 10 | { 11 | string[] parts = text.Split(new char[0], StringSplitOptions.RemoveEmptyEntries); 12 | byte[] data = new byte[parts.Length]; 13 | 14 | for(int i = 0; i < parts.Length; i++) 15 | { 16 | try 17 | { 18 | if (parts[i].Length > 2) 19 | throw new FormatException(); 20 | 21 | data[i] = (byte)uint.Parse(parts[i], NumberStyles.AllowHexSpecifier); 22 | } 23 | catch(FormatException) 24 | { 25 | throw new FormatException("Incorrect sequence, " + parts[i] + " is not a 8-bit hexadecimal number."); 26 | } 27 | } 28 | 29 | return data; 30 | } 31 | 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /TcpUdpTool/Model/Parser/IParser.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace TcpUdpTool.Model.Parser 4 | { 5 | public interface IParser 6 | { 7 | byte[] Parse(string text, Encoding encoding = null); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /TcpUdpTool/Model/Parser/PlainTextParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Text; 5 | 6 | namespace TcpUdpTool.Model.Parser 7 | { 8 | public class PlainTextParser : IParser 9 | { 10 | 11 | public byte[] Parse(string text, Encoding encoding = null) 12 | { 13 | if(encoding == null) 14 | { 15 | encoding = Encoding.UTF8; 16 | } 17 | 18 | List res = new List(text.Length * 2); 19 | StringBuilder cache = new StringBuilder(); 20 | 21 | bool escape = false; 22 | bool escapeHex = false; 23 | bool escapeUnicode = false; 24 | string hexStr = ""; 25 | 26 | foreach (char c in text) 27 | { 28 | if (escape) 29 | { 30 | if (c == '\\') 31 | cache.Append('\\'); 32 | else if (c == '0') 33 | cache.Append('\0'); 34 | else if (c == 'a') 35 | cache.Append('\a'); 36 | else if (c == 'b') 37 | cache.Append('\b'); 38 | else if (c == 'f') 39 | cache.Append('\f'); 40 | else if (c == 'n') 41 | cache.Append('\n'); 42 | else if (c == 'r') 43 | cache.Append('\r'); 44 | else if (c == 't') 45 | cache.Append('\t'); 46 | else if (c == 'v') 47 | cache.Append('\v'); 48 | else if (c == 'x') 49 | escapeHex = true; 50 | else if (c == 'u') 51 | escapeUnicode = true; 52 | else 53 | throw new FormatException("Incorrect escape sequence found, \\" 54 | + c + " is not allowed."); 55 | 56 | escape = false; 57 | } 58 | else if (escapeHex) 59 | { 60 | hexStr += c; 61 | 62 | if (hexStr.Length == 2) 63 | { 64 | try 65 | { 66 | // adding binary data that should not be character encoded, 67 | // encode and move previous data to result and clear cache. 68 | res.AddRange(encoding.GetBytes(cache.ToString())); 69 | cache.Clear(); 70 | res.Add((byte)int.Parse(hexStr, NumberStyles.AllowHexSpecifier)); 71 | } 72 | catch (FormatException) 73 | { 74 | throw new FormatException("Incorrect escape sequence found, \\x" 75 | + hexStr + " is not a 8-bit hexadecimal number."); 76 | } 77 | 78 | escapeHex = false; 79 | hexStr = ""; 80 | } 81 | } 82 | else if (escapeUnicode) 83 | { 84 | hexStr += c; 85 | 86 | if (hexStr.Length == 4) 87 | { 88 | try 89 | { 90 | cache.Append(Convert.ToChar(int.Parse(hexStr, NumberStyles.AllowHexSpecifier))); 91 | } 92 | catch (FormatException) 93 | { 94 | throw new FormatException("Incorrect escape sequence found, \\u" 95 | + hexStr + " is not a 16-bit hexadecimal unicode character code."); 96 | } 97 | 98 | escapeUnicode = false; 99 | hexStr = ""; 100 | } 101 | } 102 | else 103 | { 104 | if (c == '\\') 105 | { 106 | // next char is an escape sequence. 107 | escape = true; 108 | } 109 | else 110 | { 111 | cache.Append(c); 112 | } 113 | } 114 | } 115 | 116 | res.AddRange(encoding.GetBytes(cache.ToString())); 117 | 118 | return res.ToArray(); 119 | } 120 | 121 | } 122 | 123 | } 124 | -------------------------------------------------------------------------------- /TcpUdpTool/Model/RateMonitor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Threading; 4 | using TcpUdpTool.Model.Data; 5 | 6 | namespace TcpUdpTool.Model 7 | { 8 | public class RateMonitor : IDisposable 9 | { 10 | 11 | #region private members 12 | 13 | private ulong _recevedBytes; 14 | private ulong _sentBytes; 15 | 16 | private ulong _recvSinceLastUpdate; 17 | private ulong _sentSinceLastUpdate; 18 | 19 | private Timer _updateTimer; 20 | private Stopwatch _stopwatch; 21 | 22 | #endregion 23 | 24 | #region public properties 25 | 26 | public ulong TotalSentBytes { get; set; } 27 | public ulong TotalReceivedBytes { get; set; } 28 | public ulong CurrentSendRate { get; set; } 29 | public ulong CurrentReceiveRate { get; set; } 30 | 31 | #endregion 32 | 33 | #region constructors 34 | 35 | public RateMonitor() 36 | { 37 | 38 | } 39 | 40 | #endregion 41 | 42 | #region public functions 43 | 44 | public void Start() 45 | { 46 | if(_updateTimer == null) 47 | { 48 | Reset(); 49 | _stopwatch = new Stopwatch(); 50 | _updateTimer = new Timer(OnUpdate, null, 0, 500); 51 | } 52 | } 53 | 54 | public void Stop() 55 | { 56 | if (_updateTimer != null) 57 | { 58 | _updateTimer.Dispose(); 59 | _updateTimer = null; 60 | } 61 | Reset(); 62 | } 63 | 64 | public void Reset() 65 | { 66 | lock(this) 67 | { 68 | _recevedBytes = 0; 69 | _sentBytes = 0; 70 | _recvSinceLastUpdate = 0; 71 | _sentSinceLastUpdate = 0; 72 | TotalSentBytes = 0; 73 | TotalReceivedBytes = 0; 74 | CurrentReceiveRate = 0; 75 | CurrentSendRate = 0; 76 | } 77 | } 78 | 79 | public void NoteReceived(Transmission data) 80 | { 81 | lock(this) 82 | { 83 | _recevedBytes += (uint)data.Length; 84 | _recvSinceLastUpdate+= (uint)data.Length; 85 | } 86 | } 87 | 88 | public void NoteSent(Transmission data) 89 | { 90 | lock(this) 91 | { 92 | _sentBytes += (uint)data.Length; 93 | _sentSinceLastUpdate += (uint)data.Length; 94 | } 95 | } 96 | 97 | #endregion 98 | 99 | #region private functions 100 | 101 | private void OnUpdate(object state) 102 | { 103 | lock (this) 104 | { 105 | _stopwatch.Stop(); 106 | ulong millis = (ulong)_stopwatch.ElapsedMilliseconds; 107 | if (millis > 0) 108 | { 109 | TotalSentBytes = _sentBytes; 110 | TotalReceivedBytes = _recevedBytes; 111 | CurrentSendRate = (_sentSinceLastUpdate * 8 * 1000) / millis; 112 | CurrentReceiveRate = (_recvSinceLastUpdate * 8 * 1000) / millis; 113 | _sentSinceLastUpdate = 0; 114 | _recvSinceLastUpdate = 0; 115 | } 116 | _stopwatch.Reset(); 117 | _stopwatch.Start(); 118 | } 119 | } 120 | 121 | public void Dispose() 122 | { 123 | _updateTimer?.Dispose(); 124 | } 125 | 126 | #endregion 127 | 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /TcpUdpTool/Model/TcpClient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Threading.Tasks; 4 | using TcpUdpTool.Model.Data; 5 | using TcpUdpTool.Model.EventArg; 6 | using TcpUdpTool.Model.Util; 7 | 8 | namespace TcpUdpTool.Model 9 | { 10 | public class TcpClient : IDisposable 11 | { 12 | 13 | public event EventHandler Received; 14 | public event EventHandler StatusChanged; 15 | 16 | private System.Net.Sockets.TcpClient _tcpClient; 17 | private byte[] _buffer; 18 | 19 | 20 | public TcpClient() 21 | { 22 | _buffer = new byte[8192]; 23 | } 24 | 25 | 26 | public async Task ConnectAsync(string host, int port) 27 | { 28 | if (_tcpClient != null && _tcpClient.Connected) 29 | return; // already connected 30 | 31 | OnConnectStatusChanged(TcpClientStatusEventArgs.EConnectStatus.Connecting); 32 | 33 | try 34 | { 35 | // resolve ip address 36 | IPAddress addr = await NetworkUtils.DnsResolveAsync(host); 37 | 38 | _tcpClient = new System.Net.Sockets.TcpClient(addr.AddressFamily); 39 | 40 | await _tcpClient.ConnectAsync(addr, port); 41 | OnConnectStatusChanged(TcpClientStatusEventArgs.EConnectStatus.Connected); 42 | 43 | StartReceive(); 44 | } 45 | catch(Exception) 46 | { 47 | Disconnect(); 48 | throw; 49 | } 50 | } 51 | 52 | public void Disconnect() 53 | { 54 | if(_tcpClient != null) 55 | { 56 | _tcpClient.Close(); 57 | _tcpClient = null; 58 | } 59 | 60 | OnConnectStatusChanged(TcpClientStatusEventArgs.EConnectStatus.Disconnected); 61 | } 62 | 63 | public async Task SendAsync(Transmission msg) 64 | { 65 | if(!_tcpClient.Connected) 66 | { 67 | return null; 68 | } 69 | 70 | IPEndPoint from = _tcpClient.Client.LocalEndPoint as IPEndPoint; 71 | IPEndPoint to = _tcpClient.Client.RemoteEndPoint as IPEndPoint; 72 | 73 | await _tcpClient.GetStream().WriteAsync(msg.Data, 0, msg.Length); 74 | 75 | return new TransmissionResult() { From = from, To = to }; 76 | } 77 | 78 | 79 | private void StartReceive() 80 | { 81 | Task.Run(async () => 82 | { 83 | while(_tcpClient != null) 84 | { 85 | try 86 | { 87 | int read = await _tcpClient.GetStream().ReadAsync(_buffer, 0, _buffer.Length); 88 | 89 | if(read > 0) 90 | { 91 | byte[] data = new byte[read]; 92 | Array.Copy(_buffer, data, read); 93 | 94 | 95 | Transmission msg = new Transmission(data, Transmission.EType.Received); 96 | msg.Destination = _tcpClient.Client.LocalEndPoint as IPEndPoint; 97 | msg.Origin = _tcpClient.Client.RemoteEndPoint as IPEndPoint; 98 | 99 | Received?.Invoke(this, new ReceivedEventArgs(msg)); 100 | } 101 | else 102 | { 103 | // server closes connection. 104 | Disconnect(); 105 | break; 106 | } 107 | } 108 | catch(Exception e) 109 | when(e is ObjectDisposedException || e is InvalidOperationException) 110 | { 111 | Disconnect(); 112 | break; 113 | } 114 | } 115 | }); 116 | } 117 | 118 | private void OnConnectStatusChanged(TcpClientStatusEventArgs.EConnectStatus status) 119 | { 120 | IPEndPoint ep = status == TcpClientStatusEventArgs.EConnectStatus.Connected ? 121 | _tcpClient.Client.RemoteEndPoint as IPEndPoint : null; 122 | 123 | StatusChanged?.Invoke(false, new TcpClientStatusEventArgs(status, ep)); 124 | } 125 | 126 | public void Dispose() 127 | { 128 | _tcpClient?.Close(); 129 | _tcpClient = null; 130 | } 131 | 132 | } 133 | 134 | public class TcpClientStatusEventArgs : EventArgs 135 | { 136 | public enum EConnectStatus { Disconnected, Connecting, Connected }; 137 | 138 | 139 | public EConnectStatus Status { get; private set; } 140 | public IPEndPoint RemoteEndPoint { get; private set; } 141 | 142 | public TcpClientStatusEventArgs(EConnectStatus status, IPEndPoint remoteEndPoint) 143 | { 144 | Status = status; 145 | RemoteEndPoint = remoteEndPoint; 146 | } 147 | 148 | } 149 | 150 | } 151 | -------------------------------------------------------------------------------- /TcpUdpTool/Model/TcpServer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net; 5 | using System.Net.Sockets; 6 | using System.Threading.Tasks; 7 | using TcpUdpTool.Model.Data; 8 | using TcpUdpTool.Model.EventArg; 9 | 10 | namespace TcpUdpTool.Model 11 | { 12 | 13 | public class TcpServer : IDisposable 14 | { 15 | public event EventHandler Received; 16 | public event EventHandler StatusChanged; 17 | 18 | private TcpListener _tcpServer; 19 | private List _connectedClients; 20 | private byte[] _buffer; 21 | 22 | 23 | public int NumConnectedClients 24 | { 25 | get 26 | { 27 | int count = 0; 28 | lock(_connectedClients) 29 | { 30 | count = _connectedClients.Count; 31 | } 32 | 33 | return count; 34 | } 35 | } 36 | 37 | 38 | public TcpServer() 39 | { 40 | _connectedClients = new List(); 41 | _buffer = new byte[8192]; 42 | } 43 | 44 | public void Start(IPAddress ip, int port) 45 | { 46 | if(_tcpServer != null) 47 | return; 48 | 49 | try 50 | { 51 | _tcpServer = new TcpListener(new IPEndPoint(ip, port)); 52 | _tcpServer.Start(0); 53 | OnStatusChanged(TcpServerStatusEventArgs.EServerStatus.Started); 54 | } 55 | catch(Exception) 56 | { 57 | Stop(); 58 | throw; 59 | } 60 | 61 | StartAcceptClient(); 62 | } 63 | 64 | public void Stop() 65 | { 66 | Disconnect(); 67 | if(_tcpServer != null) 68 | { 69 | _tcpServer.Stop(); 70 | _tcpServer = null; 71 | OnStatusChanged(TcpServerStatusEventArgs.EServerStatus.Stopped); 72 | } 73 | } 74 | 75 | public async Task> SendAsync(Transmission msg) 76 | { 77 | var result = new List(); 78 | 79 | List copy; 80 | lock (_connectedClients) 81 | { 82 | copy = _connectedClients.ToList(); 83 | } 84 | 85 | foreach (var c in copy) 86 | { 87 | IPEndPoint from = c.Client.LocalEndPoint as IPEndPoint; 88 | IPEndPoint to = c.Client.RemoteEndPoint as IPEndPoint; 89 | 90 | try 91 | { 92 | await c.GetStream().WriteAsync(msg.Data, 0, msg.Length); 93 | result.Add(new TransmissionResult { From = from, To = to }); 94 | } 95 | catch(Exception) 96 | { 97 | DisconnectClient(c); 98 | } 99 | } 100 | 101 | return result; 102 | } 103 | 104 | public void Disconnect() 105 | { 106 | // close client connection. 107 | 108 | List copy; 109 | lock(_connectedClients) 110 | { 111 | copy = _connectedClients.ToList(); 112 | } 113 | 114 | foreach(var c in copy) 115 | { 116 | OnClientStatusChanged(TcpServerStatusEventArgs.EServerStatus.ClientDisconnected, c); 117 | c.Close(); 118 | c.Dispose(); 119 | } 120 | 121 | lock(_connectedClients) 122 | { 123 | _connectedClients.Clear(); 124 | } 125 | } 126 | 127 | 128 | private void StartAcceptClient() 129 | { 130 | Task.Run(async () => 131 | { 132 | while(_tcpServer != null) 133 | { 134 | try 135 | { 136 | var client = await _tcpServer.AcceptTcpClientAsync(); 137 | lock(_connectedClients) 138 | { 139 | _connectedClients.Add(client); 140 | } 141 | OnClientStatusChanged(TcpServerStatusEventArgs.EServerStatus.ClientConnected, client); 142 | StartReceive(client); 143 | } 144 | catch(Exception ex) 145 | when(ex is SocketException || ex is ObjectDisposedException) 146 | { 147 | Stop(); 148 | break; 149 | } 150 | } 151 | }); 152 | } 153 | 154 | private void StartReceive(System.Net.Sockets.TcpClient client) 155 | { 156 | Task.Run(async () => 157 | { 158 | bool stop = false; 159 | while (!stop) 160 | { 161 | try 162 | { 163 | int read = await client.GetStream().ReadAsync(_buffer, 0, _buffer.Length); 164 | 165 | if (read > 0) 166 | { 167 | byte[] data = new byte[read]; 168 | Array.Copy(_buffer, data, read); 169 | 170 | Transmission msg = new Transmission(data, Transmission.EType.Received); 171 | msg.Destination = client.Client.LocalEndPoint as IPEndPoint; 172 | msg.Origin = client.Client.RemoteEndPoint as IPEndPoint; 173 | 174 | Received?.Invoke(this, new ReceivedEventArgs(msg)); 175 | } 176 | else 177 | { 178 | // server closed connection. 179 | DisconnectClient(client); 180 | stop = true; 181 | } 182 | } 183 | catch (Exception e) 184 | when (e is ObjectDisposedException || e is InvalidOperationException) 185 | { 186 | DisconnectClient(client); 187 | stop = true; 188 | } 189 | } 190 | }); 191 | } 192 | 193 | private void OnStatusChanged(TcpServerStatusEventArgs.EServerStatus status) 194 | { 195 | StatusChanged?.Invoke(this, new TcpServerStatusEventArgs(status, 196 | _tcpServer?.LocalEndpoint as IPEndPoint, null)); 197 | } 198 | 199 | private void OnClientStatusChanged(TcpServerStatusEventArgs.EServerStatus status, System.Net.Sockets.TcpClient client) 200 | { 201 | StatusChanged?.Invoke(this, new TcpServerStatusEventArgs(status, 202 | _tcpServer?.LocalEndpoint as IPEndPoint, client.Client.RemoteEndPoint as IPEndPoint)); 203 | } 204 | 205 | private void DisconnectClient(System.Net.Sockets.TcpClient client) 206 | { 207 | lock(_connectedClients) 208 | { 209 | _connectedClients.Remove(client); 210 | } 211 | 212 | OnClientStatusChanged(TcpServerStatusEventArgs.EServerStatus.ClientDisconnected, client); 213 | client.Close(); 214 | client.Dispose(); 215 | } 216 | 217 | public void Dispose() 218 | { 219 | lock (_connectedClients) 220 | { 221 | foreach (var c in _connectedClients) 222 | { 223 | c.Close(); 224 | c.Dispose(); 225 | } 226 | 227 | _connectedClients.Clear(); 228 | } 229 | 230 | _tcpServer?.Stop(); 231 | _tcpServer = null; 232 | } 233 | } 234 | 235 | public class TcpServerStatusEventArgs : EventArgs 236 | { 237 | public enum EServerStatus { Started, Stopped, ClientConnected, ClientDisconnected } 238 | 239 | public EServerStatus Status { get; private set; } 240 | public IPEndPoint ServerInfo { get; private set; } 241 | public IPEndPoint ClientInfo { get; private set; } 242 | 243 | 244 | public TcpServerStatusEventArgs(EServerStatus status, IPEndPoint serverInfo, IPEndPoint clientInfo = null) 245 | { 246 | Status = status; 247 | ServerInfo = serverInfo; 248 | ClientInfo = clientInfo; 249 | } 250 | } 251 | 252 | } 253 | -------------------------------------------------------------------------------- /TcpUdpTool/Model/UdpClientServer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Net.Sockets; 4 | using System.Threading.Tasks; 5 | using TcpUdpTool.Model.Data; 6 | using TcpUdpTool.Model.EventArg; 7 | using TcpUdpTool.Model.Util; 8 | 9 | namespace TcpUdpTool.Model 10 | { 11 | public class UdpClientServer : IDisposable 12 | { 13 | private UdpClient _udpClient; 14 | 15 | public event EventHandler Received; 16 | public event EventHandler StatusChanged; 17 | 18 | 19 | public UdpClientServer() 20 | { 21 | 22 | } 23 | 24 | public void Start(IPAddress ip, int port) 25 | { 26 | if (_udpClient != null) 27 | return; 28 | 29 | _udpClient = new UdpClient(ip.AddressFamily); 30 | _udpClient.EnableBroadcast = true; 31 | 32 | _udpClient.Client.SetSocketOption( 33 | SocketOptionLevel.Socket, 34 | SocketOptionName.ReuseAddress, 35 | true 36 | ); 37 | 38 | _udpClient.Client.Bind(new IPEndPoint(ip, port)); 39 | 40 | StatusChanged?.Invoke(this, new UdpClientServerStatusEventArgs( 41 | UdpClientServerStatusEventArgs.EServerStatus.Started, 42 | _udpClient.Client.LocalEndPoint as IPEndPoint)); 43 | 44 | StartReceive(); 45 | } 46 | 47 | public void Stop() 48 | { 49 | if(_udpClient != null) 50 | { 51 | _udpClient.Close(); 52 | _udpClient = null; 53 | 54 | StatusChanged?.Invoke(this, new UdpClientServerStatusEventArgs( 55 | UdpClientServerStatusEventArgs.EServerStatus.Stopped)); 56 | } 57 | } 58 | 59 | public async Task SendAsync(string host, int port, Transmission msg) 60 | { 61 | // resolve, prefer ipv6 if currently in use. 62 | IPAddress addr = await NetworkUtils.DnsResolveAsync(host, _udpClient != null && 63 | _udpClient.Client.LocalEndPoint.AddressFamily == AddressFamily.InterNetworkV6); 64 | 65 | IPEndPoint from = null; 66 | IPEndPoint to = new IPEndPoint(addr, port); 67 | 68 | if(_udpClient != null) 69 | { 70 | if(_udpClient.Client.AddressFamily != addr.AddressFamily) 71 | { 72 | Func IpvToString = (family) => 73 | { 74 | return family == AddressFamily.InterNetworkV6 ? "IPv6" : "IPv4"; 75 | }; 76 | 77 | throw new InvalidOperationException( 78 | "Cannot send UDP packet using " + IpvToString(addr.AddressFamily) + 79 | " when bound to an " + IpvToString(_udpClient.Client.AddressFamily) + " interface."); 80 | } 81 | 82 | from = _udpClient.Client.LocalEndPoint as IPEndPoint; 83 | await _udpClient.SendAsync(msg.Data, msg.Data.Length, to); 84 | } 85 | else 86 | { 87 | // send from new udp client, don't care about any response 88 | // since we are not listening. 89 | using (UdpClient tmpClient = new UdpClient(addr.AddressFamily)) 90 | { 91 | from = tmpClient.Client.LocalEndPoint as IPEndPoint; 92 | tmpClient.EnableBroadcast = true; 93 | await tmpClient.SendAsync(msg.Data, msg.Data.Length, to); 94 | } 95 | } 96 | 97 | return new TransmissionResult() { From = from, To = to }; 98 | } 99 | 100 | 101 | private void StartReceive() 102 | { 103 | Task.Run(async () => 104 | { 105 | while (_udpClient != null) 106 | { 107 | try 108 | { 109 | UdpReceiveResult res = await _udpClient.ReceiveAsync(); 110 | 111 | Transmission msg = new Transmission(res.Buffer, Transmission.EType.Received); 112 | msg.Origin = res.RemoteEndPoint; 113 | msg.Destination = _udpClient.Client.LocalEndPoint as IPEndPoint; 114 | 115 | Received?.Invoke(this, new ReceivedEventArgs(msg)); 116 | } 117 | catch (SocketException ex) 118 | { 119 | // Ignore this error, triggered after sending 120 | // a packet to an unreachable port. UDP is not 121 | // reliable anyway, this can safetly be ignored. 122 | if(ex.ErrorCode != 10054) 123 | { 124 | Stop(); 125 | break; 126 | } 127 | } 128 | catch(Exception) 129 | { 130 | Stop(); 131 | break; // end receive; 132 | } 133 | } 134 | }); 135 | } 136 | 137 | public void Dispose() 138 | { 139 | _udpClient?.Close(); 140 | _udpClient = null; 141 | } 142 | } 143 | 144 | public class UdpClientServerStatusEventArgs : EventArgs 145 | { 146 | public enum EServerStatus { Started, Stopped }; 147 | 148 | public EServerStatus ServerStatus { get; private set; } 149 | public IPEndPoint ServerInfo { get; private set; } 150 | 151 | public UdpClientServerStatusEventArgs(EServerStatus status, IPEndPoint info = null) 152 | { 153 | ServerStatus = status; 154 | ServerInfo = info; 155 | } 156 | } 157 | 158 | } 159 | -------------------------------------------------------------------------------- /TcpUdpTool/Model/Util/DialogUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | 8 | namespace TcpUdpTool.Model.Util 9 | { 10 | public class DialogUtils 11 | { 12 | 13 | public static void ShowErrorDialog(string message) 14 | { 15 | MessageBox.Show(message, "Error", MessageBoxButton.OK, MessageBoxImage.Error); 16 | } 17 | 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /TcpUdpTool/Model/Util/NetworkUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net; 5 | using System.Net.NetworkInformation; 6 | using System.Net.Sockets; 7 | using System.Threading.Tasks; 8 | 9 | namespace TcpUdpTool.Model.Util 10 | { 11 | 12 | public static class NetworkUtils 13 | { 14 | 15 | public static event Action NetworkInterfaceChange; 16 | 17 | static NetworkUtils() 18 | { 19 | NetworkChange.NetworkAddressChanged += (s, e) => NetworkInterfaceChange?.Invoke(); 20 | NetworkChange.NetworkAvailabilityChanged += (s, e) => NetworkInterfaceChange?.Invoke(); 21 | } 22 | 23 | public static List GetActiveInterfaces() 24 | { 25 | var result = new List(); 26 | 27 | foreach (var adapter in System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces()) 28 | { 29 | if ((!adapter.Supports(NetworkInterfaceComponent.IPv4) && 30 | !adapter.Supports(NetworkInterfaceComponent.IPv6)) || 31 | adapter.OperationalStatus != OperationalStatus.Up) 32 | { 33 | continue; 34 | } 35 | 36 | var ni = new NetworkInterface(); 37 | ni.Id = adapter.Id; 38 | ni.Name = adapter.Name; 39 | ni.Description = adapter.Description; 40 | 41 | var aip = adapter.GetIPProperties(); 42 | 43 | if (adapter.Supports(NetworkInterfaceComponent.IPv4)) 44 | { 45 | ni.IPv4.Index = aip.GetIPv4Properties().Index; 46 | } 47 | 48 | if (adapter.Supports(NetworkInterfaceComponent.IPv6)) 49 | { 50 | ni.IPv6.Index = aip.GetIPv6Properties().Index; 51 | } 52 | 53 | foreach (var uip in aip.UnicastAddresses) 54 | { 55 | if (uip.Address.AddressFamily == AddressFamily.InterNetwork) 56 | { 57 | ni.IPv4.Addresses.Add(uip.Address); 58 | } 59 | else if (uip.Address.AddressFamily == AddressFamily.InterNetworkV6) 60 | { 61 | ni.IPv6.Addresses.Add(uip.Address); 62 | } 63 | } 64 | 65 | result.Add(ni); 66 | } 67 | 68 | return result; 69 | } 70 | 71 | public static int GetBestMulticastInterfaceIndex(IPAddress localInterface) 72 | { 73 | var interfaces = GetActiveInterfaces(); 74 | foreach(var intf in interfaces) 75 | { 76 | if (localInterface.AddressFamily == AddressFamily.InterNetwork) 77 | { 78 | if (intf.IPv4.Addresses.Contains(localInterface)) 79 | { 80 | return intf.IPv4.Index; 81 | } 82 | } 83 | else if (localInterface.AddressFamily == AddressFamily.InterNetworkV6) 84 | { 85 | if (intf.IPv6.Addresses.Contains(localInterface)) 86 | { 87 | return intf.IPv6.Index; 88 | } 89 | } 90 | } 91 | 92 | return -1; 93 | } 94 | 95 | public static bool IsMulticast(IPAddress ipAddress) 96 | { 97 | bool isMulticast = false; 98 | 99 | if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6) 100 | { 101 | // In IPv6 Multicast addresses first byte is 0xFF 102 | byte[] bytes = ipAddress.GetAddressBytes(); 103 | isMulticast = (bytes[0] == 0xff); 104 | } 105 | else if (ipAddress.AddressFamily == AddressFamily.InterNetwork) 106 | { 107 | // In IPv4 Multicast addresses first byte is between 224 and 239 108 | byte[] bytes = ipAddress.GetAddressBytes(); 109 | isMulticast = (bytes[0] >= 224) && (bytes[0] <= 239); 110 | } 111 | 112 | return isMulticast; 113 | } 114 | 115 | public static bool IsSourceSpecificMulticast(IPAddress ipAddress) 116 | { 117 | bool isSSM = false; 118 | 119 | if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6) 120 | { 121 | // In IPv6 SSM first byte is 0xFF and second byte is 0x3X 122 | byte[] bytes = ipAddress.GetAddressBytes(); 123 | isSSM = (bytes[0] == 0xff && (bytes[1] >> 4) == 0x03); 124 | } 125 | else if (ipAddress.AddressFamily == AddressFamily.InterNetwork) 126 | { 127 | // In IPv4 SSM first byte is 232 128 | byte[] addressBytes = ipAddress.GetAddressBytes(); 129 | isSSM = addressBytes[0] == 232; 130 | } 131 | 132 | return isSSM; 133 | } 134 | 135 | public static bool IsValidPort(int port, bool allowZero = false) 136 | { 137 | return (port >= (allowZero ? 0 : 1)) && port < 65536; 138 | } 139 | 140 | public static async Task DnsResolveAsync(string hostOrAddress, bool favorIpV6 = false) 141 | { 142 | var favoredFamily = favorIpV6 ? AddressFamily.InterNetworkV6 : AddressFamily.InterNetwork; 143 | var addrs = await Dns.GetHostAddressesAsync(hostOrAddress); 144 | return addrs.FirstOrDefault(addr => addr.AddressFamily == favoredFamily) 145 | ?? addrs.FirstOrDefault(); 146 | } 147 | 148 | } 149 | 150 | public class NetworkInterface 151 | { 152 | public class IPInterface 153 | { 154 | public IPInterface() 155 | { 156 | Index = -1; 157 | Addresses = new List(); 158 | } 159 | 160 | public int Index { get; set; } 161 | public List Addresses { get; } 162 | } 163 | 164 | public NetworkInterface() 165 | { 166 | IPv4 = new IPInterface(); 167 | IPv6 = new IPInterface(); 168 | } 169 | 170 | public string Id { get; set; } 171 | 172 | public string Name { get; set; } 173 | 174 | public string Description { get; set; } 175 | 176 | public IPInterface IPv4 { get; private set; } 177 | 178 | public IPInterface IPv6 { get; private set; } 179 | 180 | } 181 | 182 | } 183 | -------------------------------------------------------------------------------- /TcpUdpTool/Model/Util/SettingsUtils.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace TcpUdpTool.Model.Util 4 | { 5 | class SettingsUtils 6 | { 7 | public static Encoding GetEncoding() 8 | { 9 | int codePage = Properties.Settings.Default.Encoding; 10 | if(codePage == 0) 11 | { 12 | return Encoding.Default; 13 | } 14 | 15 | return Encoding.GetEncoding(codePage); 16 | } 17 | 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /TcpUdpTool/Model/Util/StringFormatUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | namespace TcpUdpTool.Model.Util 3 | { 4 | public class StringFormatUtils 5 | { 6 | 7 | public static string GetSizeAsString(ulong bytes) 8 | { 9 | ulong B = 0, KB = 1024, MB = KB * 1024, GB = MB * 1024, TB = GB * 1024; 10 | double size = bytes; 11 | string suffix = nameof(B); 12 | 13 | if (bytes >= TB) 14 | { 15 | size = Math.Round((double)bytes / TB, 2); 16 | suffix = nameof(TB); 17 | } 18 | else if (bytes >= GB) 19 | { 20 | size = Math.Round((double)bytes / GB, 2); 21 | suffix = nameof(GB); 22 | } 23 | else if (bytes >= MB) 24 | { 25 | size = Math.Round((double)bytes / MB, 2); 26 | suffix = nameof(MB); 27 | } 28 | else if (bytes >= KB) 29 | { 30 | size = Math.Round((double)bytes / KB, 2); 31 | suffix = nameof(KB); 32 | } 33 | 34 | return $"{size} {suffix}"; 35 | } 36 | 37 | public static string GetRateAsString(ulong bitsPerSecond) 38 | { 39 | ulong b = 0, Kb = 1000, Mb = Kb * 1000, Gb = Mb * 1000, Tb = Gb * 1000; 40 | double rate = bitsPerSecond; 41 | string suffix = nameof(b); 42 | 43 | if (bitsPerSecond >= Tb) 44 | { 45 | rate = Math.Round((double)bitsPerSecond / Tb, 2); 46 | suffix = nameof(Tb); 47 | } 48 | else if (bitsPerSecond >= Gb) 49 | { 50 | rate = Math.Round((double)bitsPerSecond / Gb, 2); 51 | suffix = nameof(Gb); 52 | } 53 | else if (bitsPerSecond >= Mb) 54 | { 55 | rate = Math.Round((double)bitsPerSecond / Mb, 2); 56 | suffix = nameof(Mb); 57 | } 58 | else if (bitsPerSecond >= Kb) 59 | { 60 | rate = Math.Round((double)bitsPerSecond / Kb, 2); 61 | suffix = nameof(Kb); 62 | } 63 | 64 | return $"{rate} {suffix}ps"; 65 | } 66 | 67 | 68 | 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /TcpUdpTool/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Resources; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using System.Windows; 6 | 7 | // General Information about an assembly is controlled through the following 8 | // set of attributes. Change these attribute values to modify the information 9 | // associated with an assembly. 10 | [assembly: AssemblyTitle("TcpUdpTool")] 11 | [assembly: AssemblyDescription("")] 12 | [assembly: AssemblyConfiguration("")] 13 | [assembly: AssemblyCompany("Daniel Nilsson")] 14 | [assembly: AssemblyProduct("TcpUdpTool")] 15 | [assembly: AssemblyCopyright("Copyright © 2022 Daniel Nilsson")] 16 | [assembly: AssemblyTrademark("")] 17 | [assembly: AssemblyCulture("")] 18 | 19 | // Setting ComVisible to false makes the types in this assembly not visible 20 | // to COM components. If you need to access a type in this assembly from 21 | // COM, set the ComVisible attribute to true on that type. 22 | [assembly: ComVisible(false)] 23 | 24 | //In order to begin building localizable applications, set 25 | //CultureYouAreCodingWith in your .csproj file 26 | //inside a . For example, if you are using US english 27 | //in your source files, set the to en-US. Then uncomment 28 | //the NeutralResourceLanguage attribute below. Update the "en-US" in 29 | //the line below to match the UICulture setting in the project file. 30 | 31 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] 32 | 33 | 34 | [assembly: ThemeInfo( 35 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 36 | //(used if a resource is not found in the page, 37 | // or application resource dictionaries) 38 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 39 | //(used if a resource is not found in the page, 40 | // app, or any theme specific resource dictionaries) 41 | )] 42 | 43 | 44 | // Version information for an assembly consists of the following four values: 45 | // 46 | // Major Version 47 | // Minor Version 48 | // Build Number 49 | // Revision 50 | // 51 | // You can specify all the values or you can default the Build and Revision Numbers 52 | // by using the '*' as shown below: 53 | // [assembly: AssemblyVersion("1.0.*")] 54 | [assembly: AssemblyVersion("1.1.0.0")] 55 | [assembly: AssemblyFileVersion("1.1.0.0")] 56 | [assembly: NeutralResourcesLanguage("en")] 57 | 58 | -------------------------------------------------------------------------------- /TcpUdpTool/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace TcpUdpTool.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("TcpUdpTool.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /TcpUdpTool/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | -------------------------------------------------------------------------------- /TcpUdpTool/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace TcpUdpTool.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.10.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | 26 | [global::System.Configuration.UserScopedSettingAttribute()] 27 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 28 | [global::System.Configuration.DefaultSettingValueAttribute("65001")] 29 | public int Encoding { 30 | get { 31 | return ((int)(this["Encoding"])); 32 | } 33 | set { 34 | this["Encoding"] = value; 35 | } 36 | } 37 | 38 | [global::System.Configuration.UserScopedSettingAttribute()] 39 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 40 | [global::System.Configuration.DefaultSettingValueAttribute("False")] 41 | public bool IPv6Support { 42 | get { 43 | return ((bool)(this["IPv6Support"])); 44 | } 45 | set { 46 | this["IPv6Support"] = value; 47 | } 48 | } 49 | 50 | [global::System.Configuration.UserScopedSettingAttribute()] 51 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 52 | [global::System.Configuration.DefaultSettingValueAttribute("100")] 53 | public int HistoryEntries { 54 | get { 55 | return ((int)(this["HistoryEntries"])); 56 | } 57 | set { 58 | this["HistoryEntries"] = value; 59 | } 60 | } 61 | 62 | [global::System.Configuration.UserScopedSettingAttribute()] 63 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 64 | [global::System.Configuration.DefaultSettingValueAttribute("True")] 65 | public bool HistoryInfoTimestamp { 66 | get { 67 | return ((bool)(this["HistoryInfoTimestamp"])); 68 | } 69 | set { 70 | this["HistoryInfoTimestamp"] = value; 71 | } 72 | } 73 | 74 | [global::System.Configuration.UserScopedSettingAttribute()] 75 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 76 | [global::System.Configuration.DefaultSettingValueAttribute("True")] 77 | public bool HistoryInfoIpAdress { 78 | get { 79 | return ((bool)(this["HistoryInfoIpAdress"])); 80 | } 81 | set { 82 | this["HistoryInfoIpAdress"] = value; 83 | } 84 | } 85 | 86 | [global::System.Configuration.UserScopedSettingAttribute()] 87 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 88 | [global::System.Configuration.DefaultSettingValueAttribute("True")] 89 | public bool ScrollToEnd { 90 | get { 91 | return ((bool)(this["ScrollToEnd"])); 92 | } 93 | set { 94 | this["ScrollToEnd"] = value; 95 | } 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /TcpUdpTool/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 65001 7 | 8 | 9 | False 10 | 11 | 12 | 100 13 | 14 | 15 | True 16 | 17 | 18 | True 19 | 20 | 21 | True 22 | 23 | 24 | -------------------------------------------------------------------------------- /TcpUdpTool/Resources/Icons.xaml: -------------------------------------------------------------------------------- 1 |  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 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /TcpUdpTool/Resources/Styles.xaml: -------------------------------------------------------------------------------- 1 |  4 | 5 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 54 | 55 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /TcpUdpTool/Resources/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielnilsson9/tcp-udp-tool/1a115ad0c43b9d908118f85b9748b075753cb52d/TcpUdpTool/Resources/icon.ico -------------------------------------------------------------------------------- /TcpUdpTool/TcpUdpTool.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {C3FF0B8C-0EAF-41EF-AEF6-C8C597CF0A7F} 8 | WinExe 9 | Properties 10 | TcpUdpTool 11 | TcpUdpTool 12 | v4.8 13 | 512 14 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 15 | 4 16 | true 17 | publish\ 18 | true 19 | Disk 20 | false 21 | Foreground 22 | 7 23 | Days 24 | false 25 | false 26 | true 27 | 0 28 | 1.0.0.%2a 29 | false 30 | false 31 | true 32 | 33 | 34 | 35 | AnyCPU 36 | true 37 | full 38 | false 39 | bin\Debug\ 40 | DEBUG;TRACE 41 | prompt 42 | 4 43 | 44 | 45 | AnyCPU 46 | pdbonly 47 | true 48 | bin\Release\ 49 | TRACE 50 | prompt 51 | 4 52 | 53 | 54 | Resources\icon.ico 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 4.0 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | MSBuild:Compile 75 | Designer 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | HelpView.xaml 115 | 116 | 117 | HistoryView.xaml 118 | 119 | 120 | SettingsView.xaml 121 | 122 | 123 | TcpClientView.xaml 124 | 125 | 126 | TcpServerView.xaml 127 | 128 | 129 | UdpSsmView.xaml 130 | 131 | 132 | UdpAsmView.xaml 133 | 134 | 135 | UdpView.xaml 136 | 137 | 138 | Designer 139 | MSBuild:Compile 140 | 141 | 142 | Designer 143 | MSBuild:Compile 144 | 145 | 146 | Designer 147 | MSBuild:Compile 148 | 149 | 150 | MSBuild:Compile 151 | Designer 152 | 153 | 154 | App.xaml 155 | Code 156 | 157 | 158 | MainWindow.xaml 159 | Code 160 | 161 | 162 | Designer 163 | MSBuild:Compile 164 | 165 | 166 | Designer 167 | MSBuild:Compile 168 | 169 | 170 | Designer 171 | MSBuild:Compile 172 | 173 | 174 | Designer 175 | MSBuild:Compile 176 | 177 | 178 | MSBuild:Compile 179 | Designer 180 | 181 | 182 | Designer 183 | MSBuild:Compile 184 | 185 | 186 | Designer 187 | MSBuild:Compile 188 | 189 | 190 | 191 | 192 | Code 193 | 194 | 195 | True 196 | True 197 | Resources.resx 198 | 199 | 200 | True 201 | Settings.settings 202 | True 203 | 204 | 205 | ResXFileCodeGenerator 206 | Resources.Designer.cs 207 | Designer 208 | 209 | 210 | SettingsSingleFileGenerator 211 | Settings.Designer.cs 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | False 221 | Microsoft .NET Framework 4.5.2 %28x86 and x64%29 222 | true 223 | 224 | 225 | False 226 | .NET Framework 3.5 SP1 227 | false 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 242 | -------------------------------------------------------------------------------- /TcpUdpTool/View/HelpView.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Controls; 2 | 3 | namespace TcpUdpTool.View 4 | { 5 | /// 6 | /// Interaction logic for HelpView.xaml 7 | /// 8 | public partial class HelpView : UserControl 9 | { 10 | public HelpView() 11 | { 12 | InitializeComponent(); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /TcpUdpTool/View/HistoryView.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Controls; 2 | 3 | namespace TcpUdpTool.View 4 | { 5 | /// 6 | /// Interaction logic for TcpClientView.xaml 7 | /// 8 | public partial class HistoryView : UserControl 9 | { 10 | public HistoryView() 11 | { 12 | InitializeComponent(); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /TcpUdpTool/View/MainWindow.xaml: -------------------------------------------------------------------------------- 1 |  14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 24 | 25 | 26 | 27 | 29 | 30 | 31 | 32 | 34 | 35 | 36 | 37 | 39 | 40 | 41 | 42 | 44 | 45 | 46 | 47 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /TcpUdpTool/View/MainWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | namespace TcpUdpTool.View 4 | { 5 | /// 6 | /// Interaction logic for MainWindow.xaml 7 | /// 8 | public partial class MainWindow : Window 9 | { 10 | 11 | public MainWindow() 12 | { 13 | InitializeComponent(); 14 | } 15 | 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /TcpUdpTool/View/SettingsView.xaml: -------------------------------------------------------------------------------- 1 |  10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 67 | 68 | 69 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 146 | 147 | 148 | 149 | 150 | 151 | -------------------------------------------------------------------------------- /TcpUdpTool/View/SettingsView.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | using System.Windows.Controls; 8 | using System.Windows.Data; 9 | using System.Windows.Documents; 10 | using System.Windows.Input; 11 | using System.Windows.Media; 12 | using System.Windows.Media.Imaging; 13 | using System.Windows.Navigation; 14 | using System.Windows.Shapes; 15 | 16 | namespace TcpUdpTool.View 17 | { 18 | /// 19 | /// Interaction logic for SettingsView.xaml 20 | /// 21 | public partial class SettingsView : UserControl 22 | { 23 | public SettingsView() 24 | { 25 | InitializeComponent(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /TcpUdpTool/View/TcpClientView.xaml: -------------------------------------------------------------------------------- 1 |  9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 85 | 86 | 87 | 91 | 92 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 116 | 117 | 118 | 126 | 127 | 128 | 129 | 139 | 140 | 160 | 161 | 103 | 104 | 105 | 106 | 107 | 108 | 112 | 113 | 114 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 138 | 139 | 147 | 148 | 149 | 150 | 159 | 160 | 180 | 181 | 154 | 155 | 156 | 157 | 162 | 163 | 164 | 165 | -------------------------------------------------------------------------------- /TcpUdpTool/View/UdpSsmView.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Controls; 2 | 3 | namespace TcpUdpTool.View 4 | { 5 | /// 6 | /// Interaction logic for UdpMulticastView.xaml 7 | /// 8 | public partial class UdpSsmView : UserControl 9 | { 10 | public UdpSsmView() 11 | { 12 | InitializeComponent(); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /TcpUdpTool/View/UdpView.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows; 7 | using System.Windows.Controls; 8 | using System.Windows.Data; 9 | using System.Windows.Documents; 10 | using System.Windows.Input; 11 | using System.Windows.Media; 12 | using System.Windows.Media.Imaging; 13 | using System.Windows.Navigation; 14 | using System.Windows.Shapes; 15 | 16 | namespace TcpUdpTool.View 17 | { 18 | /// 19 | /// Interaction logic for UdpView.xaml 20 | /// 21 | public partial class UdpView : UserControl 22 | { 23 | public UdpView() 24 | { 25 | InitializeComponent(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /TcpUdpTool/ViewModel/Base/BatchObservableCollection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.ObjectModel; 4 | using System.Collections.Specialized; 5 | using System.ComponentModel; 6 | 7 | namespace TcpUdpTool.ViewModel.Base 8 | { 9 | public class BatchObservableCollection : Collection, INotifyCollectionChanged, INotifyPropertyChanged 10 | { 11 | private int _batchOperationCount; 12 | private bool _changedDuringBatchOperation; 13 | 14 | public event NotifyCollectionChangedEventHandler CollectionChanged; 15 | public event PropertyChangedEventHandler PropertyChanged; 16 | 17 | 18 | public void BeginBatch() 19 | { 20 | _batchOperationCount++; 21 | } 22 | 23 | public void EndBatch() 24 | { 25 | if (_batchOperationCount == 0) 26 | { 27 | throw new InvalidOperationException("EndBatch() called without a matching call to BeginBatch()."); 28 | } 29 | 30 | _batchOperationCount--; 31 | 32 | if (_batchOperationCount == 0 && _changedDuringBatchOperation) 33 | { 34 | OnCollectionReset(); 35 | _changedDuringBatchOperation = false; 36 | } 37 | } 38 | 39 | public void AddRange(IEnumerable items) 40 | { 41 | if (items == null) 42 | { 43 | return; 44 | } 45 | 46 | BeginBatch(); 47 | try 48 | { 49 | var list = items as IList; 50 | if (list != null) 51 | { 52 | for (var i = 0; i < list.Count; i++) 53 | { 54 | Add(list[i]); 55 | } 56 | } 57 | else 58 | { 59 | { 60 | foreach (var item in items) 61 | { 62 | Add(item); 63 | } 64 | } 65 | } 66 | } 67 | finally 68 | { 69 | EndBatch(); 70 | } 71 | } 72 | 73 | protected override void ClearItems() 74 | { 75 | var hadItems = Count != 0; 76 | 77 | base.ClearItems(); 78 | 79 | if (hadItems) 80 | { 81 | if (_batchOperationCount == 0) 82 | { 83 | OnCollectionReset(); 84 | } 85 | else 86 | { 87 | _changedDuringBatchOperation = true; 88 | } 89 | } 90 | } 91 | 92 | protected override void InsertItem(int index, T item) 93 | { 94 | base.InsertItem(index, item); 95 | 96 | if (_batchOperationCount == 0) 97 | { 98 | OnCountChanged(); 99 | OnCollectionChanged(NotifyCollectionChangedAction.Add, item, index); 100 | } 101 | else 102 | { 103 | _changedDuringBatchOperation = true; 104 | } 105 | } 106 | 107 | protected override void RemoveItem(int index) 108 | { 109 | var item = this[index]; 110 | base.RemoveItem(index); 111 | 112 | if (_batchOperationCount == 0) 113 | { 114 | OnCountChanged(); 115 | OnCollectionChanged(NotifyCollectionChangedAction.Remove, item, index); 116 | } 117 | else 118 | { 119 | _changedDuringBatchOperation = true; 120 | } 121 | } 122 | 123 | protected override void SetItem(int index, T item) 124 | { 125 | var oldItem = this[index]; 126 | base.SetItem(index, item); 127 | 128 | if (_batchOperationCount == 0) 129 | { 130 | OnItemsChanged(); 131 | OnCollectionChanged(NotifyCollectionChangedAction.Replace, item, oldItem, index); 132 | } 133 | else 134 | { 135 | _changedDuringBatchOperation = true; 136 | } 137 | } 138 | 139 | protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e) 140 | { 141 | CollectionChanged?.Invoke(this, e); 142 | } 143 | 144 | protected virtual void OnPropertyChanged(string name) 145 | { 146 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); 147 | } 148 | 149 | private void OnCollectionChanged(NotifyCollectionChangedAction action, T item, int index) 150 | { 151 | OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, item, index)); 152 | } 153 | 154 | private void OnCollectionChanged(NotifyCollectionChangedAction action, T item, T oldItem, int index) 155 | { 156 | OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, item, oldItem, index)); 157 | } 158 | 159 | private void OnCollectionReset() 160 | { 161 | OnCountChanged(); 162 | OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); 163 | } 164 | 165 | private void OnCountChanged() 166 | { 167 | OnPropertyChanged("Count"); 168 | OnItemsChanged(); 169 | } 170 | 171 | private void OnItemsChanged() 172 | { 173 | OnPropertyChanged("Items[]"); 174 | } 175 | 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /TcpUdpTool/ViewModel/Base/DelegateCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Input; 3 | 4 | namespace TcpUdpTool.ViewModel.Base 5 | { 6 | public class DelegateCommand : ICommand 7 | { 8 | private readonly Action _action; 9 | private readonly Action _actionWithParam; 10 | 11 | public event EventHandler CanExecuteChanged; 12 | 13 | 14 | public DelegateCommand(Action action) 15 | { 16 | _action = action; 17 | _actionWithParam = null; 18 | } 19 | 20 | public DelegateCommand(Action action) 21 | { 22 | _actionWithParam = action; 23 | _action = null; 24 | } 25 | 26 | 27 | 28 | public bool CanExecute(object parameter) 29 | { 30 | return true; 31 | } 32 | 33 | public void Execute(object parameter) 34 | { 35 | _action?.Invoke(); 36 | _actionWithParam?.Invoke(parameter); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /TcpUdpTool/ViewModel/Base/ObservableObject.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.ComponentModel; 5 | 6 | namespace TcpUdpTool.ViewModel.Base 7 | { 8 | public class ObservableObject : INotifyPropertyChanged, INotifyDataErrorInfo 9 | { 10 | 11 | #region Private Members 12 | 13 | private Dictionary _errors = new Dictionary(); 14 | 15 | #endregion 16 | 17 | #region Public Properties 18 | 19 | public bool HasErrors 20 | { 21 | get 22 | { 23 | return _errors.Count > 0; 24 | } 25 | } 26 | 27 | #endregion 28 | 29 | #region Public events 30 | 31 | public event EventHandler ErrorsChanged; 32 | public event PropertyChangedEventHandler PropertyChanged; 33 | 34 | #endregion 35 | 36 | #region Public Functions 37 | 38 | public void AddError(string propertyName, string error) 39 | { 40 | if(!_errors.ContainsKey(propertyName) || _errors[propertyName] != error) 41 | { 42 | _errors[propertyName] = error; 43 | ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName)); 44 | } 45 | } 46 | 47 | public void RemoveError(string propertyName) 48 | { 49 | if(_errors.Remove(propertyName)) 50 | { 51 | ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName)); 52 | } 53 | } 54 | 55 | public string GetError(string propertyName) 56 | { 57 | if(_errors.ContainsKey(propertyName)) 58 | { 59 | return _errors[propertyName]; 60 | } 61 | 62 | return null; 63 | } 64 | 65 | public bool HasError(string propertyName) 66 | { 67 | return _errors.ContainsKey(propertyName); 68 | } 69 | 70 | public IEnumerable GetErrors(string propertyName) 71 | { 72 | if (propertyName == null) 73 | return null; 74 | 75 | List err = new List(); 76 | if (_errors.ContainsKey(propertyName)) 77 | { 78 | err.Add(_errors[propertyName]); 79 | } 80 | 81 | return err; 82 | } 83 | 84 | #endregion 85 | 86 | #region Protected Functions 87 | 88 | protected void OnPropertyChanged(string propertyName) 89 | { 90 | PropertyChanged?.Invoke(this, 91 | new PropertyChangedEventArgs(propertyName)); 92 | } 93 | 94 | #endregion 95 | 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /TcpUdpTool/ViewModel/Extension/ListViewExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.Specialized; 4 | using System.ComponentModel; 5 | using System.Windows; 6 | using System.Windows.Controls; 7 | using System.Windows.Controls.Primitives; 8 | 9 | namespace TcpUdpTool.ViewModel.Extension 10 | { 11 | public static class ListViewExtension 12 | { 13 | 14 | private static readonly Dictionary AutoScrollHandlers = 15 | new Dictionary(); 16 | 17 | 18 | public static readonly DependencyProperty AutoScrollToEndProperty = 19 | DependencyProperty.RegisterAttached("AutoScrollToEnd", typeof(bool), typeof(ListViewExtension), 20 | new FrameworkPropertyMetadata(false, OnAutoScrolToEndChanged)); 21 | 22 | public static bool GetAutoScrollToEnd(DependencyObject d) 23 | { 24 | return (bool) d.GetValue(AutoScrollToEndProperty); 25 | } 26 | 27 | public static void SetAutoScrollToEnd(DependencyObject d, bool value) 28 | { 29 | d.SetValue(AutoScrollToEndProperty, value); 30 | } 31 | 32 | 33 | public static void OnAutoScrolToEndChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 34 | { 35 | var listView = d as ListView; 36 | if (listView == null) 37 | { 38 | return; 39 | } 40 | 41 | var oldValue = (bool) e.OldValue; 42 | var newValue = (bool) e.NewValue; 43 | 44 | if (oldValue == newValue) 45 | { 46 | return; 47 | } 48 | 49 | if (newValue) 50 | { 51 | listView.Unloaded += OnListViewUnloaded; 52 | listView.Loaded += OnListViewLoaded; 53 | var desc = TypeDescriptor.GetProperties(listView)["ItemsSource"]; 54 | desc.AddValueChanged(listView, OnListViewItemsSourceChanged); 55 | } 56 | else 57 | { 58 | listView.Unloaded -= OnListViewUnloaded; 59 | listView.Loaded -= OnListViewLoaded; 60 | 61 | ListViewCapture capture; 62 | if (AutoScrollHandlers.TryGetValue(listView, out capture)) 63 | { 64 | capture.Dispose(); 65 | AutoScrollHandlers.Remove(listView); 66 | } 67 | 68 | var desc = TypeDescriptor.GetProperties(listView)["ItemsSource"]; 69 | desc.RemoveValueChanged(listView, OnListViewItemsSourceChanged); 70 | } 71 | } 72 | 73 | private static void OnListViewUnloaded(object sender, RoutedEventArgs e) 74 | { 75 | var listView = sender as ListView; 76 | if (listView != null && GetAutoScrollToEnd(listView)) 77 | { 78 | ListViewCapture capture; 79 | if (AutoScrollHandlers.TryGetValue(listView, out capture)) 80 | { 81 | capture.Dispose(); 82 | AutoScrollHandlers.Remove(listView); 83 | } 84 | } 85 | } 86 | 87 | private static void OnListViewLoaded(object sender, RoutedEventArgs e) 88 | { 89 | var listView = sender as ListView; 90 | if (listView != null && GetAutoScrollToEnd(listView)) 91 | { 92 | if (!AutoScrollHandlers.ContainsKey(listView)) 93 | { 94 | AutoScrollHandlers.Add(listView, new ListViewCapture(listView)); 95 | } 96 | } 97 | } 98 | 99 | 100 | private static void OnListViewItemsSourceChanged(object sender, EventArgs e) 101 | { 102 | var listView = sender as ListView; 103 | if (listView != null && GetAutoScrollToEnd(listView)) 104 | { 105 | // remove if already exist. 106 | ListViewCapture capture; 107 | if (AutoScrollHandlers.TryGetValue(listView, out capture)) 108 | { 109 | capture.Dispose(); 110 | AutoScrollHandlers.Remove(listView); 111 | } 112 | 113 | AutoScrollHandlers.Add(listView, new ListViewCapture(listView)); 114 | } 115 | } 116 | } 117 | 118 | class ListViewCapture : IDisposable 119 | { 120 | private readonly ListView _listView; 121 | private readonly INotifyCollectionChanged _notifyCollectionChanged; 122 | 123 | public ListViewCapture(ListView listView) 124 | { 125 | _listView = listView; 126 | _notifyCollectionChanged = _listView.ItemsSource as INotifyCollectionChanged; 127 | if (_notifyCollectionChanged != null) 128 | { 129 | _notifyCollectionChanged.CollectionChanged += OnCollectionChanged; 130 | } 131 | } 132 | 133 | void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) 134 | { 135 | if (e.Action == NotifyCollectionChangedAction.Add || e.Action == NotifyCollectionChangedAction.Reset) 136 | { 137 | _listView.ItemContainerGenerator.StatusChanged += ScrollToEnd; 138 | } 139 | } 140 | 141 | public void ScrollToEnd(object sender, EventArgs arg) 142 | { 143 | if (_listView.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated) 144 | { 145 | var item = _listView.Items[_listView.Items.Count - 1]; 146 | if (item == null) 147 | { 148 | return; 149 | } 150 | 151 | _listView.ScrollIntoView(item); 152 | _listView.ItemContainerGenerator.StatusChanged -= ScrollToEnd; 153 | } 154 | } 155 | 156 | public void Dispose() 157 | { 158 | if (_notifyCollectionChanged != null) 159 | _notifyCollectionChanged.CollectionChanged -= OnCollectionChanged; 160 | 161 | _listView.ItemContainerGenerator.StatusChanged -= ScrollToEnd; 162 | } 163 | } 164 | 165 | } 166 | -------------------------------------------------------------------------------- /TcpUdpTool/ViewModel/Extension/WindowExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Windows; 4 | using System.Windows.Input; 5 | 6 | namespace TcpUdpTool.ViewModel.Extension 7 | { 8 | public static class WindowExtension 9 | { 10 | 11 | private static Dictionary _closeEventHandlers = 12 | new Dictionary(); 13 | 14 | 15 | public static readonly DependencyProperty CloseCommandProperty = 16 | DependencyProperty.RegisterAttached("CloseCommand", typeof(ICommand), typeof(WindowExtension), 17 | new FrameworkPropertyMetadata(null, CloseCommandChanged)); 18 | 19 | public static ICommand GetCloseCommand(DependencyObject d) 20 | { 21 | return (ICommand)d.GetValue(CloseCommandProperty); 22 | } 23 | 24 | public static void SetCloseCommand(DependencyObject d, ICommand cmd) 25 | { 26 | d.SetValue(CloseCommandProperty, cmd); 27 | } 28 | 29 | 30 | private static void CloseCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs args) 31 | { 32 | var window = d as Window; 33 | if(window == null) 34 | { 35 | return; 36 | } 37 | 38 | window.Unloaded += OnWindowUnloaded; 39 | 40 | var newCmd = args.NewValue as ICommand; 41 | var oldCmd = args.OldValue as ICommand; 42 | 43 | if(oldCmd != null) 44 | { 45 | UnsubscribeCloseEvent(window); 46 | } 47 | 48 | if (newCmd != null) 49 | { 50 | SubscribeCloseEvent(window, newCmd); 51 | } 52 | } 53 | 54 | private static void OnWindowUnloaded(object sender, RoutedEventArgs args) 55 | { 56 | // prevent window from leaking. 57 | var window = sender as Window; 58 | if (window != null) 59 | { 60 | UnsubscribeCloseEvent(window); 61 | } 62 | } 63 | 64 | 65 | private static void SubscribeCloseEvent(Window window, ICommand cmd) 66 | { 67 | var handler = new EventHandler((s, e) => cmd.Execute(null)); 68 | _closeEventHandlers.Add(window, handler); 69 | window.Closed += handler; 70 | } 71 | 72 | private static void UnsubscribeCloseEvent(Window window) 73 | { 74 | EventHandler handler; 75 | if (_closeEventHandlers.TryGetValue(window, out handler)) 76 | { 77 | window.Closed -= handler; 78 | _closeEventHandlers.Remove(window); 79 | } 80 | } 81 | 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /TcpUdpTool/ViewModel/Helper/DispatchHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows; 3 | using System.Windows.Threading; 4 | 5 | namespace TcpUdpTool.ViewModel.Helper 6 | { 7 | public static class DispatchHelper 8 | { 9 | public static void Invoke(Action action) 10 | { 11 | var dispatchObject = Application.Current.Dispatcher; 12 | if (dispatchObject == null || dispatchObject.CheckAccess()) 13 | { 14 | action(); 15 | } 16 | else 17 | { 18 | dispatchObject.Invoke(action); 19 | } 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /TcpUdpTool/ViewModel/Helper/IContentChangedHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace TcpUdpTool.ViewModel.Helper 4 | { 5 | public interface IContentChangedHelper 6 | { 7 | 8 | event Action ContentChanged; 9 | 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /TcpUdpTool/ViewModel/Item/ConversationItemViewModel.cs: -------------------------------------------------------------------------------- 1 | using TcpUdpTool.Model.Data; 2 | using TcpUdpTool.Model.Formatter; 3 | using TcpUdpTool.ViewModel.Base; 4 | 5 | namespace TcpUdpTool.ViewModel.Item 6 | { 7 | public class ConversationItemViewModel : ObservableObject 8 | { 9 | private Transmission _message; 10 | private IFormatter _formatter; 11 | private string _contentCache; 12 | 13 | public string Timestamp 14 | { 15 | get { return "[" + _message.Timestamp.ToLongTimeString() + "]"; } 16 | } 17 | 18 | public bool TimestampVisible 19 | { 20 | get { return Properties.Settings.Default.HistoryInfoTimestamp; } 21 | } 22 | 23 | public string Source 24 | { 25 | get { return "[" + (IsReceived ? _message.Origin?.ToString() : _message.Destination?.ToString()) + "]"; } 26 | } 27 | 28 | public bool SourceVisible 29 | { 30 | get { return Properties.Settings.Default.HistoryInfoIpAdress; } 31 | } 32 | 33 | public bool IsReceived 34 | { 35 | get { return _message.IsReceived; } 36 | } 37 | 38 | public string Content 39 | { 40 | get { return _contentCache; } 41 | } 42 | 43 | public bool IsSelected { get; set; } 44 | 45 | public ConversationItemViewModel(Transmission message, IFormatter formatter) 46 | { 47 | _message = message; 48 | _formatter = formatter; 49 | _contentCache = _formatter.Format(message); 50 | } 51 | 52 | public void SetFormatter(IFormatter formatter) 53 | { 54 | _formatter = formatter; 55 | _contentCache = _formatter.Format(_message); 56 | OnPropertyChanged(nameof(Content)); 57 | OnPropertyChanged(nameof(TimestampVisible)); 58 | OnPropertyChanged(nameof(SourceVisible)); 59 | } 60 | 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /TcpUdpTool/ViewModel/Item/InterfaceAddress.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Net.Sockets; 4 | using TcpUdpTool.Model.Util; 5 | using TcpUdpTool.ViewModel.Base; 6 | 7 | namespace TcpUdpTool.ViewModel.Item 8 | { 9 | public class InterfaceAddress : ObservableObject, IComparable 10 | { 11 | public enum EInterfaceType { Default, All, Any, Specific } 12 | 13 | 14 | private EInterfaceType _type; 15 | public EInterfaceType Type 16 | { 17 | get { return _type; } 18 | private set { _type = value; } 19 | } 20 | 21 | private IPAddress _address; 22 | public IPAddress Address 23 | { 24 | get { return _address; } 25 | private set { _address = value; } 26 | } 27 | 28 | public string Name 29 | { 30 | get { return ToString(); } 31 | } 32 | 33 | public NetworkInterface Nic { get; private set; } 34 | 35 | public string GroupName 36 | { 37 | get { return Nic == null ? "Network Interface" : Nic.Name; } 38 | } 39 | 40 | 41 | public InterfaceAddress(EInterfaceType type, NetworkInterface nic, IPAddress address = null) 42 | { 43 | Type = type; 44 | Address = address; 45 | Nic = nic; 46 | 47 | if(Address == null && (Type == EInterfaceType.Any || Type == EInterfaceType.Specific)) 48 | { 49 | throw new ArgumentNullException( 50 | "address cannot be null for types: [Specific, Any]"); 51 | } 52 | } 53 | 54 | public override string ToString() 55 | { 56 | if (Type == EInterfaceType.Default) 57 | { 58 | return "Default"; 59 | } 60 | else if (Type == EInterfaceType.All) 61 | { 62 | return "All"; 63 | } 64 | else if (Type == EInterfaceType.Any) 65 | { 66 | if (Address.AddressFamily == AddressFamily.InterNetwork) 67 | { 68 | return "Any IPv4 (0.0.0.0)"; 69 | } 70 | else 71 | { 72 | return "Any IPv6 (::)"; 73 | } 74 | } 75 | else 76 | { 77 | return Address.ToString(); 78 | } 79 | } 80 | 81 | public int CompareTo(object other) 82 | { 83 | InterfaceAddress o = other as InterfaceAddress; 84 | 85 | if (o == null) 86 | { 87 | return 0; 88 | } 89 | 90 | int r = this.Type.CompareTo(o.Type); 91 | 92 | if(r == 0) 93 | { 94 | return this.ToString().CompareTo(o.ToString()); 95 | } 96 | else 97 | { 98 | return r; 99 | } 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /TcpUdpTool/ViewModel/MainViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Input; 2 | using TcpUdpTool.Properties; 3 | using TcpUdpTool.ViewModel.Base; 4 | 5 | namespace TcpUdpTool.ViewModel 6 | { 7 | public class MainViewModel : ObservableObject 8 | { 9 | 10 | public TcpClientViewModel TcpClientViewModel { get; private set; } 11 | 12 | public TcpServerViewModel TcpServerViewModel { get; private set; } 13 | 14 | public UdpViewModel UdpViewModel { get; private set; } 15 | 16 | public UdpAsmViewModel UdpAsmViewModel { get; private set; } 17 | 18 | public UdpSsmViewModel UdpSsmViewModel { get; private set; } 19 | 20 | public SettingsViewModel SettingsViewModel { get; private set; } 21 | 22 | 23 | public ICommand CloseCommand 24 | { 25 | get { return new DelegateCommand(OnWindowClose); } 26 | } 27 | 28 | 29 | public MainViewModel() 30 | { 31 | TcpClientViewModel = new TcpClientViewModel(); 32 | TcpServerViewModel = new TcpServerViewModel(); 33 | UdpViewModel = new UdpViewModel(); 34 | UdpAsmViewModel = new UdpAsmViewModel(); 35 | UdpSsmViewModel = new UdpSsmViewModel(); 36 | SettingsViewModel = new SettingsViewModel(); 37 | } 38 | 39 | 40 | private void OnWindowClose() 41 | { 42 | Settings.Default.Save(); 43 | TcpClientViewModel?.Dispose(); 44 | TcpServerViewModel?.Dispose(); 45 | UdpViewModel?.Dispose(); 46 | UdpAsmViewModel?.Dispose(); 47 | } 48 | 49 | } 50 | } -------------------------------------------------------------------------------- /TcpUdpTool/ViewModel/SendViewModel.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Win32; 2 | using System; 3 | using System.Collections.ObjectModel; 4 | using System.IO; 5 | using System.Net; 6 | using System.Windows.Input; 7 | using TcpUdpTool.Model.Parser; 8 | using TcpUdpTool.Model.Util; 9 | using TcpUdpTool.ViewModel.Item; 10 | using TcpUdpTool.ViewModel.Base; 11 | 12 | namespace TcpUdpTool.ViewModel 13 | { 14 | public class SendViewModel : ObservableObject 15 | { 16 | 17 | #region private members 18 | 19 | private IParser _parser; 20 | 21 | #endregion 22 | 23 | #region public properties 24 | 25 | private string _ipAddress; 26 | public string IpAddress 27 | { 28 | get { return _ipAddress; } 29 | set 30 | { 31 | if (_ipAddress != value) 32 | { 33 | _ipAddress = value; 34 | 35 | if (String.IsNullOrWhiteSpace(_ipAddress)) 36 | { 37 | AddError(nameof(IpAddress), "IP address cannot be empty."); 38 | } 39 | else 40 | { 41 | RemoveError(nameof(IpAddress)); 42 | } 43 | 44 | OnPropertyChanged(nameof(IpAddress)); 45 | } 46 | } 47 | } 48 | 49 | private int? _port; 50 | public int? Port 51 | { 52 | get { return _port; } 53 | set 54 | { 55 | if (_port != value) 56 | { 57 | _port = value; 58 | 59 | if (!NetworkUtils.IsValidPort(_port.HasValue ? _port.Value : -1, false)) 60 | { 61 | AddError(nameof(Port), "Port must be between 1 and 65535."); 62 | } 63 | else 64 | { 65 | RemoveError(nameof(Port)); 66 | } 67 | 68 | OnPropertyChanged(nameof(Port)); 69 | } 70 | } 71 | } 72 | 73 | private string _multicastGroup; 74 | public string MulticastGroup 75 | { 76 | get { return _multicastGroup; } 77 | set 78 | { 79 | if (_multicastGroup != value) 80 | { 81 | _multicastGroup = value; 82 | 83 | try 84 | { 85 | var addr = IPAddress.Parse(_multicastGroup); 86 | 87 | if (!NetworkUtils.IsMulticast(addr)) 88 | { 89 | throw new Exception(); 90 | } 91 | else 92 | { 93 | RemoveError(nameof(MulticastGroup)); 94 | } 95 | } 96 | catch (Exception) 97 | { 98 | if (String.IsNullOrWhiteSpace(_multicastGroup)) 99 | { 100 | AddError(nameof(MulticastGroup), "Multicast address cannot be empty."); 101 | } 102 | else 103 | { 104 | AddError(nameof(MulticastGroup), 105 | String.Format("\"{0}\" is not a valid multicast address.", _multicastGroup)); 106 | } 107 | } 108 | 109 | OnPropertyChanged(nameof(MulticastGroup)); 110 | } 111 | } 112 | } 113 | 114 | private int _multicastTtl; 115 | public int MulticastTtl 116 | { 117 | get { return _multicastTtl; } 118 | set 119 | { 120 | if (_multicastTtl != value) 121 | { 122 | _multicastTtl = value; 123 | 124 | if (_multicastTtl < 1 || _multicastTtl > 255) 125 | { 126 | AddError(nameof(MulticastTtl), "TTL must be between 1 and 255."); 127 | } 128 | else 129 | { 130 | RemoveError(nameof(MulticastTtl)); 131 | } 132 | 133 | OnPropertyChanged(nameof(MulticastTtl)); 134 | } 135 | } 136 | } 137 | 138 | private ObservableCollection _interfaces; 139 | public ObservableCollection Interfaces 140 | { 141 | get { return _interfaces; } 142 | set 143 | { 144 | if (_interfaces != value) 145 | { 146 | _interfaces = value; 147 | OnPropertyChanged(nameof(Interfaces)); 148 | } 149 | } 150 | } 151 | 152 | private InterfaceAddress _selectedInterface; 153 | public InterfaceAddress SelectedInterface 154 | { 155 | get { return _selectedInterface; } 156 | set 157 | { 158 | if (_selectedInterface != value) 159 | { 160 | _selectedInterface = value; 161 | OnPropertyChanged(nameof(SelectedInterface)); 162 | } 163 | } 164 | } 165 | 166 | 167 | private string _message; 168 | public string Message 169 | { 170 | get { return _message; } 171 | set 172 | { 173 | if(_message != value) 174 | { 175 | _message = value; 176 | OnPropertyChanged(nameof(Message)); 177 | } 178 | } 179 | } 180 | 181 | private bool _plainTextSelected; 182 | public bool PlainTextSelected 183 | { 184 | get { return _plainTextSelected; } 185 | set 186 | { 187 | if (_plainTextSelected != value) 188 | { 189 | if (FileSelected) 190 | Message = ""; 191 | 192 | _plainTextSelected = value; 193 | OnPropertyChanged(nameof(PlainTextSelected)); 194 | } 195 | } 196 | } 197 | 198 | private bool _hexSelected; 199 | public bool HexSelected 200 | { 201 | get { return _hexSelected; } 202 | set 203 | { 204 | if (_hexSelected != value) 205 | { 206 | if (FileSelected) 207 | Message = ""; 208 | 209 | _hexSelected = value; 210 | OnPropertyChanged(nameof(HexSelected)); 211 | } 212 | } 213 | } 214 | 215 | private bool _fileSelected; 216 | public bool FileSelected 217 | { 218 | get { return _fileSelected; } 219 | set 220 | { 221 | if(_fileSelected != value) 222 | { 223 | _fileSelected = value; 224 | OnPropertyChanged(nameof(FileSelected)); 225 | } 226 | } 227 | } 228 | 229 | #endregion 230 | 231 | #region public events 232 | 233 | public event Action SendData; 234 | 235 | #endregion 236 | 237 | #region public commands 238 | 239 | public ICommand TypeChangedCommand 240 | { 241 | get { return new DelegateCommand(TypeChanged); } 242 | } 243 | 244 | public ICommand BrowseCommand 245 | { 246 | get { return new DelegateCommand(Browse); } 247 | } 248 | 249 | public ICommand SendCommand 250 | { 251 | get { return new DelegateCommand(Send); } 252 | } 253 | 254 | #endregion 255 | 256 | #region constructors 257 | 258 | public SendViewModel() 259 | { 260 | PlainTextSelected = true; 261 | Message = ""; 262 | _parser = new PlainTextParser(); 263 | } 264 | 265 | #endregion 266 | 267 | #region private functions 268 | 269 | private void TypeChanged() 270 | { 271 | if (PlainTextSelected) 272 | { 273 | _parser = new PlainTextParser(); 274 | } 275 | else if(HexSelected) 276 | { 277 | _parser = new HexParser(); 278 | } 279 | } 280 | 281 | private void Browse() 282 | { 283 | var dialog = new OpenFileDialog(); 284 | 285 | if(dialog.ShowDialog().GetValueOrDefault()) 286 | { 287 | Message = dialog.FileName; 288 | } 289 | } 290 | 291 | private void Send() 292 | { 293 | if(FileSelected) 294 | { 295 | var filePath = Message; 296 | 297 | if(!File.Exists(filePath)) 298 | { 299 | DialogUtils.ShowErrorDialog("The file does not exist."); 300 | return; 301 | } 302 | 303 | if(new FileInfo(filePath).Length > 4096) 304 | { 305 | DialogUtils.ShowErrorDialog("The file is to large to send, maximum size is 16 KB."); 306 | return; 307 | } 308 | 309 | try 310 | { 311 | byte[] file = File.ReadAllBytes(filePath); 312 | SendData?.Invoke(file); 313 | } 314 | catch(Exception ex) 315 | { 316 | DialogUtils.ShowErrorDialog("Error while reading file. " + ex.Message); 317 | return; 318 | } 319 | } 320 | else 321 | { 322 | byte[] data = new byte[0]; 323 | try 324 | { 325 | data = _parser.Parse(Message, SettingsUtils.GetEncoding()); 326 | SendData?.Invoke(data); 327 | } 328 | catch (FormatException ex) 329 | { 330 | DialogUtils.ShowErrorDialog(ex.Message); 331 | return; 332 | } 333 | } 334 | } 335 | 336 | #endregion 337 | 338 | } 339 | } 340 | -------------------------------------------------------------------------------- /TcpUdpTool/ViewModel/SettingsViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.ObjectModel; 2 | using System.Text; 3 | using TcpUdpTool.ViewModel.Base; 4 | using System.Linq; 5 | 6 | namespace TcpUdpTool.ViewModel 7 | { 8 | public class SettingsViewModel : ObservableObject 9 | { 10 | 11 | private ObservableCollection _encodings; 12 | public ObservableCollection Encodings 13 | { 14 | get { return _encodings; } 15 | set 16 | { 17 | _encodings = value; 18 | OnPropertyChanged(nameof(Encodings)); 19 | } 20 | } 21 | 22 | private EncodingItem _selectedEncoding; 23 | public EncodingItem SelectedEncoding 24 | { 25 | get { return _selectedEncoding; } 26 | set 27 | { 28 | if(_selectedEncoding != value) 29 | { 30 | _selectedEncoding = value; 31 | Properties.Settings.Default.Encoding = _selectedEncoding.GetCodePage(); 32 | OnPropertyChanged(nameof(SelectedEncoding)); 33 | } 34 | } 35 | } 36 | 37 | private bool _ipv6Support; 38 | public bool IPv6SupportEnabled 39 | { 40 | get { return _ipv6Support; } 41 | set 42 | { 43 | if(_ipv6Support != value) 44 | { 45 | _ipv6Support = value; 46 | Properties.Settings.Default.IPv6Support = _ipv6Support; 47 | OnPropertyChanged(nameof(IPv6SupportEnabled)); 48 | OnPropertyChanged(nameof(IPv6SupportDisabled)); 49 | } 50 | } 51 | } 52 | public bool IPv6SupportDisabled 53 | { 54 | get { return !IPv6SupportEnabled; } 55 | } 56 | 57 | private bool _scrollToEnd; 58 | public bool ScrollToEndEnabled 59 | { 60 | get { return _scrollToEnd; } 61 | set 62 | { 63 | if (_scrollToEnd != value) 64 | { 65 | _scrollToEnd = value; 66 | Properties.Settings.Default.ScrollToEnd = _scrollToEnd; 67 | OnPropertyChanged(nameof(ScrollToEndEnabled)); 68 | OnPropertyChanged(nameof(ScrollToEndDisabled)); 69 | } 70 | } 71 | } 72 | public bool ScrollToEndDisabled 73 | { 74 | get { return !ScrollToEndEnabled; } 75 | } 76 | 77 | private int _historyEntries; 78 | public int HistoryEntries 79 | { 80 | get { return _historyEntries; } 81 | set 82 | { 83 | if(_historyEntries != value) 84 | { 85 | _historyEntries = value; 86 | if(_historyEntries < 1) 87 | { 88 | _historyEntries = 1; 89 | } 90 | else if(_historyEntries > 1000) 91 | { 92 | _historyEntries = 1000; 93 | } 94 | 95 | Properties.Settings.Default.HistoryEntries = _historyEntries; 96 | OnPropertyChanged(nameof(HistoryEntries)); 97 | } 98 | } 99 | } 100 | 101 | private bool _historyInfoTimestamp; 102 | public bool HistoryInfoTimestamp 103 | { 104 | get { return _historyInfoTimestamp; } 105 | 106 | set 107 | { 108 | if(_historyInfoTimestamp != value) 109 | { 110 | _historyInfoTimestamp = value; 111 | Properties.Settings.Default.HistoryInfoTimestamp = _historyInfoTimestamp; 112 | OnPropertyChanged(nameof(HistoryInfoTimestamp)); 113 | } 114 | } 115 | } 116 | 117 | private bool _historyInfoIpAddress; 118 | public bool HistoryInfoIpAddress 119 | { 120 | get { return _historyInfoIpAddress; } 121 | 122 | set 123 | { 124 | if(_historyInfoIpAddress != value) 125 | { 126 | _historyInfoIpAddress = value; 127 | Properties.Settings.Default.HistoryInfoIpAdress = _historyInfoIpAddress; 128 | OnPropertyChanged(nameof(HistoryInfoIpAddress)); 129 | } 130 | } 131 | } 132 | 133 | public SettingsViewModel() 134 | { 135 | Encodings = new ObservableCollection(); 136 | 137 | Encodings.Add(new EncodingItem(Encoding.Default, true)); 138 | 139 | Encoding.GetEncodings().OrderBy(o => o.Name).ToList() 140 | .ForEach(o => Encodings.Add(new EncodingItem(o.GetEncoding(), false))); 141 | 142 | IPv6SupportEnabled = Properties.Settings.Default.IPv6Support; 143 | ScrollToEndEnabled = Properties.Settings.Default.ScrollToEnd; 144 | HistoryEntries = Properties.Settings.Default.HistoryEntries; 145 | HistoryInfoTimestamp = Properties.Settings.Default.HistoryInfoTimestamp; 146 | HistoryInfoIpAddress = Properties.Settings.Default.HistoryInfoIpAdress; 147 | 148 | int selected = Properties.Settings.Default.Encoding; 149 | foreach(var e in Encodings) 150 | { 151 | if(e.GetCodePage() == selected) 152 | { 153 | SelectedEncoding = e; 154 | } 155 | } 156 | } 157 | 158 | } 159 | 160 | 161 | public class EncodingItem 162 | { 163 | private bool _default; 164 | private Encoding _encoding; 165 | 166 | public string Name 167 | { 168 | get { return ToString(); } 169 | } 170 | 171 | 172 | public EncodingItem(Encoding encoding, bool isDefault) 173 | { 174 | _encoding = encoding; 175 | _default = isDefault; 176 | } 177 | 178 | public int GetCodePage() 179 | { 180 | if (_default) return 0; 181 | return _encoding.CodePage; 182 | } 183 | 184 | public override string ToString() 185 | { 186 | if (_default) return "Default"; 187 | return _encoding.WebName; 188 | } 189 | 190 | } 191 | 192 | } 193 | -------------------------------------------------------------------------------- /TcpUdpTool/ViewModel/TcpClientViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Input; 3 | using TcpUdpTool.Model; 4 | using TcpUdpTool.Model.Data; 5 | using TcpUdpTool.Model.Util; 6 | using TcpUdpTool.ViewModel.Base; 7 | 8 | namespace TcpUdpTool.ViewModel 9 | { 10 | public class TcpClientViewModel : ObservableObject, IDisposable 11 | { 12 | 13 | #region private Members 14 | 15 | private TcpClient _tcpClient; 16 | 17 | #endregion 18 | 19 | #region public Properties 20 | 21 | private HistoryViewModel _historyViewModel = new HistoryViewModel(); 22 | public HistoryViewModel History 23 | { 24 | get { return _historyViewModel; } 25 | } 26 | 27 | private SendViewModel _sendViewModel = new SendViewModel(); 28 | public SendViewModel Send 29 | { 30 | get { return _sendViewModel; } 31 | } 32 | 33 | private bool _isConnected; 34 | public bool IsConnected 35 | { 36 | get { return _isConnected; } 37 | set 38 | { 39 | _isConnected = value; 40 | OnPropertyChanged(nameof(IsConnected)); 41 | } 42 | } 43 | 44 | private bool _isConnecting; 45 | public bool IsConnecting 46 | { 47 | get { return _isConnecting; } 48 | set 49 | { 50 | _isConnecting = value; 51 | OnPropertyChanged(nameof(IsConnecting)); 52 | } 53 | } 54 | 55 | private string _ipAddress; 56 | public string IpAddress 57 | { 58 | get { return _ipAddress; } 59 | set 60 | { 61 | if(_ipAddress != value) 62 | { 63 | _ipAddress = value; 64 | 65 | if(String.IsNullOrWhiteSpace(_ipAddress)) 66 | { 67 | AddError(nameof(IpAddress), "IP address cannot be empty."); 68 | } 69 | else 70 | { 71 | RemoveError(nameof(IpAddress)); 72 | } 73 | 74 | OnPropertyChanged(nameof(IpAddress)); 75 | } 76 | } 77 | } 78 | 79 | private int? _port; 80 | public int? Port 81 | { 82 | get { return _port; } 83 | set 84 | { 85 | if(_port != value) 86 | { 87 | _port = value; 88 | 89 | if(!NetworkUtils.IsValidPort(_port.HasValue ? _port.Value : -1, false)) 90 | { 91 | AddError(nameof(Port), "Port must be between 1 and 65535."); 92 | } 93 | else 94 | { 95 | RemoveError(nameof(Port)); 96 | } 97 | } 98 | 99 | OnPropertyChanged(nameof(Port)); 100 | } 101 | } 102 | 103 | #endregion 104 | 105 | #region public Commands 106 | 107 | public ICommand ConnectDisconnectCommand 108 | { 109 | get 110 | { 111 | return new DelegateCommand(() => 112 | { 113 | if (IsConnected) 114 | { 115 | Disconnect(); 116 | } 117 | else 118 | { 119 | Connect(); 120 | } 121 | } 122 | ); 123 | } 124 | } 125 | 126 | #endregion 127 | 128 | #region constructors 129 | 130 | public TcpClientViewModel() 131 | { 132 | _tcpClient = new TcpClient(); 133 | 134 | _sendViewModel.SendData += OnSend; 135 | _tcpClient.StatusChanged += 136 | (sender, arg) => 137 | { 138 | IsConnected = arg.Status == TcpClientStatusEventArgs.EConnectStatus.Connected; 139 | IsConnecting = arg.Status == TcpClientStatusEventArgs.EConnectStatus.Connecting; 140 | 141 | if(IsConnected) 142 | { 143 | History.Header = "Connected to: < " + arg.RemoteEndPoint.ToString() + " >"; 144 | } 145 | else 146 | { 147 | History.Header = "Conversation"; 148 | } 149 | 150 | }; 151 | 152 | _tcpClient.Received += 153 | (sender, arg) => 154 | { 155 | History.Append(arg.Message); 156 | }; 157 | 158 | 159 | IpAddress = "localhost"; 160 | Port = 0; 161 | History.Header = "Conversation"; 162 | } 163 | 164 | #endregion 165 | 166 | #region private Functions 167 | 168 | private async void Connect() 169 | { 170 | if (!ValidateConnect()) 171 | return; 172 | 173 | try 174 | { 175 | await _tcpClient.ConnectAsync(IpAddress, Port.Value); 176 | } 177 | catch(Exception ex) 178 | { 179 | DialogUtils.ShowErrorDialog(ex.Message); 180 | } 181 | } 182 | 183 | private void Disconnect() 184 | { 185 | _tcpClient.Disconnect(); 186 | } 187 | 188 | private async void OnSend(byte[] data) 189 | { 190 | try 191 | { 192 | Transmission msg = new Transmission(data, Transmission.EType.Sent); 193 | History.Append(msg); 194 | TransmissionResult res = await _tcpClient.SendAsync(msg); 195 | if (res != null) 196 | { 197 | msg.Origin = res.From; 198 | msg.Destination = res.To; 199 | Send.Message = ""; 200 | } 201 | } 202 | catch(Exception ex) 203 | { 204 | DialogUtils.ShowErrorDialog(ex.Message); 205 | } 206 | } 207 | 208 | private bool ValidateConnect() 209 | { 210 | string error = null; 211 | if (HasError(nameof(IpAddress))) 212 | error = GetError(nameof(IpAddress)); 213 | else if (HasError(nameof(Port))) 214 | error = GetError(nameof(Port)); 215 | 216 | if(error != null) 217 | { 218 | DialogUtils.ShowErrorDialog(error); 219 | return false; 220 | } 221 | 222 | return true; 223 | } 224 | 225 | public void Dispose() 226 | { 227 | _tcpClient?.Dispose(); 228 | _historyViewModel?.Dispose(); 229 | } 230 | 231 | #endregion 232 | 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /TcpUdpTool/ViewModel/TcpServerViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.ObjectModel; 3 | using System.Linq; 4 | using System.Net; 5 | using System.Windows.Input; 6 | using TcpUdpTool.Model; 7 | using TcpUdpTool.Model.Data; 8 | using TcpUdpTool.Model.Util; 9 | using TcpUdpTool.ViewModel.Item; 10 | using TcpUdpTool.ViewModel.Base; 11 | using System.Windows; 12 | using System.Collections.Generic; 13 | 14 | namespace TcpUdpTool.ViewModel 15 | { 16 | public class TcpServerViewModel : ObservableObject, IDisposable 17 | { 18 | 19 | #region private members 20 | 21 | private TcpServer _tcpServer; 22 | 23 | #endregion 24 | 25 | #region public propterties 26 | 27 | private ObservableCollection _localInterfaces; 28 | public ObservableCollection LocalInterfaces 29 | { 30 | get { return _localInterfaces; } 31 | set 32 | { 33 | if(_localInterfaces != value) 34 | { 35 | _localInterfaces = value; 36 | OnPropertyChanged(nameof(LocalInterfaces)); 37 | } 38 | } 39 | } 40 | 41 | private HistoryViewModel _historyViewModel = new HistoryViewModel(); 42 | public HistoryViewModel History 43 | { 44 | get { return _historyViewModel; } 45 | } 46 | 47 | private SendViewModel _sendViewModel = new SendViewModel(); 48 | public SendViewModel Send 49 | { 50 | get { return _sendViewModel; } 51 | } 52 | 53 | 54 | private bool _isStarted; 55 | public bool IsStarted 56 | { 57 | get { return _isStarted; } 58 | set 59 | { 60 | _isStarted = value; 61 | OnPropertyChanged(nameof(IsStarted)); 62 | } 63 | } 64 | 65 | private bool _isClientConnected; 66 | public bool IsClientConnected 67 | { 68 | get { return _isClientConnected; } 69 | set 70 | { 71 | _isClientConnected = value; 72 | OnPropertyChanged(nameof(IsClientConnected)); 73 | } 74 | } 75 | 76 | private InterfaceAddress _selectedInterface; 77 | public InterfaceAddress SelectedInterface 78 | { 79 | get { return _selectedInterface; } 80 | set 81 | { 82 | if(_selectedInterface != value) 83 | { 84 | _selectedInterface = value; 85 | OnPropertyChanged(nameof(SelectedInterface)); 86 | } 87 | } 88 | } 89 | 90 | private int? _port; 91 | public int? Port 92 | { 93 | get { return _port; } 94 | set 95 | { 96 | if(_port != value) 97 | { 98 | _port = value; 99 | 100 | if(!NetworkUtils.IsValidPort(_port.HasValue ? _port.Value : -1, true)) 101 | { 102 | AddError(nameof(Port), "Port must be between 0 and 65535."); 103 | } 104 | else 105 | { 106 | RemoveError(nameof(Port)); 107 | } 108 | 109 | OnPropertyChanged(nameof(Port)); 110 | } 111 | } 112 | } 113 | 114 | #endregion 115 | 116 | #region public commands 117 | 118 | public ICommand StartStopCommand 119 | { 120 | get 121 | { 122 | return new DelegateCommand(() => 123 | { 124 | if(IsStarted) 125 | { 126 | Stop(); 127 | } 128 | else 129 | { 130 | Start(); 131 | } 132 | }); 133 | } 134 | } 135 | 136 | public ICommand DisconnectCommand 137 | { 138 | get { return new DelegateCommand(Disconnect); } 139 | } 140 | 141 | #endregion 142 | 143 | #region constructors 144 | 145 | public TcpServerViewModel() 146 | { 147 | _tcpServer = new TcpServer(); 148 | LocalInterfaces = new ObservableCollection(); 149 | 150 | _sendViewModel.SendData += OnSend; 151 | _tcpServer.StatusChanged += 152 | (sender, arg) => 153 | { 154 | if(arg.Status == TcpServerStatusEventArgs.EServerStatus.Started) 155 | { 156 | IsStarted = true; 157 | History.Header = "Listening on: < " + arg.ServerInfo.ToString() + " >"; 158 | } 159 | else if(arg.Status == TcpServerStatusEventArgs.EServerStatus.Stopped) 160 | { 161 | History.Header = "Conversation"; 162 | IsStarted = false; 163 | IsClientConnected = false; 164 | } 165 | else if(arg.Status == TcpServerStatusEventArgs.EServerStatus.ClientConnected || 166 | arg.Status == TcpServerStatusEventArgs.EServerStatus.ClientDisconnected) 167 | { 168 | IsClientConnected = _tcpServer.NumConnectedClients > 0; 169 | 170 | if (IsClientConnected) 171 | { 172 | History.Header = "Connected client(s): < " + (_tcpServer.NumConnectedClients > 1 173 | ? _tcpServer.NumConnectedClients.ToString() : arg.ClientInfo.ToString()) + " >"; 174 | } 175 | else 176 | { 177 | History.Header = "Listening on: < " + arg.ServerInfo.ToString() + " >"; 178 | } 179 | } 180 | }; 181 | 182 | _tcpServer.Received += 183 | (sender, arg) => 184 | { 185 | History.Append(arg.Message); 186 | }; 187 | 188 | Port = 0; 189 | History.Header = "Conversation"; 190 | 191 | RebuildInterfaceList(); 192 | 193 | Properties.Settings.Default.PropertyChanged += (sender, e) => 194 | { 195 | if(e.PropertyName == nameof(Properties.Settings.Default.IPv6Support)) 196 | { 197 | RebuildInterfaceList(); 198 | } 199 | }; 200 | 201 | NetworkUtils.NetworkInterfaceChange += () => 202 | { 203 | Application.Current.Dispatcher.Invoke(RebuildInterfaceList); 204 | }; 205 | } 206 | 207 | #endregion 208 | 209 | #region private functions 210 | 211 | private void Start() 212 | { 213 | if (!ValidateStart()) 214 | return; 215 | 216 | try 217 | { 218 | _tcpServer.Start(SelectedInterface.Address, Port.Value); 219 | } 220 | catch(System.Net.Sockets.SocketException ex) 221 | { 222 | String message = ex.Message; 223 | if(ex.ErrorCode == 10013) 224 | { 225 | message = "Port " + Port + " is already in use, unable to start server."; 226 | } 227 | 228 | DialogUtils.ShowErrorDialog(message); 229 | } 230 | catch(Exception ex) 231 | { 232 | DialogUtils.ShowErrorDialog(ex.Message); 233 | } 234 | } 235 | 236 | private void Stop() 237 | { 238 | _tcpServer.Stop(); 239 | } 240 | 241 | private async void OnSend(byte[] data) 242 | { 243 | try 244 | { 245 | Transmission msg = new Transmission(data, Transmission.EType.Sent); 246 | List res = await _tcpServer.SendAsync(msg); 247 | if (res != null) 248 | { 249 | foreach (var sendResult in res) 250 | { 251 | Transmission entry = new Transmission(data, Transmission.EType.Sent); 252 | msg.Origin = sendResult.From; 253 | msg.Destination = sendResult.To; 254 | Send.Message = ""; 255 | History.Append(msg); 256 | } 257 | } 258 | } 259 | catch(Exception ex) 260 | { 261 | DialogUtils.ShowErrorDialog(ex.Message); 262 | } 263 | } 264 | 265 | private void Disconnect() 266 | { 267 | _tcpServer.Disconnect(); 268 | } 269 | 270 | private bool ValidateStart() 271 | { 272 | string error = null; 273 | if (HasError(nameof(Port))) 274 | error = GetError(nameof(Port)); 275 | 276 | 277 | if (error != null) 278 | { 279 | DialogUtils.ShowErrorDialog(error); 280 | return false; 281 | } 282 | 283 | return true; 284 | } 285 | 286 | 287 | private void RebuildInterfaceList() 288 | { 289 | LocalInterfaces.Clear(); 290 | // build interface list 291 | LocalInterfaces.Add(new InterfaceAddress(InterfaceAddress.EInterfaceType.Any, null, IPAddress.Any)); 292 | if (Properties.Settings.Default.IPv6Support) 293 | { 294 | LocalInterfaces.Add(new InterfaceAddress(InterfaceAddress.EInterfaceType.Any, null, IPAddress.IPv6Any)); 295 | } 296 | 297 | foreach (var nic in NetworkUtils.GetActiveInterfaces()) 298 | { 299 | foreach (var ip in nic.IPv4.Addresses) 300 | { 301 | LocalInterfaces.Add(new InterfaceAddress( 302 | InterfaceAddress.EInterfaceType.Specific, nic, ip)); 303 | } 304 | 305 | if (Properties.Settings.Default.IPv6Support) 306 | { 307 | foreach (var ip in nic.IPv6.Addresses) 308 | { 309 | LocalInterfaces.Add(new InterfaceAddress( 310 | InterfaceAddress.EInterfaceType.Specific, nic, ip)); 311 | } 312 | } 313 | } 314 | 315 | SelectedInterface = LocalInterfaces.FirstOrDefault(); 316 | } 317 | 318 | public void Dispose() 319 | { 320 | _tcpServer?.Dispose(); 321 | _historyViewModel?.Dispose(); 322 | } 323 | 324 | #endregion 325 | 326 | } 327 | } 328 | -------------------------------------------------------------------------------- /TcpUdpTool/ViewModel/UdpSsmViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.ObjectModel; 3 | using System.Linq; 4 | using System.Net; 5 | using System.Windows; 6 | using System.Windows.Input; 7 | using TcpUdpTool.Model; 8 | using TcpUdpTool.Model.Util; 9 | using TcpUdpTool.ViewModel.Base; 10 | using TcpUdpTool.ViewModel.Item; 11 | 12 | namespace TcpUdpTool.ViewModel 13 | { 14 | public class UdpSsmViewModel : ObservableObject, IDisposable 15 | { 16 | 17 | #region private members 18 | 19 | private UdpMulticastClient _udpClient; 20 | 21 | #endregion 22 | 23 | #region public properties 24 | 25 | private ObservableCollection _localInterfaces; 26 | public ObservableCollection LocalInterfaces 27 | { 28 | get { return _localInterfaces; } 29 | set 30 | { 31 | if (_localInterfaces != value) 32 | { 33 | _localInterfaces = value; 34 | OnPropertyChanged(nameof(LocalInterfaces)); 35 | } 36 | } 37 | } 38 | 39 | private HistoryViewModel _historyViewModel = new HistoryViewModel(); 40 | public HistoryViewModel History 41 | { 42 | get { return _historyViewModel; } 43 | } 44 | 45 | private bool _isGroupJoined; 46 | public bool IsGroupJoined 47 | { 48 | get { return _isGroupJoined; } 49 | set 50 | { 51 | _isGroupJoined = value; 52 | OnPropertyChanged(nameof(IsGroupJoined)); 53 | } 54 | } 55 | 56 | private string _multicastGroup; 57 | public string MulticastGroup 58 | { 59 | get { return _multicastGroup; } 60 | set 61 | { 62 | if (_multicastGroup != value) 63 | { 64 | _multicastGroup = value; 65 | 66 | try 67 | { 68 | var addr = IPAddress.Parse(_multicastGroup); 69 | if (!NetworkUtils.IsMulticast(addr)) 70 | { 71 | throw new Exception(); 72 | } 73 | else 74 | { 75 | RemoveError(nameof(MulticastGroup)); 76 | } 77 | } 78 | catch (Exception) 79 | { 80 | if (String.IsNullOrWhiteSpace(_multicastGroup)) 81 | { 82 | AddError(nameof(MulticastGroup), "Multicast address cannot be empty."); 83 | } 84 | else 85 | { 86 | AddError(nameof(MulticastGroup), 87 | String.Format("\"{0}\" is not a valid source specific multicast address.", _multicastGroup)); 88 | } 89 | } 90 | 91 | OnPropertyChanged(nameof(MulticastGroup)); 92 | } 93 | } 94 | } 95 | 96 | private string _multicastSource; 97 | public string MulticastSource 98 | { 99 | get { return _multicastSource; } 100 | set 101 | { 102 | if (_multicastSource != value) 103 | { 104 | _multicastSource = value; 105 | 106 | try 107 | { 108 | var addr = IPAddress.Parse(_multicastSource); 109 | 110 | RemoveError(nameof(MulticastSource)); 111 | } 112 | catch (Exception) 113 | { 114 | if (String.IsNullOrWhiteSpace(_multicastSource)) 115 | { 116 | AddError(nameof(MulticastSource), "Multicast address cannot be empty."); 117 | } 118 | else 119 | { 120 | AddError(nameof(MulticastSource), 121 | String.Format("\"{0}\" is not a valid ip address.", _multicastSource)); 122 | } 123 | } 124 | 125 | OnPropertyChanged(nameof(MulticastSource)); 126 | } 127 | } 128 | } 129 | 130 | private int? _multicastPort; 131 | public int? MulticastPort 132 | { 133 | get { return _multicastPort; } 134 | set 135 | { 136 | if (_multicastPort != value) 137 | { 138 | _multicastPort = value; 139 | 140 | if (!NetworkUtils.IsValidPort(_multicastPort.HasValue ? _multicastPort.Value : -1, false)) 141 | { 142 | AddError(nameof(MulticastPort), "Port must be between 1 and 65535."); 143 | } 144 | else 145 | { 146 | RemoveError(nameof(MulticastPort)); 147 | } 148 | 149 | OnPropertyChanged(nameof(MulticastPort)); 150 | } 151 | } 152 | } 153 | 154 | private InterfaceAddress _selectedListenInterface; 155 | public InterfaceAddress SelectedListenInterface 156 | { 157 | get { return _selectedListenInterface; } 158 | set 159 | { 160 | if (_selectedListenInterface != value) 161 | { 162 | _selectedListenInterface = value; 163 | OnPropertyChanged(nameof(SelectedListenInterface)); 164 | } 165 | } 166 | } 167 | 168 | #endregion 169 | 170 | #region public commands 171 | 172 | public ICommand JoinLeaveCommand 173 | { 174 | get 175 | { 176 | return new DelegateCommand(() => 177 | { 178 | if (IsGroupJoined) 179 | { 180 | Leave(); 181 | } 182 | else 183 | { 184 | Join(); 185 | } 186 | }); 187 | } 188 | } 189 | 190 | #endregion 191 | 192 | #region constructors 193 | 194 | public UdpSsmViewModel() 195 | { 196 | _udpClient = new UdpMulticastClient(); 197 | LocalInterfaces = new ObservableCollection(); 198 | 199 | _udpClient.Received += (sender, arg) => 200 | { 201 | History.Append(arg.Message); 202 | }; 203 | 204 | _udpClient.StatusChanged += 205 | (sender, arg) => 206 | { 207 | IsGroupJoined = arg.Joined; 208 | 209 | if (arg.Joined) 210 | { 211 | _historyViewModel.Header = "Joined: < " + arg.MulticastGroup + " >"; 212 | } 213 | else 214 | { 215 | _historyViewModel.Header = "Conversation"; 216 | } 217 | }; 218 | 219 | 220 | MulticastGroup = ""; 221 | MulticastPort = 0; 222 | _historyViewModel.Header = "Conversation"; 223 | 224 | RebuildInterfaceList(); 225 | 226 | Properties.Settings.Default.PropertyChanged += (sender, e) => 227 | { 228 | if (e.PropertyName == nameof(Properties.Settings.Default.IPv6Support)) 229 | { 230 | RebuildInterfaceList(); 231 | } 232 | }; 233 | 234 | NetworkUtils.NetworkInterfaceChange += () => 235 | { 236 | Application.Current.Dispatcher.Invoke(RebuildInterfaceList); 237 | }; 238 | } 239 | 240 | #endregion 241 | 242 | #region private functions 243 | 244 | private void Join() 245 | { 246 | if (!ValidateJoin()) 247 | return; 248 | 249 | try 250 | { 251 | _udpClient.JoinSSM(IPAddress.Parse(MulticastGroup), IPAddress.Parse(MulticastSource), MulticastPort.Value, 252 | ToEMulticastInterface(SelectedListenInterface.Type), 253 | SelectedListenInterface.Address); 254 | } 255 | catch (Exception ex) 256 | { 257 | DialogUtils.ShowErrorDialog(ex.Message); 258 | } 259 | } 260 | 261 | private void Leave() 262 | { 263 | _udpClient.Leave(); 264 | } 265 | 266 | private bool ValidateJoin() 267 | { 268 | string error = null; 269 | if (HasError(nameof(MulticastGroup))) 270 | error = GetError(nameof(MulticastGroup)); 271 | else if (HasError(nameof(MulticastSource))) 272 | error = GetError(nameof(MulticastSource)); 273 | else if (HasError(nameof(MulticastPort))) 274 | error = GetError(nameof(MulticastPort)); 275 | 276 | if (error != null) 277 | { 278 | DialogUtils.ShowErrorDialog(error); 279 | return false; 280 | } 281 | 282 | return true; 283 | } 284 | 285 | private UdpMulticastClient.EMulticastInterface ToEMulticastInterface(InterfaceAddress.EInterfaceType type) 286 | { 287 | UdpMulticastClient.EMulticastInterface res; 288 | switch (type) 289 | { 290 | case InterfaceAddress.EInterfaceType.Default: 291 | res = UdpMulticastClient.EMulticastInterface.Default; 292 | break; 293 | case InterfaceAddress.EInterfaceType.All: 294 | res = UdpMulticastClient.EMulticastInterface.All; 295 | break; 296 | default: 297 | res = UdpMulticastClient.EMulticastInterface.Specific; 298 | break; 299 | } 300 | 301 | return res; 302 | } 303 | 304 | private void RebuildInterfaceList() 305 | { 306 | LocalInterfaces.Clear(); 307 | // build interface list 308 | LocalInterfaces.Add(new InterfaceAddress(InterfaceAddress.EInterfaceType.Default, null)); 309 | LocalInterfaces.Add(new InterfaceAddress(InterfaceAddress.EInterfaceType.All, null)); 310 | foreach (var nic in NetworkUtils.GetActiveInterfaces()) 311 | { 312 | foreach (var ip in nic.IPv4.Addresses) 313 | { 314 | LocalInterfaces.Add(new InterfaceAddress( 315 | InterfaceAddress.EInterfaceType.Specific, nic, ip)); 316 | } 317 | 318 | if (Properties.Settings.Default.IPv6Support) 319 | { 320 | foreach (var ip in nic.IPv6.Addresses) 321 | { 322 | LocalInterfaces.Add(new InterfaceAddress( 323 | InterfaceAddress.EInterfaceType.Specific, nic, ip)); 324 | } 325 | } 326 | } 327 | 328 | SelectedListenInterface = LocalInterfaces.FirstOrDefault(); 329 | } 330 | 331 | public void Dispose() 332 | { 333 | _udpClient?.Dispose(); 334 | _historyViewModel?.Dispose(); 335 | } 336 | 337 | #endregion 338 | } 339 | } 340 | -------------------------------------------------------------------------------- /TcpUdpTool/ViewModel/UdpViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.ObjectModel; 3 | using System.Linq; 4 | using System.Net; 5 | using System.Windows.Input; 6 | using TcpUdpTool.Model; 7 | using TcpUdpTool.Model.Data; 8 | using TcpUdpTool.Model.Util; 9 | using TcpUdpTool.ViewModel.Item; 10 | using TcpUdpTool.ViewModel.Base; 11 | using static TcpUdpTool.Model.UdpClientServerStatusEventArgs; 12 | using System.Windows; 13 | 14 | namespace TcpUdpTool.ViewModel 15 | { 16 | public class UdpViewModel : ObservableObject, IDisposable 17 | { 18 | 19 | #region private members 20 | 21 | private UdpClientServer _udpClientServer; 22 | 23 | #endregion 24 | 25 | #region public properties 26 | 27 | private ObservableCollection _localInterfaces; 28 | public ObservableCollection LocalInterfaces 29 | { 30 | get { return _localInterfaces; } 31 | set 32 | { 33 | if (_localInterfaces != value) 34 | { 35 | _localInterfaces = value; 36 | OnPropertyChanged(nameof(LocalInterfaces)); 37 | } 38 | } 39 | } 40 | 41 | private HistoryViewModel _historyViewModel = new HistoryViewModel(); 42 | public HistoryViewModel History 43 | { 44 | get { return _historyViewModel; } 45 | } 46 | 47 | private SendViewModel _sendViewModel = new SendViewModel(); 48 | public SendViewModel Send 49 | { 50 | get { return _sendViewModel; } 51 | } 52 | 53 | private bool _isServerStarted; 54 | public bool IsServerStarted 55 | { 56 | get { return _isServerStarted; } 57 | set 58 | { 59 | if(_isServerStarted != value) 60 | { 61 | _isServerStarted = value; 62 | OnPropertyChanged(nameof(IsServerStarted)); 63 | } 64 | } 65 | } 66 | 67 | private InterfaceAddress _selectedInterface; 68 | public InterfaceAddress SelectedInterface 69 | { 70 | get { return _selectedInterface; } 71 | set 72 | { 73 | if (_selectedInterface != value) 74 | { 75 | _selectedInterface = value; 76 | OnPropertyChanged(nameof(SelectedInterface)); 77 | } 78 | } 79 | } 80 | 81 | private int? _listenPort; 82 | public int? ListenPort 83 | { 84 | get { return _listenPort; } 85 | set 86 | { 87 | if(_listenPort != value) 88 | { 89 | _listenPort = value; 90 | 91 | if(!NetworkUtils.IsValidPort(_listenPort.HasValue ? _listenPort.Value : -1, true)) 92 | { 93 | AddError(nameof(ListenPort), "Port must be between 0 and 65535."); 94 | } 95 | else 96 | { 97 | RemoveError(nameof(ListenPort)); 98 | } 99 | 100 | OnPropertyChanged(nameof(ListenPort)); 101 | } 102 | } 103 | } 104 | 105 | #endregion 106 | 107 | #region public commands 108 | 109 | public ICommand StartStopCommand 110 | { 111 | get 112 | { 113 | return new DelegateCommand(() => 114 | { 115 | if (IsServerStarted) 116 | { 117 | Stop(); 118 | } 119 | else 120 | { 121 | Start(); 122 | } 123 | }); 124 | } 125 | } 126 | 127 | #endregion 128 | 129 | #region constructors 130 | 131 | public UdpViewModel() 132 | { 133 | _udpClientServer = new UdpClientServer(); 134 | LocalInterfaces = new ObservableCollection(); 135 | 136 | _sendViewModel.SendData += OnSend; 137 | _udpClientServer.StatusChanged += 138 | (sender, arg) => 139 | { 140 | IsServerStarted = (arg.ServerStatus == EServerStatus.Started); 141 | 142 | if (IsServerStarted) 143 | { 144 | History.Header = "Listening on: < " + arg.ServerInfo.ToString() + " >"; 145 | } 146 | else 147 | { 148 | History.Header = "Conversation"; 149 | } 150 | }; 151 | 152 | _udpClientServer.Received += 153 | (sender, arg) => 154 | { 155 | History.Append(arg.Message); 156 | }; 157 | 158 | ListenPort = 0; 159 | History.Header = "Conversation"; 160 | Send.IpAddress = "localhost"; 161 | Send.Port = 0; 162 | 163 | RebuildInterfaceList(); 164 | 165 | Properties.Settings.Default.PropertyChanged += (sender, e) => 166 | { 167 | if(e.PropertyName == nameof(Properties.Settings.Default.IPv6Support)) 168 | { 169 | RebuildInterfaceList(); 170 | } 171 | }; 172 | 173 | NetworkUtils.NetworkInterfaceChange += () => 174 | { 175 | Application.Current.Dispatcher.Invoke(RebuildInterfaceList); 176 | }; 177 | } 178 | 179 | #endregion 180 | 181 | #region private functions 182 | 183 | private void Start() 184 | { 185 | if (!ValidateStart()) 186 | return; 187 | 188 | try 189 | { 190 | _udpClientServer.Start(SelectedInterface.Address, ListenPort.Value); 191 | } 192 | catch(Exception ex) 193 | { 194 | DialogUtils.ShowErrorDialog(ex.Message); 195 | } 196 | } 197 | 198 | private void Stop() 199 | { 200 | _udpClientServer.Stop(); 201 | } 202 | 203 | private async void OnSend(byte[] data) 204 | { 205 | if (!ValidateSend()) 206 | return; 207 | 208 | try 209 | { 210 | var msg = new Transmission(data, Transmission.EType.Sent); 211 | History.Append(msg); 212 | var res = await _udpClientServer.SendAsync(Send.IpAddress, Send.Port.Value, msg); 213 | if (res != null) 214 | { 215 | msg.Origin = res.From; 216 | msg.Destination = res.To; 217 | Send.Message = ""; 218 | } 219 | } 220 | catch (Exception ex) 221 | { 222 | Console.Write(ex.StackTrace); 223 | DialogUtils.ShowErrorDialog(ex.Message); 224 | } 225 | } 226 | 227 | private bool ValidateStart() 228 | { 229 | string error = null; 230 | if (HasError(nameof(ListenPort))) 231 | error = GetError(nameof(ListenPort)); 232 | 233 | if (error != null) 234 | { 235 | DialogUtils.ShowErrorDialog(error); 236 | return false; 237 | } 238 | 239 | return true; 240 | } 241 | 242 | private bool ValidateSend() 243 | { 244 | string error = null; 245 | if (Send.HasError(nameof(Send.IpAddress))) 246 | error = Send.GetError(nameof(Send.IpAddress)); 247 | else if (Send.HasError(nameof(Send.Port))) 248 | error = Send.GetError(nameof(Send.Port)); 249 | 250 | if (error != null) 251 | { 252 | DialogUtils.ShowErrorDialog(error); 253 | return false; 254 | } 255 | 256 | return true; 257 | } 258 | 259 | private void RebuildInterfaceList() 260 | { 261 | LocalInterfaces.Clear(); 262 | 263 | // build interface list 264 | LocalInterfaces.Add(new InterfaceAddress(InterfaceAddress.EInterfaceType.Any, null, IPAddress.Any)); 265 | if (Properties.Settings.Default.IPv6Support) 266 | { 267 | LocalInterfaces.Add(new InterfaceAddress(InterfaceAddress.EInterfaceType.Any, null, IPAddress.IPv6Any)); 268 | } 269 | 270 | foreach (var nic in NetworkUtils.GetActiveInterfaces()) 271 | { 272 | foreach (var ip in nic.IPv4.Addresses) 273 | { 274 | LocalInterfaces.Add(new InterfaceAddress( 275 | InterfaceAddress.EInterfaceType.Specific, nic, ip)); 276 | } 277 | 278 | if (Properties.Settings.Default.IPv6Support) 279 | { 280 | foreach (var ip in nic.IPv6.Addresses) 281 | { 282 | LocalInterfaces.Add(new InterfaceAddress( 283 | InterfaceAddress.EInterfaceType.Specific, nic, ip)); 284 | } 285 | } 286 | } 287 | 288 | SelectedInterface = LocalInterfaces.FirstOrDefault(); 289 | } 290 | 291 | public void Dispose() 292 | { 293 | _udpClientServer?.Dispose(); 294 | _historyViewModel?.Dispose(); 295 | } 296 | 297 | #endregion 298 | 299 | } 300 | } 301 | -------------------------------------------------------------------------------- /icon/tcp-udp-tool-icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielnilsson9/tcp-udp-tool/1a115ad0c43b9d908118f85b9748b075753cb52d/icon/tcp-udp-tool-icon.ico -------------------------------------------------------------------------------- /icon/tcp-udp-tool-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielnilsson9/tcp-udp-tool/1a115ad0c43b9d908118f85b9748b075753cb52d/icon/tcp-udp-tool-icon.png -------------------------------------------------------------------------------- /icon/tcp-udp-tool-icon.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/danielnilsson9/tcp-udp-tool/1a115ad0c43b9d908118f85b9748b075753cb52d/icon/tcp-udp-tool-icon.psd --------------------------------------------------------------------------------