/// <summary> /// Listens to the socket and registers all new requests /// </summary> /// <returns>The async.</returns> /// <param name="selfinfo">Selfinfo.</param> /// <param name="sock">Sock.</param> private static Task ListenAsync(PeerInfo selfinfo, TcpListener sock) { // Set up a channel for sending sockets var sockchan = Channel.Create <TcpClient>(); var listener = Task.Run(async() => { while (true) { await sockchan.WriteAsync(await sock.AcceptTcpClientAsync()); } }); // Handle each request var handlers = Skeletons.CollectAsync(sockchan, async client => { var peer = PeerConnection.CreatePeer(selfinfo, selfinfo, client.GetStream()); // Ping the peer var res = await peer.Item2.SendConnectionRequestAsync(null, null, new Protocol.Request() { Operation = Protocol.Operation.Ping, Self = selfinfo, }); // Register this peer with the broker await Channels.ConnectionBrokerRegistrations.Get().WriteAsync( new ConnectionRegistrationRequest() { IsTerminate = false, UpdateRouting = true, Peer = res.Response.Self, Channel = peer.Item2 } ); // If we get anything back, register with the routing table if (res.Response.Peers != null) { var router = Channels.RoutingTableRequests.Get(); log.Debug($"Ping response had {res.Response.Peers.Count} peers, registering with routing table"); foreach (var np in res.Response.Peers) { await router.AddPeerAsync(np.Key, np); } } log.Debug("Completed initial ping sequence"); }, 10); return(Task.WhenAll(listener, handlers)); }
/// <summary> /// Runs the console interface /// </summary> /// <returns>An awaitable task.</returns> public static Task RunAsync() { // Set up a console forwarder process var consoleOut = Skeletons.CollectAsync( Channels.ConsoleOutput.ForRead, x => Console.Out.WriteLineAsync(x ?? string.Empty) ); // Set up a channel for sending control messages var inputChannel = Channel.Create <string>(buffersize: 10); // Set up the console reader process var consoleInput = AutomationExtensions.RunTask( new { Control = inputChannel.AsWrite() }, async self => { string line; // TODO: The blocking read prevents clean shutdown, // but direct access to the input stream has issues with the buffer while ((line = await Task.Run(() => Console.ReadLine())) != null) { await self.Control.WriteAsync(line); } } ); // Set up the control logic handler var proc = AutomationExtensions.RunTask(new { Control = inputChannel.AsRead(), Output = Channels.ConsoleOutput.ForWrite }, async self => { var peers = new List <Tuple <PeerInfo, Task, IWriteChannel <PeerRequest> > >(); var rnd = new Random(); var portnr = 15000; await self.Output.WriteAsync(HELPTEXT); while (true) { try { var commandline = await self.Control.ReadAsync() ?? string.Empty; var command = commandline.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries).FirstOrDefault() ?? string.Empty; if (string.Equals(command, "help", StringComparison.OrdinalIgnoreCase)) { await self.Output.WriteAsync(HELPTEXT); } else if (string.Equals(command, "exit", StringComparison.OrdinalIgnoreCase) || string.Equals(command, "quit", StringComparison.OrdinalIgnoreCase)) { return; } else if (string.Equals(command, "check", StringComparison.OrdinalIgnoreCase)) { for (var i = peers.Count - 1; i >= 0; i--) { if (await peers[i].Item3.IsRetiredAsync) { await self.Output.WriteAsync($"Peer {peers[i].Item1.Key} at {peers[i].Item1.Address} terminated"); peers.RemoveAt(i); } } await self.Output.WriteAsync($"Completed check, found {peers.Count} live peers"); } else if (string.Equals(command, "node", StringComparison.OrdinalIgnoreCase)) { var actions = commandline.Split(new char[] { ' ' }, 4, StringSplitOptions.RemoveEmptyEntries); if (string.Equals(actions[1], "start", StringComparison.OrdinalIgnoreCase)) { var pi = new PeerInfo(Key.CreateRandomKey(), new IPEndPoint(IPAddress.Loopback, portnr)); await self.Output.WriteAsync($"Starting node {pi.Key} on {pi.Address}"); var chan = Channel.Create <PeerRequest>(); var s = Task.Run(() => Peer.RunPeer( pi, 5, 100, TimeSpan.FromDays(1), peers.Count == 0 ? new EndPoint[0] : new[] { peers[rnd.Next(0, peers.Count - 1)].Item1.Address }, chan.AsRead() ) .ContinueWith(_ => inputChannel.WriteAsync("check")) ); peers.Add(new Tuple <PeerInfo, Task, IWriteChannel <PeerRequest> >(pi, s, chan)); portnr++; } else if (string.Equals(actions[1], "list", StringComparison.OrdinalIgnoreCase)) { if (actions.Length != 2) { await self.Output.WriteAsync("The list command takes no arguments"); continue; } for (var i = 0; i < peers.Count; i++) { await self.Output.WriteAsync(string.Format("{0}: {1} - {2}", i, peers[i].Item1.Key, peers[i].Item1.Address)); } await self.Output.WriteAsync(string.Empty); } else if (string.Equals(actions[1], "connect", StringComparison.OrdinalIgnoreCase)) { actions = commandline.Split(new char[] { ' ' }, 5, StringSplitOptions.RemoveEmptyEntries); if (actions.Length != 4) { await self.Output.WriteAsync("The connect command needs exactly two arguments, the ip and the port"); continue; } if (!IPAddress.TryParse(actions[2], out var ip)) { await self.Output.WriteAsync($"Failed to parse ip: {actions[2]}"); continue; } if (!int.TryParse(actions[3], out var port)) { await self.Output.WriteAsync($"Failed to parse {actions[3]} as an integer"); continue; } var pi = new PeerInfo(Key.CreateRandomKey(), new IPEndPoint(IPAddress.Loopback, portnr)); await self.Output.WriteAsync($"Starting node {pi.Key} on {pi.Address}"); var chan = Channel.Create <PeerRequest>(); var s = Task.Run(() => Peer.RunPeer( pi, 5, 100, TimeSpan.FromDays(1), new[] { new IPEndPoint(ip, port) }, chan.AsRead() ) .ContinueWith(_ => inputChannel.WriteAsync("check")) ); peers.Add(new Tuple <PeerInfo, Task, IWriteChannel <PeerRequest> >(pi, s, chan)); portnr++; } else if (string.Equals(actions[1], "stop", StringComparison.OrdinalIgnoreCase) || string.Equals(actions[1], "stat", StringComparison.OrdinalIgnoreCase) || string.Equals(actions[1], "refresh", StringComparison.OrdinalIgnoreCase)) { if (actions.Length != 3) { await self.Output.WriteAsync($"The {actions[1]} command takes exactly one argument, the node number"); continue; } if (!int.TryParse(actions[2], out var ix)) { await self.Output.WriteAsync($"Failed to parse {actions[2]} as an integer"); continue; } if (ix < 0 || ix >= peers.Count) { await self.Output.WriteAsync($"The node number must be positive and less than {peers.Count}"); continue; } if (string.Equals(actions[1], "stop", StringComparison.OrdinalIgnoreCase)) { await self.Output.WriteAsync($"Stopping node {ix} ({peers[ix].Item1.Key} at {peers[ix].Item1.Address}) ..."); await peers[ix].Item3.RetireAsync(); await self.Output.WriteAsync($"Stopped node ({peers[ix].Item1.Key} at {peers[ix].Item1.Address}) ..."); //peers.RemoveAt(ix); } else if (string.Equals(actions[1], "stat", StringComparison.OrdinalIgnoreCase)) { await self.Output.WriteAsync($"Requesting stats from node {ix} ({peers[ix].Item1.Key} at {peers[ix].Item1.Address}) ..."); var channel = Channel.Create <PeerResponse>(); await peers[ix].Item3.WriteAsync(new PeerRequest() { Operation = PeerOperation.Stats, Response = channel }); await self.Output.WriteAsync($"Stats requested, waiting for response..."); await self.Output.WriteAsync(System.Text.Encoding.UTF8.GetString((await channel.ReadAsync()).Data)); } else if (string.Equals(actions[1], "refresh", StringComparison.OrdinalIgnoreCase)) { await self.Output.WriteAsync($"Performing refresh on {ix} ({peers[ix].Item1.Key} at {peers[ix].Item1.Address}) ..."); var channel = Channel.Create <PeerResponse>(); await peers[ix].Item3.WriteAsync(new PeerRequest() { Operation = PeerOperation.Refresh, Response = channel }); var res = await channel.ReadAsync(); await self.Output.WriteAsync($"Refreshed with {res.SuccessCount} node(s)"); } else { await self.Output.WriteAsync($"Node action not recognized: {actions[1]}"); } } else { await self.Output.WriteAsync($"Node command not recognized: {actions[1]}"); } } else if (string.Equals(command, "add", StringComparison.OrdinalIgnoreCase)) { var actions = commandline.Split(new char[] { ' ' }, 2, StringSplitOptions.RemoveEmptyEntries); if (actions.Length == 1) { await self.Output.WriteAsync("The add command needs the value to add"); continue; } if (peers.Count == 0) { await self.Output.WriteAsync("The add command does not work if no nodes are started"); continue; } var channel = Channel.Create <PeerResponse>(); var data = System.Text.Encoding.UTF8.GetBytes(actions[1]); var key = Key.ComputeKey(data); await self.Output.WriteAsync($"Adding {data.Length} byte(s) with key {key}"); await peers[rnd.Next(0, peers.Count)].Item3.WriteAsync(new PeerRequest() { Operation = PeerOperation.Add, Key = key, Data = data, Response = channel, }); await self.Output.WriteAsync("Send add request, waiting for completion"); var res = await channel.ReadAsync(); await self.Output.WriteAsync($"Add inserted into {res.SuccessCount} node(s)"); } else if (string.Equals(command, "get", StringComparison.OrdinalIgnoreCase)) { var actions = commandline.Split(new char[] { ' ' }, 3, StringSplitOptions.RemoveEmptyEntries); if (actions.Length == 1) { await self.Output.WriteAsync("The get command needs the hash to find"); continue; } if (actions.Length == 3) { await self.Output.WriteAsync("The get command needs only one argument"); continue; } if (peers.Count == 0) { await self.Output.WriteAsync("The get command does not work if no nodes are started"); continue; } Key key; try { key = new Key(actions[1]); } catch (Exception ex) { await self.Output.WriteAsync($"Failed to parse key: {ex.Message}"); continue; } var channel = Channel.Create <PeerResponse>(); await self.Output.WriteAsync($"Locating key"); await peers[rnd.Next(0, peers.Count)].Item3.WriteAsync(new PeerRequest() { Operation = PeerOperation.Find, Key = key, Response = channel, }); var res = await channel.ReadAsync(); if (res.Data == null) { await self.Output.WriteAsync($"Did not find the key ..."); } else { await self.Output.WriteAsync($"Found: {System.Text.Encoding.UTF8.GetString(res.Data)}"); } } else if (string.Equals(command, "hash", StringComparison.OrdinalIgnoreCase)) { var actions = commandline.Split(new char[] { ' ' }, 2, StringSplitOptions.RemoveEmptyEntries); if (actions.Length == 1) { await self.Output.WriteAsync("The add command needs the value to add"); continue; } await self.Output.WriteAsync($"Key: {Key.ComputeKey(actions[1])}"); } else { await self.Output.WriteAsync($"Command not recognized: {command}"); } } catch (Exception ex) { await self.Output.WriteAsync($"Command failed: {ex.Message}"); } } } ); return(Task.WhenAll(consoleOut /*, consoleInput*/, proc)); }