-
Notifications
You must be signed in to change notification settings - Fork 0
/
DiscoveryService.cs
114 lines (92 loc) · 3.91 KB
/
DiscoveryService.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Linq;
using Squared.Task;
using Squared.Task.IO;
using System.Diagnostics;
namespace Tsunagaro {
public class DiscoveryService {
public const int DiscoveryPort = 9887;
public const double HeartbeatIntervalSeconds = 60 * 5;
public readonly TaskScheduler Scheduler;
private readonly Signal EarlyAnnounceSignal = new Signal();
public UdpClient Listener;
public DiscoveryService (TaskScheduler scheduler) {
Scheduler = scheduler;
}
public IEnumerator<object> Initialize () {
yield return Future.RunInThread(() => {
Listener = new UdpClient() {
ExclusiveAddressUse = false,
EnableBroadcast = true,
MulticastLoopback = true,
DontFragment = true
};
Listener.Client.ExclusiveAddressUse = false;
Listener.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
Listener.Client.Bind(new IPEndPoint(IPAddress.Any, DiscoveryPort));
});
Scheduler.Start(ListenTask(), TaskExecutionPolicy.RunAsBackgroundTask);
Scheduler.Start(HeartbeatTask(), TaskExecutionPolicy.RunAsBackgroundTask);
}
public IEnumerator<object> HeartbeatTask () {
while (true) {
yield return Announce();
yield return Future.WaitForFirst(
EarlyAnnounceSignal.Wait(),
Scheduler.Start(new Sleep(HeartbeatIntervalSeconds))
);
}
}
public IEnumerator<object> ListenTask () {
while (true) {
var fAnnouncement = Listener.AsyncReceive();
yield return fAnnouncement;
Scheduler.Start(ProcessAnnouncement(fAnnouncement.Result), TaskExecutionPolicy.RunAsBackgroundTask);
}
}
public IEnumerator<object> Announce () {
var payload = BitConverter.GetBytes(Program.Control.Port);
yield return Listener.AsyncSend(payload, payload.Length, new IPEndPoint(IPAddress.Broadcast, DiscoveryPort));
}
private IEnumerator<object> ProcessAnnouncement (Network.UdpPacket packet) {
var fAddresses = new Future<IPAddress[]>();
Dns.BeginGetHostAddresses(
Dns.GetHostName(),
(ar) => {
try {
fAddresses.Complete(Dns.EndGetHostAddresses(ar));
} catch (Exception exc) {
fAddresses.Fail(exc);
}
},
null
);
yield return fAddresses;
var endpoint = new IPEndPoint(
packet.EndPoint.Address,
BitConverter.ToInt32(packet.Bytes, 0)
);
if (
fAddresses.Result.Contains(endpoint.Address) &&
(endpoint.Port == Program.Control.Port)
) {
// This discovery ping is from me
} else {
if (!Program.Peer.Peers.ContainsKey(endpoint))
Console.WriteLine("Got announcement from unknown peer {0}", endpoint);
var fTryConnect = Scheduler.Start(Program.Peer.TryConnectTo(endpoint));
yield return fTryConnect;
if ((fTryConnect.Failed) || Object.Equals(fTryConnect.Result, false)) {
const float retryDelay = 10;
Console.WriteLine("Connection attempt failed. Waiting {0} second(s) and retrying.", retryDelay);
yield return new Sleep(retryDelay);
yield return Program.Peer.TryConnectTo(endpoint);
}
}
}
}
}