123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275 |
- using System;
- using System.Collections.Concurrent;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.IO;
- using System.Linq;
- using System.Net;
- using System.Net.Sockets;
- using System.Security.Cryptography;
- using System.Threading;
- namespace UdPunching.Serv
- {
- internal static class ServProgram
- {
- private static readonly ConcurrentDictionary<Guid, OnlineSession> OnlineSessions = new ConcurrentDictionary<Guid, OnlineSession>();
- private static RSACryptoServiceProvider _serverKey;
- private static IReadOnlyDictionary<Guid, RSACryptoServiceProvider> _peerKeyRegister;
- private static volatile int _packetSeq;
- private static Socket _svrSocket;
- private static void ProcessPacket(SocketAsyncEventArgs sae)
- {
- //KeepAlive 1s -------- add endpoint to list if no exist, return list
- //Logout -------- remove from list
- //Knock -------- PeerID
- ++_packetSeq;
- var peerId = TransferCodec.ReadId(sae.Buffer);
- _peerKeyRegister.TryGetValue(peerId, out var peerKey);
- if (null == peerKey)
- {
- Console.WriteLine($"pacet #{_packetSeq} peer id {peerId} no registered");
- _svrSocket.SendTo(BuildInPeerId.Invalid.ToByteArray(), sae.RemoteEndPoint);
- return;
- }
- ExchangeMessage requestMessage;
- try
- {
- var msgData = TransferCodec.DecodeData(_serverKey, 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 { TimeStamp = DateTime.Now };
- if (false == requestMessage.TimeStamp.HasValue || Math.Abs((DateTime.Now - requestMessage.TimeStamp.Value).TotalSeconds) > 10)
- {
- responseMessage.Id = ExchangeMessageId.ErrTimeStamp;
- }
- else
- {
- OnlineSessions.TryGetValue(peerId, out var session);
- if (null == session)
- {
- if (ExchangeMessageId.KeepAliveReq == requestMessage.Id)
- {
- session = new OnlineSession(peerId, sae.RemoteEndPoint);
- OnlineSessions[peerId] = session;
- responseMessage.Id = ExchangeMessageId.KeepAliveAckSessionCreated;
- responseMessage.PeerEndPoint = (IPEndPoint)sae.RemoteEndPoint;
- Console.WriteLine($"pacet #{_packetSeq} Session created: {peerId} from {sae.RemoteEndPoint}");
- }
- else
- {
- responseMessage.Id = ExchangeMessageId.ErrSessionNoCreated;
- }
- }
- else
- {
- session.Actively();
- 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($"pacet #{_packetSeq} Session created: {peerId} from {sae.RemoteEndPoint}");
- }
- else
- {
- responseMessage.Id = ExchangeMessageId.KeepAliveAckNoChg;
- }
- break;
- case ExchangeMessageId.PeerKnockReq:
- if (false == requestMessage.PeerId.HasValue
- || false == _peerKeyRegister.TryGetValue(requestMessage.PeerId.Value, out var knockKey)
- || false == OnlineSessions.TryGetValue(requestMessage.PeerId.Value, out var knockSession)
- )
- {
- Console.WriteLine($"pacet #{_packetSeq} bad knock request: peer id no available {requestMessage.PeerId} by {peerId}");
- responseMessage.Id = ExchangeMessageId.PeerKnockReqErrPeerNoAvailable;
- responseMessage.PeerId = requestMessage.PeerId;
- break;
- }
- Console.WriteLine($"pacet #{_packetSeq} knock to {requestMessage.PeerId} by {peerId}");
- var reqRelayMessage = new ExchangeMessage(ExchangeMessageId.PeerKnockReqRelay)
- {
- PeerId = peerId,
- PeerEndPoint = (IPEndPoint)sae.RemoteEndPoint,
- };
- var reqRelayBytes = TransferCodec.Encode(knockKey, BuildInPeerId.Server, reqRelayMessage.ToBytes());
- _svrSocket.SendTo(reqRelayBytes, knockSession.RemoteEndPoint);
- responseMessage.Id = ExchangeMessageId.PeerKnockReqRelayed;
- responseMessage.PeerId = requestMessage.PeerId;
- break;
- case ExchangeMessageId.PeerKnockAck:
- if (false == requestMessage.PeerId.HasValue
- || false == _peerKeyRegister.TryGetValue(requestMessage.PeerId.Value, out var knockAckKey)
- || false == OnlineSessions.TryGetValue(requestMessage.PeerId.Value, out var knockAckSession)
- )
- {
- Console.WriteLine($"pacet #{_packetSeq} bad knock ack: peer id no available {requestMessage.PeerId}");
- responseMessage.Id = ExchangeMessageId.PeerKnockReqErrPeerNoAvailable;
- responseMessage.PeerId = requestMessage.PeerId;
- break;
- }
- Console.WriteLine($"pacet #{_packetSeq} knock ack {requestMessage.PeerId} by {peerId}");
- var ackRelayMessage = new ExchangeMessage(ExchangeMessageId.PeerKnockAckRelay)
- {
- PeerId = peerId,
- PeerEndPoint = (IPEndPoint)sae.RemoteEndPoint,
- };
- _svrSocket.SendExchangeMessageTo(BuildInPeerId.Server, ackRelayMessage, knockAckSession.RemoteEndPoint, knockAckKey);
- responseMessage.Id = ExchangeMessageId.PeerKnockAckRelayed;
- responseMessage.PeerId = requestMessage.PeerId;
- break;
- case ExchangeMessageId.PeerKnockDenied:
- break;
- //case ExchangeMessageId.PeerKnockConnectionReq:
- // break;
- //case ExchangeMessageId.PeerKnockConnectionAck:
- // break;
- //case ExchangeMessageId.ErrSessionNoCreated:
- // break;
- default:
- throw new ArgumentOutOfRangeException();
- }
- }
- }
-
- _svrSocket.SendExchangeMessageTo(BuildInPeerId.Server, responseMessage, sae.RemoteEndPoint, peerKey);
- }
- private static void DeadLoopTimeOutKiller()
- {
- while (true)
- {
- //TimeOutKiller
- const int timeOutSeconds = 10;
- foreach (var session in OnlineSessions.Values.ToArray())
- {
- if ((DateTime.Now - session.LastActively).TotalSeconds > timeOutSeconds)
- {
- OnlineSessions.TryRemove(session.Id, out _);
- Console.WriteLine($"Session removed by TimeOutKiller: {session.Id}");
- }
- }
- Thread.Sleep(timeOutSeconds * 1000);
- }
- // ReSharper disable once FunctionNeverReturns
- }
- private class OnlineSession
- {
- public Guid Id { get; }
- public DateTime LastActively { get; private set; }
- public EndPoint RemoteEndPoint { get; }
- public OnlineSession(Guid id, EndPoint remoteEndPoint)
- {
- Id = id;
- RemoteEndPoint = remoteEndPoint;
- Actively();
- }
- public void Actively() => LastActively = DateTime.Now;
- }
- private static void Main()
- {
- Console.WriteLine("Init...");
- _serverKey = new RSACryptoServiceProvider();
- _serverKey.FromXmlString(File.ReadAllText("ServerPrivateKey.txt"));
- _peerKeyRegister = 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}");
- const int receiveBufferSize = 1500;
- var sae = new SocketAsyncEventArgs();
- sae.SetBuffer(new byte[receiveBufferSize], 0, receiveBufferSize);
- sae.RemoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
- Console.WriteLine($"Receive buffer:{receiveBufferSize}");
- void SaeOnCompleted(object sender, SocketAsyncEventArgs e)
- {
- if (sae.SocketError == SocketError.Success)
- {
- try
- {
- ProcessPacket(sae);
- }
- catch (Exception exception)
- {
- Console.WriteLine(exception);
- }
- }
- sae.RemoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
- if (false == _svrSocket.ReceiveFromAsync(sae)) SaeOnCompleted(null, null);
- }
- sae.Completed += SaeOnCompleted;
- if (false == _svrSocket.ReceiveFromAsync(sae))
- {
- SaeOnCompleted(null, null);
- }
- Console.WriteLine($"Main thread enter dead sleep loop... to exit, kill me, pid:{Process.GetCurrentProcess().Id}");
- DeadLoopTimeOutKiller();
- }
- }
- }
|