using System; using System.IO; using System.Linq; using System.Net; using System.Net.Sockets; using System.Security.Cryptography; using System.Text; using System.Windows.Forms; namespace UdPunching.ExampleW { public partial class ExampleForm : Form { private const int ReceiveBufferSize = 1500; private static readonly IPEndPoint AnyEndPoint = new IPEndPoint(IPAddress.Any, 0); private IPEndPoint _serverEndPoint; private RSACryptoServiceProvider _serverRsa; private SocketAsyncEventArgs _saeRecv; private Guid _localId; private RSACryptoServiceProvider _localRsa; 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 privateKeys = Directory .GetFiles(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "PrivateKey")) .Select(Path.GetFileNameWithoutExtension) .ToArray(); 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(); _localId = new Guid(PeerKetyDropDown.Text); _localRsa = new RSACryptoServiceProvider(); var peerPrivateKeyPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "PrivateKey", PeerKetyDropDown.Text + ".txt"); _localRsa.FromXmlString( File.ReadAllText( peerPrivateKeyPath ) ); _localSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); _localSocket.Bind(AnyEndPoint); _saeRecv = new SocketAsyncEventArgs(); _saeRecv.SetBuffer(new byte[ReceiveBufferSize], 0, ReceiveBufferSize); _saeRecv.Completed += RecvCompleted; BeginRecv(); KeepAliveTimer.Start(); KeepAliveTimer_Tick(null, null); PeerKetyDropDown.Enabled = false; StartButton.Enabled = false; StopButton.Enabled = true; KnockButton.Enabled = true; Log("Started..."); } private void StopButton_Click(object sender, EventArgs e) { Log("Stopping..."); KeepAliveTimer.Stop(); _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 KeepAliveTimer_Tick(object sender, EventArgs e) { _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)) { if (BuildInPeerId.Invalid == peerId) { Log("ERROR SERVER FAILURE"); } else if (Guid.Empty == peerId) { var msgData = TransferCodec.DecodeData(_localRsa, _saeRecv.Buffer); var msg = new ExchangeMessage(msgData); switch (msg.Id) { 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(() => { Log($"RECV FROM {_saeRecv.RemoteEndPoint},{Encoding.UTF8.GetString(_saeRecv.Buffer, 0, _saeRecv.BytesTransferred)}"); })); } } private void RecvCompleted(object sender, SocketAsyncEventArgs e) { if (e.SocketError == SocketError.Success) { try { ProcessPacket(); } catch (Exception exception) { Log($"ERROR ProcessPacket:{exception}"); Invoke(new Action(() => { StopButton_Click(null, null); })); } } else { 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 == _localSocket.ReceiveFromAsync(_saeRecv)) { RecvCompleted(null, _saeRecv); } } //------------- util ------------- private void Log(string content) { if (InvokeRequired) { Invoke(new Action(Log), content); return; } RecvTextBox.Text = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} {content}" + $"{Environment.NewLine}" + RecvTextBox.Text; RecvTextBox.Refresh(); } } }