Skip to content

Commit

Permalink
Use id instead of ip address to detect echo message
Browse files Browse the repository at this point in the history
  • Loading branch information
sontx committed Jan 17, 2021
1 parent f0b7db6 commit 76b3e3f
Showing 1 changed file with 173 additions and 165 deletions.
338 changes: 173 additions & 165 deletions Protocol/Station.cs
Original file line number Diff line number Diff line change
@@ -1,166 +1,174 @@
using DropZone.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace DropZone.Protocol
{
public class Station
{
private const string HeaderPrefix = "DROP_ZONE";

public string Name { get; }
private readonly UdpClient _client;
private readonly ThreadWrapper _thread;
private readonly Dictionary<string, Neighbor> _neighbors = new Dictionary<string, Neighbor>();
private readonly Timer _timer;

public Action OnNeighborsChanged { get; set; }

public List<Neighbor> Neighbors
{
get
{
lock (this)
{
return _neighbors.Values.ToList();
}
}
}

public Station(string name)
{
Name = name;
_client = new UdpClient();
_thread = new ThreadWrapper
{
DoWork = ScanNeighbors
};
_timer = new Timer(UpdateNeighborsStatus);
}

public Neighbor GetNeighbor(string address)
{
lock (this)
{
var found = _neighbors.Values.FirstOrDefault(neighbor => neighbor.Address == address);
return found ?? new Neighbor { Address = address };
}
}

private void ScanNeighbors()
{
var localAddresses = GetAllIPAddresses();

while (true)
{
var from = new IPEndPoint(IPAddress.Any, 0);
var buffer = _client.Receive(ref from);
var header = Encoding.UTF8.GetString(buffer, 0, buffer.Length);
var parts = header.Split(new[] { '|' }, 2);

Debugger.Log("Received broadcast header: " + header);

if (parts.Length != 2 || parts[0] != HeaderPrefix)
continue;

var address = from.Address.ToString();
if (localAddresses.Contains(address))
continue;

var name = parts[1];
var neighbor = new Neighbor
{
Name = name,
LastOnline = DateTime.Now,
Address = address
};

lock (this)
{
var oldSize = _neighbors.Count;

_neighbors[address] = neighbor;

if (oldSize != _neighbors.Count)
OnNeighborsChanged?.Invoke();
}
}
}

private List<string> GetAllIPAddresses()
{
var ret = new List<string>();
foreach (var netInterface in NetworkInterface.GetAllNetworkInterfaces())
{
var ipProps = netInterface.GetIPProperties();
foreach (var address in ipProps.UnicastAddresses)
{
if (address.Address.AddressFamily == AddressFamily.InterNetwork)
ret.Add(address.Address.ToString());
}
}

return ret;
}

private void UpdateNeighborsStatus(object state)
{
Ping();

lock (this)
{
var now = DateTime.Now;
var pairs = _neighbors.ToArray();
foreach (var pair in pairs)
{
if (now - pair.Value.LastOnline > Constants.ShortestOnlineTime)
{
_neighbors.Remove(pair.Key);
}
}

if (_neighbors.Count != pairs.Length)
OnNeighborsChanged?.Invoke();
}
}

private async void Ping()
{
var header = $"{HeaderPrefix}|{Name}";
var bytes = Encoding.UTF8.GetBytes(header);
await _client.SendAsync(bytes, bytes.Length, "255.255.255.255", Constants.StationPort);
}

public void Start()
{
_client.Client.Bind(new IPEndPoint(IPAddress.Any, Constants.StationPort));
_thread.Start();
_timer.Change(0, 2000);
}

public void Stop()
{
_timer.Dispose();
_client.Close();
}

public class Neighbor
{
public string Address { get; set; }
public string Name { get; set; }
public DateTime LastOnline { get; set; }

public override string ToString()
{
var name = string.IsNullOrEmpty(Name) ? "UNKNOWN" : Name;
return $"{name} [{Address}]";
}
}
}
using DropZone.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace DropZone.Protocol
{
public class Station
{
private const string HeaderPrefix = "DROP_ZONE";

public string Name { get; }
private readonly UdpClient _client;
private readonly ThreadWrapper _thread;
private readonly Dictionary<string, Neighbor> _neighbors = new Dictionary<string, Neighbor>();
private readonly Timer _timer;
private readonly string _id;

public Action OnNeighborsChanged { get; set; }

public List<Neighbor> Neighbors
{
get
{
lock (this)
{
return _neighbors.Values.ToList();
}
}
}

public Station(string name)
{
Name = name;
_id = Guid.NewGuid().ToString();
_client = new UdpClient();
_thread = new ThreadWrapper
{
DoWork = ScanNeighbors
};
_timer = new Timer(UpdateNeighborsStatus);
}

public Neighbor GetNeighbor(string address)
{
lock (this)
{
var found = _neighbors.Values.FirstOrDefault(neighbor => neighbor.Address == address);
return found ?? new Neighbor { Address = address };
}
}

private void ScanNeighbors()
{
while (true)
{
var from = new IPEndPoint(IPAddress.Any, 0);
var buffer = _client.Receive(ref from);
var header = Encoding.UTF8.GetString(buffer, 0, buffer.Length);

Debugger.Log("Received broadcast header: " + header);

var pingInfo = PingInfo.Parse(header);
if (pingInfo == null || pingInfo.Id == _id)
continue;

var address = from.Address.ToString();
var neighbor = new Neighbor
{
Name = pingInfo.Name,
LastOnline = DateTime.Now,
Address = address
};

lock (this)
{
var oldSize = _neighbors.Count;

_neighbors[address] = neighbor;

if (oldSize != _neighbors.Count)
OnNeighborsChanged?.Invoke();
}
}
}

private void UpdateNeighborsStatus(object state)
{
Ping();

lock (this)
{
var now = DateTime.Now;
var pairs = _neighbors.ToArray();
foreach (var pair in pairs)
{
if (now - pair.Value.LastOnline > Constants.ShortestOnlineTime)
{
_neighbors.Remove(pair.Key);
}
}

if (_neighbors.Count != pairs.Length)
OnNeighborsChanged?.Invoke();
}
}

private async void Ping()
{
var header = new PingInfo { Id = _id, Name = Name }.BuildMessage();
var bytes = Encoding.UTF8.GetBytes(header);
await _client.SendAsync(bytes, bytes.Length, "255.255.255.255", Constants.StationPort);
}

public void Start()
{
_client.Client.Bind(new IPEndPoint(IPAddress.Any, Constants.StationPort));
_thread.Start();
_timer.Change(0, 2000);
}

public void Stop()
{
_timer.Dispose();
_client.Close();
}

public class Neighbor
{
public string Address { get; set; }
public string Name { get; set; }
public DateTime LastOnline { get; set; }

public override string ToString()
{
var name = string.IsNullOrEmpty(Name) ? "UNKNOWN" : Name;
return $"{name} [{Address}]";
}
}

private class PingInfo
{
public string Name { get; set; }
public string Id { get; set; }

public string BuildMessage()
{
return $"{HeaderPrefix}|{Name}|{Id}";
}

public static PingInfo Parse(string st)
{
if (string.IsNullOrEmpty(st))
return null;

var parts = st.Split(new[] { '|' }, 3);
if (parts.Length != 3 || parts[0] != HeaderPrefix)
return null;

var name = parts[1];
var id = parts[2];
return new PingInfo
{
Name = name,
Id = id
};
}
}
}
}

0 comments on commit 76b3e3f

Please sign in to comment.