Local %!s(int64=5) %!d(string=hai) anos
pai
achega
b9275b9709

+ 1 - 1
UdPunching.Common/BuindInPeerId.cs

@@ -6,7 +6,7 @@ using System.Threading.Tasks;
 
 namespace UdPunching
 {
-    public static class BuindInPeerId
+    public static class BuildInPeerId
     {
         public static readonly Guid Server = Guid.Empty;
         public static readonly Guid Invalid = new Guid("ffffffff-ffff-ffff-ffff-ffffffffffff");

+ 26 - 8
UdPunching.Common/ExchangeMessage.cs

@@ -13,14 +13,16 @@ namespace UdPunching
         // [3...N] Section Content
         // [N+1..] Next Section Type
 
-        public ExchangeMessageFlags Flags { get; set; }
+        public ExchangeMessageId Id { get; set; }
 
-        public DateTime? PeerTimeStamp { get; set; }
+        public DateTime? TimeStamp { get; set; }
 
         public IPEndPoint PeerEndPoint { get; set; }
 
         public Guid? PeerId { get; set; }
 
+        public byte[] PayloadBytes { get; set; }
+
         //TODO: more properties
 
         public ExchangeMessage()
@@ -30,7 +32,7 @@ namespace UdPunching
         public ExchangeMessage(byte[] data)
         {
             var ptr = -1;
-            Flags = (ExchangeMessageFlags)data[++ptr];
+            Id = (ExchangeMessageId)data[++ptr];
 
             var count = data[++ptr];
             for (var i = 0; i < count; i++)
@@ -40,7 +42,7 @@ namespace UdPunching
                 {
                     case ExchangeMessageSection.TimeStamp:
                         ptr += data.ReadUnixTimeStamp(++ptr, out var timeStamp);
-                        PeerTimeStamp = timeStamp;
+                        TimeStamp = timeStamp;
                         break;
 
                     case ExchangeMessageSection.PeerEndPoint:
@@ -53,6 +55,14 @@ namespace UdPunching
                         ptr += 16;
                         break;
 
+                    case ExchangeMessageSection.PayloadData:
+                        var len = data[++ptr];
+                        var payload = new byte[len];
+                        Buffer.BlockCopy(data, ++ptr, payload, 0, len);
+                        ptr += len;
+                        PayloadBytes = payload;
+                        break;
+
                     default:
                         //TODO: handle more sections
                         throw new ArgumentOutOfRangeException();
@@ -64,9 +74,9 @@ namespace UdPunching
         {
             var dic = new Dictionary<ExchangeMessageSection, byte[]>();
 
-            if (PeerTimeStamp.HasValue)
+            if (TimeStamp.HasValue)
             {
-                dic[ExchangeMessageSection.TimeStamp] = PeerTimeStamp.Value.ToUnixTimeStamp().ToLeBytes();
+                dic[ExchangeMessageSection.TimeStamp] = TimeStamp.Value.ToUnixTimeStamp().ToLeBytes();
             }
 
             if (null != PeerEndPoint)
@@ -82,6 +92,14 @@ namespace UdPunching
                 dic[ExchangeMessageSection.PeerId] = PeerId.Value.ToByteArray();
             }
 
+            if (null != PayloadBytes)
+            {
+                var buf = new byte[PayloadBytes.Length + 1];
+                buf[0] = (byte)PayloadBytes.Length;
+                PayloadBytes.CopyTo(buf, 1);
+                dic[ExchangeMessageSection.PeerId] = buf;
+            }
+
             //TODO: add more sections from properties
 
             return dic;
@@ -93,7 +111,7 @@ namespace UdPunching
 
             var lst = new List<byte>(1 + 1 + dic.Count + dic.Sum(p => p.Value.Length))
             {
-                (byte)Flags,
+                (byte)Id,
                 (byte)dic.Count
             };
 
@@ -111,7 +129,7 @@ namespace UdPunching
             var dic = CreateSectionDictionary();
 
             var ptr = -1;
-            buffer[++ptr] = (byte)Flags;
+            buffer[++ptr] = (byte)Id;
             buffer[++ptr] = (byte)dic.Count;
 
             foreach (var item in dic)

+ 0 - 22
UdPunching.Common/ExchangeMessageFlags.cs

@@ -1,22 +0,0 @@
-using System;
-
-namespace UdPunching
-{
-    [Flags]
-    public enum ExchangeMessageFlags : byte
-    {
-        ServerSessionCreateNew = 1,
-
-        PeerExchangeRequest = 1 << 1,
-        PeerExchangeRequestAccepted = 1 << 2,
-        PeerExchangeRequestDenied = 1 << 3,
-
-        PeerKeepAlive = 1 << 4,
-
-        EchoEndPoint = 1 << 5,
-
-        PeerNoRegistered = 1 << 6,
-
-        Error = 1 << 7
-    }
-}

+ 27 - 0
UdPunching.Common/ExchangeMessageId.cs

@@ -0,0 +1,27 @@
+namespace UdPunching
+{
+    public enum ExchangeMessageId : byte
+    {
+        // 0 is invalid
+
+        KeepAliveReq = 10,
+        KeepAliveAckSessionCreated = 11,
+        KeepAliveAckNoChg = 12,
+
+        PeerKnockReq = 60,
+        PeerKnockReqErrPeerNoReady = 61,
+        PeerKnockReqRelay = 62,
+        PeerKnockReqAck = 63,
+        PeerKnockReqAckRelay = 64,
+        PeerKnockReqDenied = 65,
+        PeerKnockReqDeniedRelay = 66,
+
+        PeerKnockConnectionReq = 100,
+        PeerKnockConnectionAck = 110,
+
+        ErrSessionNoCreated = 200,
+        ErrTimeStamp = 210,
+
+        DataTransfer = 233,
+    }
+}

+ 5 - 0
UdPunching.Common/ExchangeMessageSection.cs

@@ -16,5 +16,10 @@
         /// 16 bytes: just bytes of GUID
         /// </summary>
         PeerId = 2,
+
+        /// <summary>
+        /// N+1 byte length prefix data
+        /// </summary>
+        PayloadData=3,
     }
 }

+ 2 - 2
UdPunching.Common/UdPunching.Common.csproj

@@ -41,8 +41,8 @@
     <Reference Include="System.Xml" />
   </ItemGroup>
   <ItemGroup>
-    <Compile Include="BuindInPeerId.cs" />
-    <Compile Include="ExchangeMessageFlags.cs" />
+    <Compile Include="BuildInPeerId.cs" />
+    <Compile Include="ExchangeMessageId.cs" />
     <Compile Include="ExchangeMessage.cs" />
     <Compile Include="ExchangeMessageSection.cs" />
     <Compile Include="TransferCodec.cs" />

+ 60 - 39
UdPunching.ExampleW/ExampleForm.Designer.cs

@@ -35,18 +35,19 @@
             this.PublicEndPointTextBox = new System.Windows.Forms.TextBox();
             this.KeepAliveTimer = new System.Windows.Forms.Timer(this.components);
             this.SendButton = new System.Windows.Forms.Button();
-            this.SendToEndPointTextBox = new System.Windows.Forms.TextBox();
             this.SendContentTextBox = new System.Windows.Forms.TextBox();
             this.RecvTextBox = new System.Windows.Forms.TextBox();
             this.PeerKetyDropDown = new System.Windows.Forms.ComboBox();
-            this.StatusLabel = new System.Windows.Forms.Label();
+            this.PeerToKonckDropDown = new System.Windows.Forms.ComboBox();
+            this.KnockButton = new System.Windows.Forms.Button();
+            this.SendToEndPointTextBox = new System.Windows.Forms.TextBox();
             this.SuspendLayout();
             // 
             // StartButton
             // 
-            this.StartButton.Location = new System.Drawing.Point(155, 44);
+            this.StartButton.Location = new System.Drawing.Point(266, 12);
             this.StartButton.Name = "StartButton";
-            this.StartButton.Size = new System.Drawing.Size(75, 23);
+            this.StartButton.Size = new System.Drawing.Size(64, 23);
             this.StartButton.TabIndex = 0;
             this.StartButton.Text = "Start";
             this.StartButton.UseVisualStyleBackColor = true;
@@ -54,18 +55,18 @@
             // 
             // ServerIEndPointTextBox
             // 
-            this.ServerIEndPointTextBox.Location = new System.Drawing.Point(12, 44);
+            this.ServerIEndPointTextBox.Location = new System.Drawing.Point(12, 38);
             this.ServerIEndPointTextBox.Name = "ServerIEndPointTextBox";
-            this.ServerIEndPointTextBox.Size = new System.Drawing.Size(137, 21);
+            this.ServerIEndPointTextBox.Size = new System.Drawing.Size(107, 21);
             this.ServerIEndPointTextBox.TabIndex = 1;
-            this.ServerIEndPointTextBox.Text = "127.111.111.111:23330";
+            this.ServerIEndPointTextBox.Text = "127.0.0.1:23330";
             // 
             // StopButton
             // 
             this.StopButton.Enabled = false;
-            this.StopButton.Location = new System.Drawing.Point(236, 44);
+            this.StopButton.Location = new System.Drawing.Point(266, 41);
             this.StopButton.Name = "StopButton";
-            this.StopButton.Size = new System.Drawing.Size(75, 23);
+            this.StopButton.Size = new System.Drawing.Size(64, 23);
             this.StopButton.TabIndex = 0;
             this.StopButton.Text = "Stop";
             this.StopButton.UseVisualStyleBackColor = true;
@@ -73,51 +74,49 @@
             // 
             // PublicEndPointTextBox
             // 
-            this.PublicEndPointTextBox.Location = new System.Drawing.Point(317, 46);
+            this.PublicEndPointTextBox.Location = new System.Drawing.Point(125, 38);
             this.PublicEndPointTextBox.Name = "PublicEndPointTextBox";
             this.PublicEndPointTextBox.ReadOnly = true;
-            this.PublicEndPointTextBox.Size = new System.Drawing.Size(183, 21);
+            this.PublicEndPointTextBox.Size = new System.Drawing.Size(132, 21);
             this.PublicEndPointTextBox.TabIndex = 3;
             this.PublicEndPointTextBox.Text = "???.???.???.???:?????";
             // 
             // KeepAliveTimer
             // 
-            this.KeepAliveTimer.Interval = 1000;
+            this.KeepAliveTimer.Interval = 5000;
             this.KeepAliveTimer.Tick += new System.EventHandler(this.KeepAliveTimer_Tick);
             // 
             // SendButton
             // 
             this.SendButton.Enabled = false;
-            this.SendButton.Location = new System.Drawing.Point(155, 71);
+            this.SendButton.Location = new System.Drawing.Point(266, 99);
             this.SendButton.Name = "SendButton";
-            this.SendButton.Size = new System.Drawing.Size(75, 23);
+            this.SendButton.Size = new System.Drawing.Size(64, 23);
             this.SendButton.TabIndex = 0;
             this.SendButton.Text = "Send";
             this.SendButton.UseVisualStyleBackColor = true;
             this.SendButton.Click += new System.EventHandler(this.SendButton_Click);
             // 
-            // SendToEndPointTextBox
-            // 
-            this.SendToEndPointTextBox.Location = new System.Drawing.Point(12, 71);
-            this.SendToEndPointTextBox.Name = "SendToEndPointTextBox";
-            this.SendToEndPointTextBox.Size = new System.Drawing.Size(137, 21);
-            this.SendToEndPointTextBox.TabIndex = 1;
-            this.SendToEndPointTextBox.Text = "???.???.???.???:00000";
-            // 
             // SendContentTextBox
             // 
-            this.SendContentTextBox.Location = new System.Drawing.Point(12, 100);
+            this.SendContentTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left)));
+            this.SendContentTextBox.Location = new System.Drawing.Point(12, 126);
             this.SendContentTextBox.Multiline = true;
             this.SendContentTextBox.Name = "SendContentTextBox";
-            this.SendContentTextBox.Size = new System.Drawing.Size(241, 130);
+            this.SendContentTextBox.Size = new System.Drawing.Size(318, 193);
             this.SendContentTextBox.TabIndex = 4;
+            this.SendContentTextBox.Text = "00000000-0000-0000-0000-000000000000";
             // 
             // RecvTextBox
             // 
-            this.RecvTextBox.Location = new System.Drawing.Point(259, 100);
+            this.RecvTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
+            | System.Windows.Forms.AnchorStyles.Left) 
+            | System.Windows.Forms.AnchorStyles.Right)));
+            this.RecvTextBox.Location = new System.Drawing.Point(336, 12);
             this.RecvTextBox.Multiline = true;
             this.RecvTextBox.Name = "RecvTextBox";
-            this.RecvTextBox.Size = new System.Drawing.Size(241, 130);
+            this.RecvTextBox.Size = new System.Drawing.Size(431, 307);
             this.RecvTextBox.TabIndex = 4;
             // 
             // PeerKetyDropDown
@@ -126,31 +125,52 @@
             this.PeerKetyDropDown.FormattingEnabled = true;
             this.PeerKetyDropDown.Location = new System.Drawing.Point(12, 12);
             this.PeerKetyDropDown.Name = "PeerKetyDropDown";
-            this.PeerKetyDropDown.Size = new System.Drawing.Size(488, 20);
+            this.PeerKetyDropDown.Size = new System.Drawing.Size(248, 20);
             this.PeerKetyDropDown.TabIndex = 5;
             // 
-            // StatusLabel
+            // PeerToKonckDropDown
+            // 
+            this.PeerToKonckDropDown.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+            this.PeerToKonckDropDown.FormattingEnabled = true;
+            this.PeerToKonckDropDown.Location = new System.Drawing.Point(12, 70);
+            this.PeerToKonckDropDown.Name = "PeerToKonckDropDown";
+            this.PeerToKonckDropDown.Size = new System.Drawing.Size(248, 20);
+            this.PeerToKonckDropDown.TabIndex = 5;
+            // 
+            // KnockButton
             // 
-            this.StatusLabel.AutoSize = true;
-            this.StatusLabel.Location = new System.Drawing.Point(293, 76);
-            this.StatusLabel.Name = "StatusLabel";
-            this.StatusLabel.Size = new System.Drawing.Size(41, 12);
-            this.StatusLabel.TabIndex = 6;
-            this.StatusLabel.Text = "Ready.";
+            this.KnockButton.Enabled = false;
+            this.KnockButton.Location = new System.Drawing.Point(266, 70);
+            this.KnockButton.Name = "KnockButton";
+            this.KnockButton.Size = new System.Drawing.Size(64, 23);
+            this.KnockButton.TabIndex = 0;
+            this.KnockButton.Text = "Knock";
+            this.KnockButton.UseVisualStyleBackColor = true;
+            this.KnockButton.Click += new System.EventHandler(this.KnockButton_Click);
+            // 
+            // SendToEndPointTextBox
+            // 
+            this.SendToEndPointTextBox.Location = new System.Drawing.Point(12, 99);
+            this.SendToEndPointTextBox.Name = "SendToEndPointTextBox";
+            this.SendToEndPointTextBox.ReadOnly = true;
+            this.SendToEndPointTextBox.Size = new System.Drawing.Size(248, 21);
+            this.SendToEndPointTextBox.TabIndex = 3;
+            this.SendToEndPointTextBox.Text = "???.???.???.???:?????";
             // 
             // ExampleForm
             // 
             this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
             this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
-            this.ClientSize = new System.Drawing.Size(520, 250);
-            this.Controls.Add(this.StatusLabel);
+            this.ClientSize = new System.Drawing.Size(779, 331);
+            this.Controls.Add(this.PeerToKonckDropDown);
             this.Controls.Add(this.PeerKetyDropDown);
             this.Controls.Add(this.RecvTextBox);
             this.Controls.Add(this.SendContentTextBox);
-            this.Controls.Add(this.PublicEndPointTextBox);
             this.Controls.Add(this.SendToEndPointTextBox);
+            this.Controls.Add(this.PublicEndPointTextBox);
             this.Controls.Add(this.ServerIEndPointTextBox);
             this.Controls.Add(this.StopButton);
+            this.Controls.Add(this.KnockButton);
             this.Controls.Add(this.SendButton);
             this.Controls.Add(this.StartButton);
             this.Name = "ExampleForm";
@@ -169,11 +189,12 @@
         private System.Windows.Forms.TextBox PublicEndPointTextBox;
         private System.Windows.Forms.Timer KeepAliveTimer;
         private System.Windows.Forms.Button SendButton;
-        private System.Windows.Forms.TextBox SendToEndPointTextBox;
         private System.Windows.Forms.TextBox SendContentTextBox;
         private System.Windows.Forms.TextBox RecvTextBox;
         private System.Windows.Forms.ComboBox PeerKetyDropDown;
-        private System.Windows.Forms.Label StatusLabel;
+        private System.Windows.Forms.ComboBox PeerToKonckDropDown;
+        private System.Windows.Forms.Button KnockButton;
+        private System.Windows.Forms.TextBox SendToEndPointTextBox;
     }
 }
 

+ 132 - 69
UdPunching.ExampleW/ExampleForm.cs

@@ -19,159 +19,222 @@ namespace UdPunching.ExampleW
         private RSACryptoServiceProvider _serverRsa;
         private SocketAsyncEventArgs _saeRecv;
 
-        private Guid _peerId;
-        private RSACryptoServiceProvider _peerRsa;
+        private Guid _localId;
+        private RSACryptoServiceProvider _localRsa;
 
-        private Socket _peerSocket;
+        private Socket _localSocket;
+        private IPEndPoint _publicEndPoint;
+        private IPEndPoint _peerEndPoint;
+
+        private readonly byte[] _keepAliveBuf = new byte[7];// 1flag,1count,1section,4timestamp
+        private readonly ExchangeMessage _keepAliveMsg = new ExchangeMessage { Id = ExchangeMessageId.KeepAliveReq };
+
+        //------------- ctor -------------
 
         public ExampleForm()
         {
             InitializeComponent();
+            //------------- ui event -------------
         }
 
+        //------------- ui event -------------
+
         private void ExampleForm_Shown(object sender, EventArgs e)
         {
             _serverRsa = new RSACryptoServiceProvider();
             _serverRsa.FromXmlString(File.ReadAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ServerPublicKey.txt")));
 
-            var files = Directory
+            var privateKeys = Directory
                 .GetFiles(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "PrivateKey"))
-                .Select(Path.GetFileName)
+                .Select(Path.GetFileNameWithoutExtension)
                 .ToArray();
 
-            PeerKetyDropDown.DataSource = files;
+            PeerKetyDropDown.DataSource = privateKeys;
+
+            var peerPubKeys = Directory
+                .GetFiles(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "PeerPublicKey"))
+                .Select(Path.GetFileNameWithoutExtension)
+                .ToArray();
+
+            PeerToKonckDropDown.DataSource = peerPubKeys;
         }
 
         private void StartButton_Click(object sender, EventArgs e)
         {
+            Log("Starting...");
+
             _serverEndPoint = ServerIEndPointTextBox.Text.ParseToIpEndPointV4();
 
-            _peerRsa = new RSACryptoServiceProvider();
-            var peerPrivateKeyPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "PrivateKey", PeerKetyDropDown.Text);
-            _peerRsa.FromXmlString(
+            _localId = new Guid(PeerKetyDropDown.Text);
+            _localRsa = new RSACryptoServiceProvider();
+            var peerPrivateKeyPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "PrivateKey", PeerKetyDropDown.Text + ".txt");
+            _localRsa.FromXmlString(
                 File.ReadAllText(
                     peerPrivateKeyPath
                 )
             );
-            _peerId = new Guid(Path.GetFileNameWithoutExtension(peerPrivateKeyPath));
 
-            _peerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
-            _peerSocket.Bind(AnyEndPoint);
+            _localSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
+            _localSocket.Bind(AnyEndPoint);
 
             _saeRecv = new SocketAsyncEventArgs();
             _saeRecv.SetBuffer(new byte[ReceiveBufferSize], 0, ReceiveBufferSize);
-            _saeRecv.Completed += _saeRecv_Completed;
+            _saeRecv.Completed += RecvCompleted;
             BeginRecv();
 
             KeepAliveTimer.Start();
+            KeepAliveTimer_Tick(null, null);
 
+            PeerKetyDropDown.Enabled = false;
             StartButton.Enabled = false;
             StopButton.Enabled = true;
-            SendButton.Enabled = true;
+            KnockButton.Enabled = true;
+
+            Log("Started...");
         }
 
         private void StopButton_Click(object sender, EventArgs e)
         {
+            Log("Stopping...");
+
             KeepAliveTimer.Stop();
-            _peerSocket.Dispose();
-            _saeRecv.Dispose();
-            _peerRsa.Dispose();
 
+            _localSocket?.Dispose();
+            _saeRecv?.Dispose();
+            _localRsa?.Dispose();
+
+            _localSocket = null;
+            _saeRecv = null;
+            _localRsa = null;
+
+            PeerKetyDropDown.Enabled = true;
             StartButton.Enabled = true;
             StopButton.Enabled = false;
+            KnockButton.Enabled = false;
             SendButton.Enabled = false;
+
+            Log("Stopped");
         }
 
-        private void _saeRecv_Completed(object sender, SocketAsyncEventArgs e)
+        private void KeepAliveTimer_Tick(object sender, EventArgs e)
         {
-            if (e.SocketError == SocketError.Success)
+            _keepAliveMsg.TimeStamp = DateTime.Now;
+            _keepAliveMsg.WriteToBuffer(_keepAliveBuf);
+            var encode = TransferCodec.Encode(_serverRsa, _localId, _keepAliveBuf);
+            _localSocket.SendTo(encode, _serverEndPoint);
+        }
+
+        private void KnockButton_Click(object sender, EventArgs e)
+        {
+            //TODO:
+            SendButton.Enabled = true;
+        }
+
+        private void SendButton_Click(object sender, EventArgs e)
+        {
+            //TODO: use datatransfer message
+            var to = SendToEndPointTextBox.Text.ParseToIpEndPointV4();
+            var sent = _localSocket.SendTo(Encoding.UTF8.GetBytes(SendContentTextBox.Text), to);
+        }
+
+        //------------- logic -------------
+
+        private void ProcessPacket()
+        {
+            var peerId = TransferCodec.ReadId(_saeRecv.Buffer);
+            if (_saeRecv.RemoteEndPoint.IpEndPointEqualsTo(_serverEndPoint))
             {
-                try
+                if (BuildInPeerId.Invalid == peerId)
                 {
-                    ProcessPacket();
-                    if (false == _peerSocket.ReceiveFromAsync(_saeRecv)) _saeRecv_Completed(null, null);
+                    Log("ERROR SERVER FAILURE");
                 }
-                catch (Exception exception)
+                else if (Guid.Empty == peerId)
                 {
-                    Invoke(new Action(() =>
+                    var msgData = TransferCodec.DecodeData(_localRsa, _saeRecv.Buffer);
+
+                    var msg = new ExchangeMessage(msgData);
+
+                    switch (msg.Id)
                     {
-                        StatusLabel.Text = $"ERROR:{exception.Message}";
-                        StopButton_Click(null, null);
-                    }));
+                        case ExchangeMessageId.KeepAliveAckSessionCreated:
+                            _publicEndPoint = msg.PeerEndPoint;
+                            Log($"Session Created, public endpoint {_publicEndPoint}");
+                            Invoke(new Action(() =>
+                            {
+                                PublicEndPointTextBox.Text = msg.PeerEndPoint.ToString();
+                            }));
+                            break;
+
+                        case ExchangeMessageId.KeepAliveAckNoChg:
+                            break;
+
+                        default:
+                            throw new ArgumentOutOfRangeException();
+                    }
                 }
             }
             else
             {
+                if (BuildInPeerId.Invalid == peerId || BuildInPeerId.Server == peerId)
+                {
+                    return; //Ignore invalid message
+                }
+
                 Invoke(new Action(() =>
                 {
-                    StatusLabel.Text = $"ERROR:{e.SocketError}";
-                    StopButton_Click(null, null);
+                    Log($"RECV FROM {_saeRecv.RemoteEndPoint},{Encoding.UTF8.GetString(_saeRecv.Buffer, 0, _saeRecv.BytesTransferred)}");
                 }));
             }
         }
 
-        private void ProcessPacket()
+        private void RecvCompleted(object sender, SocketAsyncEventArgs e)
         {
-            var peerId = TransferCodec.ReadId(_saeRecv.Buffer);
-            if (_saeRecv.RemoteEndPoint.IpEndPointEqualsTo(_serverEndPoint))
+            if (e.SocketError == SocketError.Success)
             {
-                if (BuindInPeerId.Invalid == peerId)
+                try
                 {
-                    Invoke(new Action(() => { StatusLabel.Text = "Server FAILURE"; }));
+                    ProcessPacket();
                 }
-                else if (Guid.Empty == peerId)
+                catch (Exception exception)
                 {
-                    var msgData = TransferCodec.DecodeData(_peerRsa, _saeRecv.Buffer);
-
-                    var msg = new ExchangeMessage(msgData);
-
-                    if (msg.Flags.HasFlag(ExchangeMessageFlags.EchoEndPoint))
-                    {
-                        Invoke(new Action(() =>
-                        {
-                            PublicEndPointTextBox.Text = msg.PeerEndPoint.ToString();
-                        }));
-                    }
+                    Log($"ERROR ProcessPacket:{exception}");
+                    Invoke(new Action(() => { StopButton_Click(null, null); }));
                 }
             }
             else
             {
-                Invoke(new Action(() =>
-                {
-                    RecvTextBox.Text =
-                        $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} {_saeRecv.RemoteEndPoint}"
-                        + $"{Environment.NewLine} {Encoding.UTF8.GetString(_saeRecv.Buffer, 0, _saeRecv.BytesTransferred)}"
-                        + $"{Environment.NewLine}"
-                        + RecvTextBox.Text;
-                }));
+                Log($"ERROR SOCKET:{e.SocketError}");
             }
+
+            if (null == _localSocket) return;
+            if (false == _localSocket.ReceiveFromAsync(_saeRecv)) RecvCompleted(null, null);
         }
 
         private void BeginRecv()
         {
             _saeRecv.RemoteEndPoint = AnyEndPoint;
-            if (false == _peerSocket.ReceiveFromAsync(_saeRecv))
+            if (false == _localSocket.ReceiveFromAsync(_saeRecv))
             {
-                _saeRecv_Completed(null, _saeRecv);
+                RecvCompleted(null, _saeRecv);
             }
         }
 
-        private readonly byte[] _buf = new byte[7];// 1flag,1count,1section,4timestamp
-        private readonly ExchangeMessage _msg = new ExchangeMessage { Flags = ExchangeMessageFlags.PeerKeepAlive };
+        //------------- util -------------
 
-        private void KeepAliveTimer_Tick(object sender, EventArgs e)
+        private void Log(string content)
         {
-            _msg.PeerTimeStamp = DateTime.Now;
-            _msg.WriteToBuffer(_buf);
-            StatusLabel.Text = "Keeping alive...";
-            var encode = TransferCodec.Encode(_serverRsa, _peerId, _buf);
-            _peerSocket.SendTo(encode, _serverEndPoint);
-        }
+            if (InvokeRequired)
+            {
+                Invoke(new Action<string>(Log), content);
+                return;
+            }
 
-        private void SendButton_Click(object sender, EventArgs e)
-        {
-            var to = SendToEndPointTextBox.Text.ParseToIpEndPointV4();
-            var sent = _peerSocket.SendTo(Encoding.UTF8.GetBytes(SendContentTextBox.Text), to);
+            RecvTextBox.Text =
+                $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} {content}"
+                + $"{Environment.NewLine}"
+                + RecvTextBox.Text;
+
+            RecvTextBox.Refresh();
         }
     }
 }

UdPunching.Serv/Peers/26531b6b-1fa1-4c70-8a1e-7c1b579742a5.txt → UdPunching.Serv/PeerPublicKey/26531b6b-1fa1-4c70-8a1e-7c1b579742a5.txt


UdPunching.Serv/Peers/ab9ae069-1bed-4fce-8e6a-feabb8c519a7.txt → UdPunching.Serv/PeerPublicKey/ab9ae069-1bed-4fce-8e6a-feabb8c519a7.txt


+ 96 - 72
UdPunching.Serv/ServProgram.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Collections.Concurrent;
+using System.Collections.Generic;
 using System.Diagnostics;
 using System.IO;
 using System.Linq;
@@ -12,11 +13,12 @@ namespace UdPunching.Serv
 {
     internal static class ServProgram
     {
-        private static RSACryptoServiceProvider _serverRsaCryptoServiceProvider;
-
         private static readonly ConcurrentDictionary<Guid, OnlineSession> OnlineSessions = new ConcurrentDictionary<Guid, OnlineSession>();
 
-        private static int packetSeq;
+        private static RSACryptoServiceProvider _serverRsaCryptoServiceProvider;
+        private static IReadOnlyDictionary<Guid, RSACryptoServiceProvider> _peerRsaDict;
+
+        private static volatile int _packetSeq;
         private static Socket _svrSocket;
 
         private static void ProcessPacket(SocketAsyncEventArgs sae)
@@ -25,92 +27,105 @@ namespace UdPunching.Serv
             //Logout       --------  remove from list
             //Knock        --------  PeerID
 
-            ++packetSeq;
-
-            Console.WriteLine($"Incoming packet#{packetSeq:0,000,000} from {sae.RemoteEndPoint}, len {sae.BytesTransferred}");
+            ++_packetSeq;
 
             var peerId = TransferCodec.ReadId(sae.Buffer);
-            Console.WriteLine($"Incoming packet#{packetSeq:0,000,000} peer id {peerId}");
+            _peerRsaDict.TryGetValue(peerId, out var peerRsa);
 
-            byte[] msgData = null;
-
-            bool ValidateRequest()
+            if (null == peerRsa)
             {
-                try
-                {
-                    msgData = TransferCodec.DecodeData(_serverRsaCryptoServiceProvider, sae.Buffer);
-                    Console.WriteLine($"Incoming packet#{packetSeq:0,000,000} MsgLength: {msgData.Length}");
-                    return true;
-                }
-                catch (Exception ex)
-                {
-                    Console.WriteLine(ex);
-                    return false;
-                }
+                Console.WriteLine($"pacet #{_packetSeq} peer id {peerId} no registered");
+                _svrSocket.SendTo(BuildInPeerId.Invalid.ToByteArray(), sae.RemoteEndPoint);
+                return;
             }
 
-            var rspMsg = new ExchangeMessage();
+            ExchangeMessage requestMessage;
 
-            var isSessionCreateNew = false;
-            var isRequestValidated = false;
+            try
+            {
+                var msgData = TransferCodec.DecodeData(_serverRsaCryptoServiceProvider, sae.Buffer);
+                requestMessage = new ExchangeMessage(msgData);
+            }
+            catch (Exception exception)
+            {
+                Console.WriteLine($"pacet #{_packetSeq} decode fail:{exception}");
+                _svrSocket.SendTo(BuildInPeerId.Invalid.ToByteArray(), sae.RemoteEndPoint);
+                return;
+            }
+
+            var responseMessage = new ExchangeMessage();
 
-            if (OnlineSessions.TryGetValue(peerId, out var session)
-                && session.RemoteEndPoint.IpEndPointEqualsTo(sae.RemoteEndPoint))
+            if (false == requestMessage.TimeStamp.HasValue || Math.Abs((DateTime.Now - requestMessage.TimeStamp.Value).TotalSeconds) > 10)
             {
-                session.Actively();
-                isRequestValidated = ValidateRequest();
+                responseMessage.Id = ExchangeMessageId.ErrTimeStamp;
             }
             else
             {
-                var peerRsa = LoadPeerRsa(peerId);
-                if (null == peerRsa)
-                {
-                    Console.WriteLine($"Incoming packet#{packetSeq:0,000,000} peer NOT FOUND");
-                    rspMsg.Flags = ExchangeMessageFlags.PeerNoRegistered;
-                }
-                else
+                OnlineSessions.TryGetValue(peerId, out var session);
+
+                if (null == session)
                 {
-                    isRequestValidated = ValidateRequest();
-                    if (isRequestValidated)
+                    if (ExchangeMessageId.KeepAliveReq == requestMessage.Id)
                     {
-                        session = new OnlineSession(peerId, sae.RemoteEndPoint, peerRsa);
+                        session = new OnlineSession(peerId, sae.RemoteEndPoint);
                         OnlineSessions[peerId] = session;
-                        isSessionCreateNew = true;
+                        responseMessage.Id = ExchangeMessageId.KeepAliveAckSessionCreated;
+                        responseMessage.PeerEndPoint = (IPEndPoint)sae.RemoteEndPoint;
+                        Console.WriteLine($"Session created: {peerId} from {sae.RemoteEndPoint}");
+                    }
+                    else
+                    {
+                        responseMessage.Id = ExchangeMessageId.ErrSessionNoCreated;
                     }
                 }
-            }
-
-            if (isRequestValidated)
-            {
-                var reqMsg = new ExchangeMessage(msgData);
-                Console.WriteLine($"Incoming packet#{packetSeq:0,000,000} Flags: {reqMsg.Flags}");
-
-                if (isSessionCreateNew) rspMsg.Flags |= ExchangeMessageFlags.ServerSessionCreateNew;
-                if (reqMsg.Flags.HasFlag(ExchangeMessageFlags.PeerKeepAlive))
+                else
                 {
-                    rspMsg.Flags |= ExchangeMessageFlags.EchoEndPoint;
-                    rspMsg.PeerEndPoint = (IPEndPoint)sae.RemoteEndPoint;
-                }
+                    session.Actively();
 
-                //TODO: handle request
-                // Exchange request
-                // Exchange relay
-                var replyBytes = TransferCodec.Encode(session.PeerRsa, BuindInPeerId.Server, rspMsg.ToBytes());
-                _svrSocket.SendTo(replyBytes, sae.RemoteEndPoint);
-            }
-            else
-            {
-                _svrSocket.SendTo(BuindInPeerId.Invalid.ToByteArray(), sae.RemoteEndPoint);
+                    switch (requestMessage.Id)
+                    {
+                        case ExchangeMessageId.KeepAliveReq:
+                            if (false == session.RemoteEndPoint.IpEndPointEqualsTo(sae.RemoteEndPoint))
+                            {
+                                session = new OnlineSession(peerId, sae.RemoteEndPoint);
+                                OnlineSessions[peerId] = session;
+                                responseMessage.Id = ExchangeMessageId.KeepAliveAckSessionCreated;
+                                responseMessage.PeerEndPoint = (IPEndPoint)sae.RemoteEndPoint;
+                                Console.WriteLine($"Session created: {peerId} from {sae.RemoteEndPoint}");
+                            }
+                            else
+                            {
+                                responseMessage.Id = ExchangeMessageId.KeepAliveAckNoChg;
+                            }
+                            break;
+
+                        //case ExchangeMessageId.PeerKnockReq:
+                        //    break;
+
+                        //case ExchangeMessageId.PeerKnockReqAck:
+                        //    break;
+
+                        //case ExchangeMessageId.PeerKnockReqDenied:
+                        //    break;
+
+                        //case ExchangeMessageId.PeerKnockConnectionReq:
+                        //    break;
+
+                        //case ExchangeMessageId.PeerKnockConnectionAck:
+                        //    break;
+
+                        //case ExchangeMessageId.ErrSessionNoCreated:
+                        //    break;
+
+                        default:
+                            throw new ArgumentOutOfRangeException();
+                    }
+                }
             }
-        }
 
-        private static RSACryptoServiceProvider LoadPeerRsa(Guid id)
-        {
-            var peerKeyPath = Path.Combine("Peers", $"{id}.txt");
-            if (false == File.Exists(peerKeyPath)) return null;
-            var peerRsa = new RSACryptoServiceProvider();
-            peerRsa.FromXmlString(File.ReadAllText(peerKeyPath));
-            return peerRsa;
+            responseMessage.TimeStamp = DateTime.Now;
+            var replyBytes = TransferCodec.Encode(peerRsa, BuildInPeerId.Server, responseMessage.ToBytes());
+            _svrSocket.SendTo(replyBytes, sae.RemoteEndPoint);
         }
 
         private static void DeadLoopTimeOutKiller()
@@ -139,13 +154,11 @@ namespace UdPunching.Serv
             public Guid Id { get; }
             public DateTime LastActively { get; private set; }
             public EndPoint RemoteEndPoint { get; }
-            public RSACryptoServiceProvider PeerRsa { get; }
 
-            public OnlineSession(Guid id, EndPoint remoteEndPoint, RSACryptoServiceProvider peerRsa)
+            public OnlineSession(Guid id, EndPoint remoteEndPoint)
             {
                 Id = id;
                 RemoteEndPoint = remoteEndPoint;
-                PeerRsa = peerRsa;
                 Actively();
             }
 
@@ -159,6 +172,17 @@ namespace UdPunching.Serv
             _serverRsaCryptoServiceProvider = new RSACryptoServiceProvider();
             _serverRsaCryptoServiceProvider.FromXmlString(File.ReadAllText("ServerPrivateKey.txt"));
 
+            _peerRsaDict = Directory.GetFiles(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "PeerPublicKey"))
+                .ToDictionary(
+                    s => new Guid(Path.GetFileNameWithoutExtension(s)),
+                    p =>
+                    {
+                        var rsa = new RSACryptoServiceProvider();
+                        rsa.FromXmlString(File.ReadAllText(p));
+                        return rsa;
+                    }
+                );
+
             _svrSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
             _svrSocket.Bind(new IPEndPoint(IPAddress.Any, Properties.Settings.Default.ListenPort));
             Console.WriteLine($"Server Bind on {_svrSocket.LocalEndPoint}");
@@ -173,7 +197,7 @@ namespace UdPunching.Serv
 
             void SaeOnCompleted(object sender, SocketAsyncEventArgs e)
             {
-                if (e.SocketError == SocketError.Success)
+                if (sae.SocketError == SocketError.Success)
                 {
                     try
                     {

+ 2 - 2
UdPunching.Serv/UdPunching.Serv.csproj

@@ -72,10 +72,10 @@
     </ProjectReference>
   </ItemGroup>
   <ItemGroup>
-    <Content Include="Peers\26531b6b-1fa1-4c70-8a1e-7c1b579742a5.txt">
+    <Content Include="PeerPublicKey\26531b6b-1fa1-4c70-8a1e-7c1b579742a5.txt">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
-    <Content Include="Peers\ab9ae069-1bed-4fce-8e6a-feabb8c519a7.txt">
+    <Content Include="PeerPublicKey\ab9ae069-1bed-4fce-8e6a-feabb8c519a7.txt">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
     <Content Include="ServerPublicKey.txt" />