1
0
Bruce Wayne 6 жил өмнө
parent
commit
70dfb1dab0

+ 7 - 7
NatTypeTester.sln

@@ -1,9 +1,9 @@
 
 Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 15
-VisualStudioVersion = 15.0.27703.2000
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29102.190
 MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NatTypeTester", "NatTypeTester\NatTypeTester.csproj", "{F853BCC3-9695-4312-8869-5DA556FA0056}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NatTypeTester", "NatTypeTester\NatTypeTester.csproj", "{B5104123-EB01-4079-8865-2A99DD91DC24}"
 EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -11,10 +11,10 @@ Global
 		Release|Any CPU = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(ProjectConfigurationPlatforms) = postSolution
-		{F853BCC3-9695-4312-8869-5DA556FA0056}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{F853BCC3-9695-4312-8869-5DA556FA0056}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{F853BCC3-9695-4312-8869-5DA556FA0056}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{F853BCC3-9695-4312-8869-5DA556FA0056}.Release|Any CPU.Build.0 = Release|Any CPU
+		{B5104123-EB01-4079-8865-2A99DD91DC24}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{B5104123-EB01-4079-8865-2A99DD91DC24}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{B5104123-EB01-4079-8865-2A99DD91DC24}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{B5104123-EB01-4079-8865-2A99DD91DC24}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE

+ 0 - 6
NatTypeTester/App.config

@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<configuration>
-    <startup> 
-        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8"/>
-    </startup>
-</configuration>

+ 21 - 0
NatTypeTester/App.cs

@@ -0,0 +1,21 @@
+using System;
+using System.Windows;
+
+namespace NatTypeTester
+{
+	internal static class App
+	{
+		[STAThread]
+		private static void Main()
+		{
+			var app = new Application();
+			var win = new MainWindow();
+
+			app.MainWindow = win;
+			win.Show();
+
+			app.ShutdownMode = ShutdownMode.OnMainWindowClose;
+			app.Run();
+		}
+	}
+}

+ 23 - 0
NatTypeTester/Controls/NumberUpDown.xaml

@@ -0,0 +1,23 @@
+<UserControl x:Class="NatTypeTester.Controls.NumberUpDown"
+             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
+             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+             mc:Ignorable="d"
+             x:Name="Uc"
+             Loaded="UserControl_Loaded">
+    <Grid LostFocus="Grid_LostFocus">
+        <Grid.RowDefinitions>
+            <RowDefinition Height="1*" />
+            <RowDefinition Height="1*" />
+        </Grid.RowDefinitions>
+        <Grid.ColumnDefinitions>
+            <ColumnDefinition Width="*" />
+            <ColumnDefinition Width="12" />
+        </Grid.ColumnDefinitions>
+        <TextBox x:FieldModifier="private" Grid.Row="0" Grid.Column="0" Grid.RowSpan="2" x:Name="TxtNum"
+        Text="{Binding Value , Mode=TwoWay, ElementName=Uc,UpdateSourceTrigger=PropertyChanged}" TextChanged="TxtNum_TextChanged" VerticalContentAlignment="Center"/>
+        <RepeatButton Grid.Row="0" Grid.Column="1" Content="▲" Click="Up_Click" FontSize="6" />
+        <RepeatButton Grid.Row="1" Grid.Column="1" Content="▼" Click="Down_Click" FontSize="6" />
+    </Grid>
+</UserControl>

+ 114 - 0
NatTypeTester/Controls/NumberUpDown.xaml.cs

@@ -0,0 +1,114 @@
+using System;
+using System.Windows;
+using System.Windows.Controls;
+
+namespace NatTypeTester.Controls
+{
+	public partial class NumberUpDown
+	{
+		private int _numValue;
+
+		private int _maxNum = 65535;
+
+		private int _minNum;
+
+		public int NumValue
+		{
+			get
+			{
+				if (_numValue > _maxNum)
+				{
+					return _maxNum;
+				}
+				if (_numValue < _minNum)
+				{
+					return _minNum;
+				}
+				return _numValue;
+			}
+			set
+			{
+				if (_numValue != value)
+				{
+					_numValue = value;
+					TxtNum.Text = value.ToString();
+					ValueChanged?.Invoke(this, new EventArgs());
+				}
+			}
+		}
+
+		public string Value
+		{
+			get => GetValue(ValueProperty) as string;
+			set => SetValue(ValueProperty, value);
+		}
+
+		public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(@"Value", typeof(string), typeof(NumberUpDown));
+
+		public int MinNum
+		{
+			get => _minNum;
+			set => _minNum = value > _maxNum ? _maxNum : value;
+		}
+
+		public int MaxNum
+		{
+			get => _maxNum;
+			set => _maxNum = value < _minNum ? _minNum : value;
+		}
+
+		public event EventHandler ValueChanged;
+
+		public NumberUpDown()
+		{
+			InitializeComponent();
+		}
+
+		private void Up_Click(object sender, RoutedEventArgs e)
+		{
+			if (NumValue < _maxNum)
+			{
+				++NumValue;
+			}
+			else
+			{
+				NumValue = _maxNum;
+			}
+		}
+
+		private void Down_Click(object sender, RoutedEventArgs e)
+		{
+			if (NumValue > _minNum)
+			{
+				--NumValue;
+			}
+			else
+			{
+				NumValue = _minNum;
+			}
+		}
+
+		private void TxtNum_TextChanged(object sender, TextChangedEventArgs e)
+		{
+			if (TxtNum == null)
+			{
+				return;
+			}
+
+			if (int.TryParse(TxtNum.Text, out var num))
+			{
+				NumValue = num;
+			}
+		}
+
+		private void UserControl_Loaded(object sender, RoutedEventArgs e)
+		{
+			TxtNum.Text = NumValue.ToString();
+		}
+
+		private void Grid_LostFocus(object sender, RoutedEventArgs e)
+		{
+			TxtNum.Text = NumValue.ToString();
+		}
+	}
+}

+ 0 - 195
NatTypeTester/MainForm.Designer.cs

@@ -1,195 +0,0 @@
-namespace NatTypeTester
-{
-	partial class MainForm
-	{
-		/// <summary>
-		/// 必需的设计器变量。
-		/// </summary>
-		private System.ComponentModel.IContainer components = null;
-
-		/// <summary>
-		/// 清理所有正在使用的资源。
-		/// </summary>
-		/// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
-		protected override void Dispose(bool disposing)
-		{
-			if (disposing && (components != null))
-			{
-				components.Dispose();
-			}
-			base.Dispose(disposing);
-		}
-
-		#region Windows 窗体设计器生成的代码
-
-		/// <summary>
-		/// 设计器支持所需的方法 - 不要修改
-		/// 使用代码编辑器修改此方法的内容。
-		/// </summary>
-		private void InitializeComponent()
-		{
-			this.label1 = new System.Windows.Forms.Label();
-			this.label2 = new System.Windows.Forms.Label();
-			this.label3 = new System.Windows.Forms.Label();
-			this.label4 = new System.Windows.Forms.Label();
-			this.textBox2 = new System.Windows.Forms.TextBox();
-			this.textBox3 = new System.Windows.Forms.TextBox();
-			this.textBox4 = new System.Windows.Forms.TextBox();
-			this.numericUpDown1 = new System.Windows.Forms.NumericUpDown();
-			this.button1 = new System.Windows.Forms.Button();
-			this.comboBox1 = new System.Windows.Forms.ComboBox();
-			((System.ComponentModel.ISupportInitialize)(this.numericUpDown1)).BeginInit();
-			this.SuspendLayout();
-			// 
-			// label1
-			// 
-			this.label1.AutoSize = true;
-			this.label1.Location = new System.Drawing.Point(12, 20);
-			this.label1.Name = "label1";
-			this.label1.Size = new System.Drawing.Size(77, 12);
-			this.label1.TabIndex = 0;
-			this.label1.Text = "STUN Server:";
-			// 
-			// label2
-			// 
-			this.label2.AutoSize = true;
-			this.label2.Location = new System.Drawing.Point(12, 47);
-			this.label2.Name = "label2";
-			this.label2.Size = new System.Drawing.Size(59, 12);
-			this.label2.TabIndex = 1;
-			this.label2.Text = "NAT type:";
-			// 
-			// label3
-			// 
-			this.label3.AutoSize = true;
-			this.label3.Location = new System.Drawing.Point(12, 74);
-			this.label3.Name = "label3";
-			this.label3.Size = new System.Drawing.Size(65, 12);
-			this.label3.TabIndex = 2;
-			this.label3.Text = "Local end:";
-			// 
-			// label4
-			// 
-			this.label4.AutoSize = true;
-			this.label4.Location = new System.Drawing.Point(12, 101);
-			this.label4.Name = "label4";
-			this.label4.Size = new System.Drawing.Size(71, 12);
-			this.label4.TabIndex = 3;
-			this.label4.Text = "Public end:";
-			// 
-			// textBox2
-			// 
-			this.textBox2.Location = new System.Drawing.Point(95, 44);
-			this.textBox2.Name = "textBox2";
-			this.textBox2.ReadOnly = true;
-			this.textBox2.Size = new System.Drawing.Size(240, 21);
-			this.textBox2.TabIndex = 5;
-			// 
-			// textBox3
-			// 
-			this.textBox3.Location = new System.Drawing.Point(95, 71);
-			this.textBox3.Name = "textBox3";
-			this.textBox3.Size = new System.Drawing.Size(240, 21);
-			this.textBox3.TabIndex = 6;
-			this.textBox3.Text = "0.0.0.0:0";
-			// 
-			// textBox4
-			// 
-			this.textBox4.Location = new System.Drawing.Point(95, 98);
-			this.textBox4.Name = "textBox4";
-			this.textBox4.ReadOnly = true;
-			this.textBox4.Size = new System.Drawing.Size(240, 21);
-			this.textBox4.TabIndex = 7;
-			// 
-			// numericUpDown1
-			// 
-			this.numericUpDown1.Location = new System.Drawing.Point(275, 17);
-			this.numericUpDown1.Maximum = new decimal(new int[] {
-            65535,
-            0,
-            0,
-            0});
-			this.numericUpDown1.Minimum = new decimal(new int[] {
-            1,
-            0,
-            0,
-            0});
-			this.numericUpDown1.Name = "numericUpDown1";
-			this.numericUpDown1.Size = new System.Drawing.Size(60, 21);
-			this.numericUpDown1.TabIndex = 8;
-			this.numericUpDown1.Value = new decimal(new int[] {
-            3478,
-            0,
-            0,
-            0});
-			// 
-			// button1
-			// 
-			this.button1.Location = new System.Drawing.Point(260, 129);
-			this.button1.Name = "button1";
-			this.button1.Size = new System.Drawing.Size(75, 23);
-			this.button1.TabIndex = 9;
-			this.button1.Text = "Test";
-			this.button1.UseVisualStyleBackColor = true;
-			this.button1.Click += new System.EventHandler(this.button1_Click);
-			// 
-			// comboBox1
-			// 
-			this.comboBox1.FormattingEnabled = true;
-			this.comboBox1.Items.AddRange(new object[] {
-            "stun.miwifi.com",
-            "stun.bige0.com",
-            "stun.stunprotocol.org",
-            "iphone-stun.strato-iphone.de",
-            "stun.voipstunt.com",
-            "stun.xten.com",
-            "stun.schlund.de",
-            "numb.viagenie.ca",
-            "stun.ekiga.net"});
-			this.comboBox1.Location = new System.Drawing.Point(95, 16);
-			this.comboBox1.Name = "comboBox1";
-			this.comboBox1.Size = new System.Drawing.Size(174, 20);
-			this.comboBox1.TabIndex = 10;
-			// 
-			// MainForm
-			// 
-			this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
-			this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
-			this.ClientSize = new System.Drawing.Size(351, 164);
-			this.Controls.Add(this.comboBox1);
-			this.Controls.Add(this.button1);
-			this.Controls.Add(this.numericUpDown1);
-			this.Controls.Add(this.textBox4);
-			this.Controls.Add(this.textBox3);
-			this.Controls.Add(this.textBox2);
-			this.Controls.Add(this.label4);
-			this.Controls.Add(this.label3);
-			this.Controls.Add(this.label2);
-			this.Controls.Add(this.label1);
-			this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
-			this.MaximizeBox = false;
-			this.Name = "MainForm";
-			this.ShowIcon = false;
-			this.Text = "NatTypeTester";
-			this.Load += new System.EventHandler(this.MainForm_Load);
-			((System.ComponentModel.ISupportInitialize)(this.numericUpDown1)).EndInit();
-			this.ResumeLayout(false);
-			this.PerformLayout();
-
-		}
-
-		#endregion
-
-		private System.Windows.Forms.Label label1;
-		private System.Windows.Forms.Label label2;
-		private System.Windows.Forms.Label label3;
-		private System.Windows.Forms.Label label4;
-		private System.Windows.Forms.TextBox textBox2;
-		private System.Windows.Forms.TextBox textBox3;
-		private System.Windows.Forms.TextBox textBox4;
-		private System.Windows.Forms.NumericUpDown numericUpDown1;
-		private System.Windows.Forms.Button button1;
-		private System.Windows.Forms.ComboBox comboBox1;
-	}
-}
-

+ 0 - 102
NatTypeTester/MainForm.cs

@@ -1,102 +0,0 @@
-using System;
-using System.Net;
-using System.Net.Sockets;
-using System.Threading.Tasks;
-using System.Windows.Forms;
-using LumiSoft.Net.STUN.Client;
-
-namespace NatTypeTester
-{
-	public partial class MainForm : Form
-	{
-		public MainForm()
-		{
-			InitializeComponent();
-		}
-
-		private delegate void VoidMethodDelegate();
-
-		private static string[] Core(string local, string server, int port)
-		{
-			try
-			{
-				if (string.IsNullOrWhiteSpace(server))
-				{
-					MessageBox.Show(@"Please specify STUN server !", @"Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
-					return null;
-				}
-
-				using (var socketv4 = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp))
-				{
-					if (local != string.Empty)
-					{
-						var ip_port = local.Split(':');
-						socketv4.Bind(new IPEndPoint(IPAddress.Parse(ip_port[0]), Convert.ToInt32(ip_port[1])));
-					}
-					else
-					{
-						socketv4.Bind(new IPEndPoint(IPAddress.Any, 0));
-					}
-
-					var result = STUN_Client.Query(server, port, socketv4);
-
-					return new[]
-					{
-							result.NetType.ToString(),
-							socketv4.LocalEndPoint.ToString(),
-							result.NetType != STUN_NetType.UdpBlocked ? result.PublicEndPoint.ToString() : string.Empty
-					};
-				}
-			}
-			catch (Exception ex)
-			{
-				MessageBox.Show($@"Error: {ex}", @"Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
-				return null;
-			}
-			finally
-			{
-
-			}
-		}
-
-		private void button1_Click(object sender, EventArgs e)
-		{
-			try
-			{
-				button1.Enabled = false;
-				var server = comboBox1.Text;
-				var port = Convert.ToInt32(numericUpDown1.Value);
-				var local = textBox3.Text;
-				string[] res = null;
-				var t = new Task(() =>
-				{
-					res = Core(local, server, port);
-				});
-				t.Start();
-				t.ContinueWith(task =>
-				{
-					BeginInvoke(new VoidMethodDelegate(() =>
-					{
-						if (res != null)
-						{
-							textBox2.Text = res[0];
-							textBox3.Text = res[1];
-							textBox4.Text = res[2];
-						}
-						button1.Enabled = true;
-					}));
-				});
-			}
-			catch (Exception ex)
-			{
-				MessageBox.Show($@"Error: {ex}", @"Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
-				button1.Enabled = true;
-			}
-		}
-
-		private void MainForm_Load(object sender, EventArgs e)
-		{
-			comboBox1.SelectedIndex = 0;
-		}
-	}
-}

+ 0 - 120
NatTypeTester/MainForm.resx

@@ -1,120 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<root>
-  <!-- 
-    Microsoft ResX Schema 
-    
-    Version 2.0
-    
-    The primary goals of this format is to allow a simple XML format 
-    that is mostly human readable. The generation and parsing of the 
-    various data types are done through the TypeConverter classes 
-    associated with the data types.
-    
-    Example:
-    
-    ... ado.net/XML headers & schema ...
-    <resheader name="resmimetype">text/microsoft-resx</resheader>
-    <resheader name="version">2.0</resheader>
-    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
-    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
-    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
-    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
-    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
-        <value>[base64 mime encoded serialized .NET Framework object]</value>
-    </data>
-    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
-        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
-        <comment>This is a comment</comment>
-    </data>
-                
-    There are any number of "resheader" rows that contain simple 
-    name/value pairs.
-    
-    Each data row contains a name, and value. The row also contains a 
-    type or mimetype. Type corresponds to a .NET class that support 
-    text/value conversion through the TypeConverter architecture. 
-    Classes that don't support this are serialized and stored with the 
-    mimetype set.
-    
-    The mimetype is used for serialized objects, and tells the 
-    ResXResourceReader how to depersist the object. This is currently not 
-    extensible. For a given mimetype the value must be set accordingly:
-    
-    Note - application/x-microsoft.net.object.binary.base64 is the format 
-    that the ResXResourceWriter will generate, however the reader can 
-    read any of the formats listed below.
-    
-    mimetype: application/x-microsoft.net.object.binary.base64
-    value   : The object must be serialized with 
-            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
-            : and then encoded with base64 encoding.
-    
-    mimetype: application/x-microsoft.net.object.soap.base64
-    value   : The object must be serialized with 
-            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
-            : and then encoded with base64 encoding.
-
-    mimetype: application/x-microsoft.net.object.bytearray.base64
-    value   : The object must be serialized into a byte array 
-            : using a System.ComponentModel.TypeConverter
-            : and then encoded with base64 encoding.
-    -->
-  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
-    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
-    <xsd:element name="root" msdata:IsDataSet="true">
-      <xsd:complexType>
-        <xsd:choice maxOccurs="unbounded">
-          <xsd:element name="metadata">
-            <xsd:complexType>
-              <xsd:sequence>
-                <xsd:element name="value" type="xsd:string" minOccurs="0" />
-              </xsd:sequence>
-              <xsd:attribute name="name" use="required" type="xsd:string" />
-              <xsd:attribute name="type" type="xsd:string" />
-              <xsd:attribute name="mimetype" type="xsd:string" />
-              <xsd:attribute ref="xml:space" />
-            </xsd:complexType>
-          </xsd:element>
-          <xsd:element name="assembly">
-            <xsd:complexType>
-              <xsd:attribute name="alias" type="xsd:string" />
-              <xsd:attribute name="name" type="xsd:string" />
-            </xsd:complexType>
-          </xsd:element>
-          <xsd:element name="data">
-            <xsd:complexType>
-              <xsd:sequence>
-                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
-                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
-              </xsd:sequence>
-              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
-              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
-              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
-              <xsd:attribute ref="xml:space" />
-            </xsd:complexType>
-          </xsd:element>
-          <xsd:element name="resheader">
-            <xsd:complexType>
-              <xsd:sequence>
-                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
-              </xsd:sequence>
-              <xsd:attribute name="name" type="xsd:string" use="required" />
-            </xsd:complexType>
-          </xsd:element>
-        </xsd:choice>
-      </xsd:complexType>
-    </xsd:element>
-  </xsd:schema>
-  <resheader name="resmimetype">
-    <value>text/microsoft-resx</value>
-  </resheader>
-  <resheader name="version">
-    <value>2.0</value>
-  </resheader>
-  <resheader name="reader">
-    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
-  </resheader>
-  <resheader name="writer">
-    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
-  </resheader>
-</root>

+ 54 - 0
NatTypeTester/MainWindow.xaml

@@ -0,0 +1,54 @@
+<Window x:Class="NatTypeTester.MainWindow"
+        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+        xmlns:controls="clr-namespace:NatTypeTester.Controls"
+        mc:Ignorable="d"
+        Title="NatTypeTester" Height="210" Width="385" WindowStartupLocation="CenterScreen" ResizeMode="CanMinimize"
+        KeyDown="MainWindow_OnKeyDown">
+
+	<Window.Resources>
+		<SolidColorBrush x:Key="DisabledBackgroundBrush" Color="LightGray" />
+		<Style TargetType="{x:Type TextBox}">
+			<Style.Triggers>
+				<Trigger Property="IsReadOnly" Value="True">
+					<Setter Property="Background" Value="{StaticResource DisabledBackgroundBrush}" />
+				</Trigger>
+			</Style.Triggers>
+		</Style>
+	</Window.Resources>
+
+	<Grid>
+		<Grid.RowDefinitions>
+			<RowDefinition />
+			<RowDefinition />
+			<RowDefinition />
+			<RowDefinition />
+			<RowDefinition />
+		</Grid.RowDefinitions>
+		<Grid.ColumnDefinitions>
+			<ColumnDefinition Width="Auto"/>
+			<ColumnDefinition />
+			<ColumnDefinition Width="65" />
+		</Grid.ColumnDefinitions>
+
+		<TextBlock Grid.Row="0" Grid.Column="0" Margin="5,0" VerticalAlignment="Center" Text="STUN Server" />
+		<TextBlock Grid.Row="1" Grid.Column="0" Margin="5,0" VerticalAlignment="Center"
+		           Text="NAT type" />
+		<TextBlock Grid.Row="2" Grid.Column="0" Margin="5,0" VerticalAlignment="Center" Text="Local end" />
+		<TextBlock Grid.Row="3" Grid.Column="0" Margin="5,0" VerticalAlignment="Center"
+		           Text="Public end" />
+
+		<ComboBox x:Name="ServersComboBox" Grid.Row="0" Grid.Column="1" Height="23.24" IsEditable="True" VerticalContentAlignment="Center" />
+		<controls:NumberUpDown x:Name="PortNumber" Grid.Row="0" Grid.Column="2" Height="23.24" Margin="5,0" MinNum="1" MaxNum="65535" NumValue="3478"/>
+		<TextBox x:Name="NatTypeTextBox" Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" Height="23.24" Margin="0,5,5,0"
+		IsReadOnly="True" VerticalContentAlignment="Center" />
+		<TextBox x:Name="LocalEndTextBox" Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="2" Height="23.24" Margin="0,5,5,0" 
+		VerticalContentAlignment="Center"/>
+		<TextBox x:Name="PublicEndTextBox" Grid.Row="3" Grid.Column="1" Grid.ColumnSpan="2" Height="23.24" Margin="0,5,5,0" 
+		IsReadOnly="True" VerticalContentAlignment="Center" />
+
+		<Button x:Name="TestButton" Grid.Row="4" Grid.Column="2" Content="Test" Margin="0,5,5,10" Click="TestButton_OnClick"/>
+	</Grid>
+</Window>

+ 66 - 0
NatTypeTester/MainWindow.xaml.cs

@@ -0,0 +1,66 @@
+using System;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Input;
+
+namespace NatTypeTester
+{
+	public partial class MainWindow
+	{
+		public MainWindow()
+		{
+			InitializeComponent();
+
+			foreach (var stunServer in StunServers)
+			{
+				ServersComboBox.Items.Add(stunServer);
+			}
+
+			ServersComboBox.SelectedIndex = 0;
+			LocalEndTextBox.Text = Utils.Utils.DefaultLocalEnd;
+		}
+
+		private static readonly string[] StunServers =
+		{
+				@"stun.miwifi.com",
+				@"stun.bige0.com",
+				@"stun.syncthing.net",
+				@"stun.stunprotocol.org",
+				@"iphone-stun.strato-iphone.de",
+				@"stun.voipstunt.com",
+				@"stun.xten.com",
+				@"stun.schlund.de",
+				@"numb.viagenie.ca",
+				@"stun.ekiga.net",
+				@"stun.sipgate.net",
+		};
+
+		private void TestButton_OnClick(object sender, RoutedEventArgs e)
+		{
+			TestButton.IsEnabled = false;
+			var server = ServersComboBox.Text;
+			var port = PortNumber.NumValue;
+			var local = LocalEndTextBox.Text;
+			Task.Run(() =>
+			{
+				var (natType, localEnd, publicEnd) = Utils.Utils.NatTypeTestCore(local, server, port);
+
+				Dispatcher.BeginInvoke(new Action(() =>
+				{
+					NatTypeTextBox.Text = natType;
+					LocalEndTextBox.Text = localEnd;
+					PublicEndTextBox.Text = publicEnd;
+					TestButton.IsEnabled = true;
+				}));
+			});
+		}
+
+		private void MainWindow_OnKeyDown(object sender, KeyEventArgs e)
+		{
+			if (e.Key == Key.Enter)
+			{
+				TestButton_OnClick(this, new RoutedEventArgs());
+			}
+		}
+	}
+}

+ 11 - 78
NatTypeTester/NatTypeTester.csproj

@@ -1,52 +1,18 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
+
   <PropertyGroup>
-    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
-    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
-    <ProjectGuid>{F853BCC3-9695-4312-8869-5DA556FA0056}</ProjectGuid>
     <OutputType>WinExe</OutputType>
-    <RootNamespace>NatTypeTester</RootNamespace>
-    <AssemblyName>NatTypeTester</AssemblyName>
-    <TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
-    <FileAlignment>512</FileAlignment>
-    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
-    <TargetFrameworkProfile />
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
-    <PlatformTarget>AnyCPU</PlatformTarget>
-    <DebugSymbols>true</DebugSymbols>
-    <DebugType>full</DebugType>
-    <Optimize>false</Optimize>
-    <OutputPath>bin\Debug\</OutputPath>
-    <DefineConstants>DEBUG;TRACE</DefineConstants>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <Prefer32Bit>false</Prefer32Bit>
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
-    <PlatformTarget>AnyCPU</PlatformTarget>
-    <DebugType>pdbonly</DebugType>
-    <Optimize>true</Optimize>
-    <OutputPath>bin\Release\</OutputPath>
-    <DefineConstants>TRACE</DefineConstants>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <Prefer32Bit>false</Prefer32Bit>
+    <TargetFramework>net48</TargetFramework>
+    <UseWPF>true</UseWPF>
   </PropertyGroup>
+
   <ItemGroup>
-    <Reference Include="System" />
-    <Reference Include="System.Core" />
-    <Reference Include="System.Xml.Linq" />
-    <Reference Include="System.Data.DataSetExtensions" />
-    <Reference Include="Microsoft.CSharp" />
-    <Reference Include="System.Data" />
-    <Reference Include="System.Deployment" />
-    <Reference Include="System.Drawing" />
-    <Reference Include="System.Net.Http" />
-    <Reference Include="System.Windows.Forms" />
-    <Reference Include="System.Xml" />
+    <Compile Remove="Lumisoft.Net\**" />
+    <EmbeddedResource Remove="Lumisoft.Net\**" />
+    <None Remove="Lumisoft.Net\**" />
+    <Page Remove="Lumisoft.Net\**" />
   </ItemGroup>
+
   <ItemGroup>
     <Compile Include="Lumisoft.Net\Net\AsyncOP_State.cs" />
     <Compile Include="Lumisoft.Net\Net\EventArgs.cs" />
@@ -77,39 +43,6 @@
     <Compile Include="Lumisoft.Net\Net\STUN\Message\STUN_MessageType.cs" />
     <Compile Include="Lumisoft.Net\Net\STUN\Message\STUN_t_ChangeRequest.cs" />
     <Compile Include="Lumisoft.Net\Net\STUN\Message\STUN_t_ErrorCode.cs" />
-    <Compile Include="MainForm.cs">
-      <SubType>Form</SubType>
-    </Compile>
-    <Compile Include="MainForm.Designer.cs">
-      <DependentUpon>MainForm.cs</DependentUpon>
-    </Compile>
-    <Compile Include="Program.cs" />
-    <Compile Include="Properties\AssemblyInfo.cs" />
-    <EmbeddedResource Include="MainForm.resx">
-      <DependentUpon>MainForm.cs</DependentUpon>
-    </EmbeddedResource>
-    <EmbeddedResource Include="Properties\Resources.resx">
-      <Generator>ResXFileCodeGenerator</Generator>
-      <LastGenOutput>Resources.Designer.cs</LastGenOutput>
-      <SubType>Designer</SubType>
-    </EmbeddedResource>
-    <Compile Include="Properties\Resources.Designer.cs">
-      <AutoGen>True</AutoGen>
-      <DependentUpon>Resources.resx</DependentUpon>
-      <DesignTime>True</DesignTime>
-    </Compile>
-    <None Include="Properties\Settings.settings">
-      <Generator>SettingsSingleFileGenerator</Generator>
-      <LastGenOutput>Settings.Designer.cs</LastGenOutput>
-    </None>
-    <Compile Include="Properties\Settings.Designer.cs">
-      <AutoGen>True</AutoGen>
-      <DependentUpon>Settings.settings</DependentUpon>
-      <DesignTimeSharedInput>True</DesignTimeSharedInput>
-    </Compile>
-  </ItemGroup>
-  <ItemGroup>
-    <None Include="App.config" />
   </ItemGroup>
-  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+
 </Project>

+ 0 - 19
NatTypeTester/Program.cs

@@ -1,19 +0,0 @@
-using System;
-using System.Windows.Forms;
-
-namespace NatTypeTester
-{
-	static class Program
-	{
-		/// <summary>
-		/// 应用程序的主入口点。
-		/// </summary>
-		[STAThread]
-		static void Main()
-		{
-			Application.EnableVisualStyles();
-			Application.SetCompatibleTextRenderingDefault(false);
-			Application.Run(new MainForm());
-		}
-	}
-}

+ 0 - 36
NatTypeTester/Properties/AssemblyInfo.cs

@@ -1,36 +0,0 @@
-using System.Reflection;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-
-// 有关程序集的一般信息由以下
-// 控制。更改这些特性值可修改
-// 与程序集关联的信息。
-[assembly: AssemblyTitle("NatTypeTester")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("NatTypeTester")]
-[assembly: AssemblyCopyright("Copyright ©  2018")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-
-// 将 ComVisible 设置为 false 会使此程序集中的类型
-//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型
-//请将此类型的 ComVisible 特性设置为 true。
-[assembly: ComVisible(false)]
-
-// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
-[assembly: Guid("f853bcc3-9695-4312-8869-5da556fa0056")]
-
-// 程序集的版本信息由下列四个值组成: 
-//
-//      主版本
-//      次版本
-//      生成号
-//      修订号
-//
-// 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号
-// 方法是按如下所示使用“*”: :
-// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.3.0.0")]
-[assembly: AssemblyFileVersion("1.3.0.0")]

+ 0 - 63
NatTypeTester/Properties/Resources.Designer.cs

@@ -1,63 +0,0 @@
-//------------------------------------------------------------------------------
-// <auto-generated>
-//     此代码由工具生成。
-//     运行时版本:4.0.30319.42000
-//
-//     对此文件的更改可能会导致不正确的行为,并且如果
-//     重新生成代码,这些更改将会丢失。
-// </auto-generated>
-//------------------------------------------------------------------------------
-
-namespace NatTypeTester.Properties {
-    using System;
-    
-    
-    /// <summary>
-    ///   一个强类型的资源类,用于查找本地化的字符串等。
-    /// </summary>
-    // 此类是由 StronglyTypedResourceBuilder
-    // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
-    // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
-    // (以 /str 作为命令选项),或重新生成 VS 项目。
-    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
-    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
-    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
-    internal class Resources {
-        
-        private static global::System.Resources.ResourceManager resourceMan;
-        
-        private static global::System.Globalization.CultureInfo resourceCulture;
-        
-        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
-        internal Resources() {
-        }
-        
-        /// <summary>
-        ///   返回此类使用的缓存的 ResourceManager 实例。
-        /// </summary>
-        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
-        internal static global::System.Resources.ResourceManager ResourceManager {
-            get {
-                if (object.ReferenceEquals(resourceMan, null)) {
-                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("NatTypeTester.Properties.Resources", typeof(Resources).Assembly);
-                    resourceMan = temp;
-                }
-                return resourceMan;
-            }
-        }
-        
-        /// <summary>
-        ///   重写当前线程的 CurrentUICulture 属性
-        ///   重写当前线程的 CurrentUICulture 属性。
-        /// </summary>
-        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
-        internal static global::System.Globalization.CultureInfo Culture {
-            get {
-                return resourceCulture;
-            }
-            set {
-                resourceCulture = value;
-            }
-        }
-    }
-}

+ 0 - 120
NatTypeTester/Properties/Resources.resx

@@ -1,120 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<root>
-  <!-- 
-    Microsoft ResX Schema 
-    
-    Version 2.0
-    
-    The primary goals of this format is to allow a simple XML format 
-    that is mostly human readable. The generation and parsing of the 
-    various data types are done through the TypeConverter classes 
-    associated with the data types.
-    
-    Example:
-    
-    ... ado.net/XML headers & schema ...
-    <resheader name="resmimetype">text/microsoft-resx</resheader>
-    <resheader name="version">2.0</resheader>
-    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
-    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
-    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
-    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
-    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
-        <value>[base64 mime encoded serialized .NET Framework object]</value>
-    </data>
-    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
-        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
-        <comment>This is a comment</comment>
-    </data>
-                
-    There are any number of "resheader" rows that contain simple 
-    name/value pairs.
-    
-    Each data row contains a name, and value. The row also contains a 
-    type or mimetype. Type corresponds to a .NET class that support 
-    text/value conversion through the TypeConverter architecture. 
-    Classes that don't support this are serialized and stored with the 
-    mimetype set.
-    
-    The mimetype is used for serialized objects, and tells the 
-    ResXResourceReader how to depersist the object. This is currently not 
-    extensible. For a given mimetype the value must be set accordingly:
-    
-    Note - application/x-microsoft.net.object.binary.base64 is the format 
-    that the ResXResourceWriter will generate, however the reader can 
-    read any of the formats listed below.
-    
-    mimetype: application/x-microsoft.net.object.binary.base64
-    value   : The object must be serialized with 
-            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
-            : and then encoded with base64 encoding.
-    
-    mimetype: application/x-microsoft.net.object.soap.base64
-    value   : The object must be serialized with 
-            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
-            : and then encoded with base64 encoding.
-
-    mimetype: application/x-microsoft.net.object.bytearray.base64
-    value   : The object must be serialized into a byte array 
-            : using a System.ComponentModel.TypeConverter
-            : and then encoded with base64 encoding.
-    -->
-  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
-    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
-    <xsd:element name="root" msdata:IsDataSet="true">
-      <xsd:complexType>
-        <xsd:choice maxOccurs="unbounded">
-          <xsd:element name="metadata">
-            <xsd:complexType>
-              <xsd:sequence>
-                <xsd:element name="value" type="xsd:string" minOccurs="0" />
-              </xsd:sequence>
-              <xsd:attribute name="name" use="required" type="xsd:string" />
-              <xsd:attribute name="type" type="xsd:string" />
-              <xsd:attribute name="mimetype" type="xsd:string" />
-              <xsd:attribute ref="xml:space" />
-            </xsd:complexType>
-          </xsd:element>
-          <xsd:element name="assembly">
-            <xsd:complexType>
-              <xsd:attribute name="alias" type="xsd:string" />
-              <xsd:attribute name="name" type="xsd:string" />
-            </xsd:complexType>
-          </xsd:element>
-          <xsd:element name="data">
-            <xsd:complexType>
-              <xsd:sequence>
-                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
-                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
-              </xsd:sequence>
-              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
-              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
-              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
-              <xsd:attribute ref="xml:space" />
-            </xsd:complexType>
-          </xsd:element>
-          <xsd:element name="resheader">
-            <xsd:complexType>
-              <xsd:sequence>
-                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
-              </xsd:sequence>
-              <xsd:attribute name="name" type="xsd:string" use="required" />
-            </xsd:complexType>
-          </xsd:element>
-        </xsd:choice>
-      </xsd:complexType>
-    </xsd:element>
-  </xsd:schema>
-  <resheader name="resmimetype">
-    <value>text/microsoft-resx</value>
-  </resheader>
-  <resheader name="version">
-    <value>2.0</value>
-  </resheader>
-  <resheader name="reader">
-    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
-  </resheader>
-  <resheader name="writer">
-    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
-  </resheader>
-</root>

+ 0 - 26
NatTypeTester/Properties/Settings.Designer.cs

@@ -1,26 +0,0 @@
-//------------------------------------------------------------------------------
-// <auto-generated>
-//     此代码由工具生成。
-//     运行时版本:4.0.30319.42000
-//
-//     对此文件的更改可能会导致不正确的行为,并且如果
-//     重新生成代码,这些更改将会丢失。
-// </auto-generated>
-//------------------------------------------------------------------------------
-
-namespace NatTypeTester.Properties {
-    
-    
-    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
-    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.1.0.0")]
-    internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
-        
-        private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
-        
-        public static Settings Default {
-            get {
-                return defaultInstance;
-            }
-        }
-    }
-}

+ 0 - 7
NatTypeTester/Properties/Settings.settings

@@ -1,7 +0,0 @@
-<?xml version='1.0' encoding='utf-8'?>
-<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
-  <Profiles>
-    <Profile Name="(Default)" />
-  </Profiles>
-  <Settings />
-</SettingsFile>

+ 60 - 0
NatTypeTester/Utils/Utils.cs

@@ -0,0 +1,60 @@
+using LumiSoft.Net.STUN.Client;
+using System;
+using System.Net;
+using System.Net.Sockets;
+using System.Windows;
+
+namespace NatTypeTester.Utils
+{
+	public static class Utils
+	{
+		public const string DefaultLocalEnd = @"0.0.0.0:0";
+
+		public static IPEndPoint ParseEndpoint(string str)
+		{
+			var ipPort = str.Trim().Split(':');
+			if (ipPort.Length == 2)
+			{
+				if (IPAddress.TryParse(ipPort[0], out var ip))
+				{
+					if (ushort.TryParse(ipPort[1], out var port))
+					{
+						return new IPEndPoint(ip, port);
+					}
+				}
+			}
+
+			return null;
+		}
+
+		public static (string, string, string) NatTypeTestCore(string local, string server, int port)
+		{
+			try
+			{
+				if (string.IsNullOrWhiteSpace(server))
+				{
+					MessageBox.Show(@"Please specify STUN server !", @"Error", MessageBoxButton.OK, MessageBoxImage.Error);
+					return (string.Empty, DefaultLocalEnd, string.Empty);
+				}
+
+				using (var socketV4 = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp))
+				{
+					var ipe = ParseEndpoint(local) ?? new IPEndPoint(IPAddress.Any, 0);
+					socketV4.Bind(ipe);
+					var result = STUN_Client.Query(server, port, socketV4);
+
+					return (
+							result.NetType.ToString(),
+							socketV4.LocalEndPoint.ToString(),
+							result.NetType != STUN_NetType.UdpBlocked ? result.PublicEndPoint.ToString() : string.Empty
+					);
+				}
+			}
+			catch (Exception ex)
+			{
+				MessageBox.Show($@"Error: {ex}", @"Error", MessageBoxButton.OK, MessageBoxImage.Error);
+				return (string.Empty, DefaultLocalEnd, string.Empty);
+			}
+		}
+	}
+}