Write initial DiscoveryBeacon and PixelServer implementation.

This commit is contained in:
Starbeamrainbowlabs 2016-10-15 16:11:14 +01:00
parent 9ae5d2c97a
commit 9c545a6d2a
5 changed files with 210 additions and 13 deletions

View file

@ -19,13 +19,19 @@ namespace PixelServer.Net
{ {
TextWriter logger; TextWriter logger;
UdpClient beacon; UdpClient beacon;
int emitFrequency = 2000;
string role = "server";
/// <summary> /// <summary>
/// Whether the beacon is currently active. /// Whether the beacon is currently active.
/// If true then the beacon needs disposing of properly by calling the Dispose() method before the program ends. /// If true then the beacon needs disposing of properly by calling the Dispose() method before the program ends.
/// </summary> /// </summary>
public bool Active { get; private set; } = true; public bool Active { get; private set; } = true;
/// <summary>
/// Whether the logging output should be verbose.
/// </summary>
public bool Verbose { get; private set; } = false;
string role = "server";
/// <summary> /// <summary>
/// The current IP address of the local machine. /// The current IP address of the local machine.
@ -47,7 +53,9 @@ namespace PixelServer.Net
/// The port that the beacon should broadcast on. /// The port that the beacon should broadcast on.
/// </summary> /// </summary>
public int Port { get; set; } = 5050; public int Port { get; set; } = 5050;
/// <summary>
/// The role that the program hosted at this beacon plays. Default: server.
/// </summary>
public string Role { public string Role {
get{ get{
return role; return role;
@ -60,9 +68,17 @@ namespace PixelServer.Net
} }
/// <summary> /// <summary>
/// The frequency at which the beacon should emit broadcasts. /// The time, in milliseconds, that the beacon should wait between broadcasts.
/// </summary> /// </summary>
public int EmitFrequency { get; set; } = 2000; public int EmitFrequency {
get {
return emitFrequency;
}
set {
emitFrequency = value;
logger.WriteLine("Beacon broadcast frequency changed to {0}ms.", EmitFrequency);
}
}
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="PixelServer.Net.DiscoveryBeacon"/> class. /// Initializes a new instance of the <see cref="PixelServer.Net.DiscoveryBeacon"/> class.
@ -103,7 +119,7 @@ namespace PixelServer.Net
public async Task Emit() public async Task Emit()
{ {
connect(); connect();
logger.WriteLine("Beacon activated with broadcast interval of {0}ms.", EmitFrequency);
while(true) while(true)
{ {
await emitOnce(); await emitOnce();
@ -129,6 +145,7 @@ namespace PixelServer.Net
beacon = new UdpClient(Port); beacon = new UdpClient(Port);
Active = true; Active = true;
beacon.JoinMulticastGroup(DestinationAddress); beacon.JoinMulticastGroup(DestinationAddress);
logger.WriteLine("One way beacon set up pointing at {0}.", MulticastChannel);
} }
/// <summary> /// <summary>
@ -154,7 +171,7 @@ namespace PixelServer.Net
/// <param name="data">The message payload.</param> /// <param name="data">The message payload.</param>
private async Task Send(IPEndPoint destination, string data) private async Task Send(IPEndPoint destination, string data)
{ {
logger.WriteLine("Sending '{0}' to {1}.", Utilities.EscapeString(data), destination); if(Verbose) logger.WriteLine("Sending '{0}' to {1}.", Utilities.EscapeString(data), destination);
byte[] payload = Encoding.UTF8.GetBytes(data); byte[] payload = Encoding.UTF8.GetBytes(data);
await Send(destination, payload); await Send(destination, payload);
} }
@ -182,7 +199,12 @@ namespace PixelServer.Net
/// <see cref="PixelServer.Net.DiscoveryBeacon"/> was occupying.</remarks> /// <see cref="PixelServer.Net.DiscoveryBeacon"/> was occupying.</remarks>
public void Dispose() public void Dispose()
{ {
beacon.Close(); if(Active)
{
beacon.Close();
Active = false;
}
} }
} }
} }

View file

@ -0,0 +1,94 @@
using System;
using System.Net.Sockets;
using System.Threading.Tasks;
using System.Net;
using SBRL.Utilities;
using System.IO;
namespace PixelServer
{
/// <summary>
/// Provides the server component of the Hull Pixelbot server that can be used to control any number of pixel bots at once.
/// </summary>
public class PixelServer
{
TextWriter logger;
IPAddress bindAddress = IPAddress.Any;
int port;
TcpListener server;
/// <summary>
/// Whether the Hull Pixelbot serve is currently active and listening.
/// </summary>
public bool Active { get; private set; } = false;
public IPAddress BindAddress
{
get {
return bindAddress;
}
set {
if (Active == true)
throw new InvalidOperationException("Error: The bind address can't be changed once the server has started listening!");
bindAddress = value;
}
}
public int Port
{
get {
return port;
}
set {
if (Active == true)
throw new InvalidOperationException("Error: The port can't be changed once the server has started listening!");
port = value;
}
}
public IPEndPoint Endpoint
{
get {
return new IPEndPoint(BindAddress, Port);
}
}
public PixelServer(int inPort, TextWriter inLogger)
{
Port = inPort;
logger = inLogger;
}
public PixelServer(int inPort) : this(inPort, Console.Out)
{
}
public async Task Listen()
{
server = new TcpListener(Endpoint);
logger.WriteLine("TCP Listener set up on {0}.", Endpoint);
Active = true;
while(true)
{
TcpClient nextClient = await server.AcceptTcpClientAsync();
AsyncTools.ForgetTask(Handle(nextClient));
}
// TODO: Add an shutdown CancellationToken thingy here
//Active = false;
}
private async Task Handle(TcpClient client)
{
logger.WriteLine("Accepted connection from {0}.", client.Client.RemoteEndPoint);
using (StreamReader incoming = new StreamReader(client.GetStream()))
using (StreamWriter outgoing = new StreamWriter(client.GetStream()) { AutoFlush = true })
{
string nextLine;
while((nextLine = await incoming.ReadLineAsync()) != null)
{
Console.WriteLine("Got message from client: '{0}'", nextLine.Trim());
}
}
Console.WriteLine("Lost connection from {0}.", client.Client.RemoteEndPoint);
}
}
}

View file

@ -38,6 +38,8 @@
<Compile Include="DiscoveryBeacon.cs" /> <Compile Include="DiscoveryBeacon.cs" />
<Compile Include="Utilities.cs" /> <Compile Include="Utilities.cs" />
<Compile Include="ForgetTask.cs" /> <Compile Include="ForgetTask.cs" />
<Compile Include="PixelServer.cs" />
<Compile Include="PrefixedWriter.cs" />
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
</Project> </Project>

View file

@ -0,0 +1,67 @@
using System;
using System.IO;
using System.Text;
namespace SBRL.Utilities
{
public class PrefixedWriter : TextWriter, IDisposable
{
private TextWriter destination;
public string Prefix { get; set; } = "";
public override Encoding Encoding {
get {
return Encoding.UTF8;
}
}
public PrefixedWriter(TextWriter inDestination)
{
destination = inDestination;
}
public override void Write(string str, params object[] args)
{
destination.Write(Prefix + str, args);
}
public override void Write(string str)
{
Write(str, new object[] {});
}
public override void Write(string str, object arg1)
{
Write(str, new object[] { arg1 });
}
public override void Write(string str, object arg1, object arg2)
{
Write(str, new object[] { arg1, arg2 });
}
public override void Write(string str, object arg1, object arg2, object arg3)
{
Write(str, new object[] { arg1, arg2, arg3 });
}
public override void WriteLine(string str, params object[] args)
{
Write(str + Environment.NewLine, args);
}
public override void WriteLine(string str)
{
WriteLine(str, new object[] {});
}
public override void WriteLine(string str, object arg1)
{
WriteLine(str, new object[] { arg1 });
}
public override void WriteLine(string str, object arg1, object arg2)
{
WriteLine(str, new object[] { arg1, arg2 });
}
public override void WriteLine(string str, object arg1, object arg2, object arg3)
{
WriteLine(str, new object[] { arg1, arg2, arg3 });
}
}
}

View file

@ -1,22 +1,34 @@
using System; using System;
using PixelServer.Net;
using System.Net; using System.Net;
using System.Threading.Tasks; using System.Threading.Tasks;
using SBRL.Utilities; using SBRL.Utilities;
using System.Net.NetworkInformation; using PixelServer.Net;
using System.Net.Sockets;
namespace PixelServer namespace PixelServer
{ {
class MainClass static class Program
{ {
private static int port = 5050;
private static PrefixedWriter systemWriter = new PrefixedWriter(Console.Out) { Prefix = "[System] " };
private static PrefixedWriter beaconWriter = new PrefixedWriter(Console.Out) { Prefix = "[Beacon] " };
private static PrefixedWriter serverWriter = new PrefixedWriter(Console.Out) { Prefix = "[Server] " };
public static void Main(string[] args) public static void Main(string[] args)
{ {
DiscoveryBeacon beacon = new DiscoveryBeacon(IPAddress.Parse("239.62.148.30")); systemWriter.WriteLine("Booting server.");
DiscoveryBeacon beacon = new DiscoveryBeacon(
IPAddress.Parse("239.62.148.30"), port,
beaconWriter
);
PixelServer server = new PixelServer(port, serverWriter);
systemWriter.WriteLine("Server booting complete. Beginning async loop.");
//AsyncTools.ForgetTask(beacon.Emit()); //AsyncTools.ForgetTask(beacon.Emit());
beacon.Emit().Wait(); Task.WaitAll(new Task[] {
beacon.Emit(),
server.Listen()
});
} }
} }
} }