using System;
using Agent;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
using System.Collections;
using Timer = System.Timers.Timer;
using System.Collections.Generic;
using System.Security.Permissions;
namespace Server
{
class Program
{
static void Main(string[] args)
{
Console.Title = "Server Window";
// Create server object instance...
ServerClass.StartServer();
// FLUFF: Display statistics about our server channel...
RemoteUtility.ShowChannelInfo(ServerClass.ServerChannel);
// Wait... allow server instance to run...
System.Console.WriteLine("Press ENTER to quit");
System.Console.ReadLine();
}
}
// This is our broadcast/server object.
// Clients can subscribe to this service by creating an EventSink (see Agent.cs)
// and then adding client's event handler to ClientServiceEvent handler...
public class ServerClass : AbstractServer
{
// ServerClass instance objects...
static ServerClass Server { get; set; }
internal static TcpChannel ServerChannel { get; set; }
// List of clients subscribing to service...
Dictionary<int, ClientEventHandler> clients = new Dictionary<int, ClientEventHandler>();
int clientID = 0; // used to generate unique keys for our client list...
// Timer class used to broadcast messages on intervals for simulation...
TimerClass broadcastTimer;
public ServerClass()
{
// create timer that will broadcast messages at specified intervals...
broadcastTimer = new TimerClass(this, 3000); // broadcast at 3 second intervals...
broadcastTimer.StartTimer();
}
// Create broadcast server object...
[SecurityPermission(SecurityAction.Demand)]
internal static void StartServer()
{
try
{
// Create server and client sink providers needed for TCP channel...
BinaryServerFormatterSinkProvider serverProv = new BinaryServerFormatterSinkProvider();
serverProv.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full; //full deserialization level
BinaryClientFormatterSinkProvider clientProv = new BinaryClientFormatterSinkProvider();
// Properties of the server channel...
IDictionary props = new Hashtable();
props["port"] = 9999;
props["rejectRemoteRequests"] = "false";
// Create the server channel (message transport mechanism)...
ServerChannel = new TcpChannel(props,
clientProv, // receiver
serverProv); // sender
// Register the server channel...
ChannelServices.RegisterChannel(ServerChannel, false);
// Create the server instance...
Server = new ServerClass();
// Get type of the server instance to expose...
Type serverClassType = Server.GetType();
// Expose the server object for remote calls...
// Clients will create proxy using URI (RemotingBroadcastServer.rem),
// and our AbstractServer class object type...
RemotingConfiguration.RegisterWellKnownServiceType(
serverClassType, // Service type...
"RemotingBroadcastServer.rem", // object URI
WellKnownObjectMode.Singleton); // mode: every incoming message serviced by the same object instance...
}
catch (Exception e)
{
Console.WriteLine("Exception: {0}. Stack trace: {1}.", e.Message, e.StackTrace);
}
}
// A method to allow client to obtain unique client id assigned by server...
// These keys (ID's) are created by incrementing, beginning with ID = 1,
// every time a client subscribes...
public override int GetClientID(ClientEventHandler client)
{
Console.WriteLine("Client requested it's ID.");
// Iterate through each client that has subscribed to our broadcast...
Dictionary<int, ClientEventHandler>.KeyCollection keys = clients.Keys;
foreach (int key in keys)
{
// dispatch the message string...
ClientEventHandler subscriber = clients[key];
if (client.Equals(subscriber))
return key;
}
// Our server's response if key not found...
return -1; // client not found!--how can this be?--should never happen!
}
// A method to demonstrate and allow client to send a message to server...
public override string MsgToServer(string s)
{
Console.WriteLine("Client sent message: " + s);
// Our server's response...
return "Server received message from client.";
}
// Event handler that allows clients to subscribe to the broadcast server events...
public override event ClientEventHandler ClientServiceEvent
{
add
{
Console.WriteLine("New Client subscribed to service...");
// Add client's event handler to our subscribed list...
clientID++;
clients[clientID] = value;
}
remove
{
// Just allow broadcast/dispatcher to remove so we don't have to
// depend on client to perform a graceful shutdown/cleanup;
// If connection is lost, client's event handler will be removed...
}
}
// Our broadcast/dispatcher to each client that has subscribed to the
// service event (ClientServiceEvent)...
// Called by timer and transmits date/time stamp to clients at specified intervals...
protected void BroadcastedMessageEventToClients(string s)
{
// list of any keys will be built to remove if any connection fails...
List<int> removeKeys = new List<int>();
// for each client subscribing to our service...
Dictionary<int, ClientEventHandler>.KeyCollection keys = clients.Keys;
foreach(int key in keys)
{
try
{
// retrieve client/subscriber's event handler...
ClientEventHandler subscriber = clients[key];
// dispatch the message string...
subscriber(s);
}
catch (Exception e)
{
// if exception, assume client has shutdown or been disconnected;
// remove client from subscribed list...
Console.WriteLine("Exception: {0}.", e.Message);//, e.StackTrace);
Console.WriteLine("\tRemoving client (ID: {0}) event handler...", key);
removeKeys.Add(key);
}
}
// remove any failed client connections...
foreach (int key in removeKeys)
clients.Remove(key);
}
// This timer class borrowed from sknake's code found in thread "blinking label"...
class TimerClass
{
ServerClass server;
public TimerClass(ServerClass server, int interval)
{
this.server = server;
__timerLock = new object();
timer = new Timer();
timer.Interval = interval;
timer.AutoReset = true;
timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);
syncPoint = 2; //NOTE the value change here
}
private object __timerLock;
private bool TimerRunning
{
get
{
lock (__timerLock)
{
return timer.Enabled;
}
}
}
private Timer timer;
/// <summary>
/// 0: The timer is available for the next elapsed event
/// 1: A thread is busy with the timer's elapsed event
/// 2: The timer is in a stopped state
/// </summary>
private long syncPoint;
public void StartTimer()
{
if (this.TimerRunning)
throw new InvalidOperationException("The timer is already running.");
if (System.Threading.Interlocked.Read(ref syncPoint) != 2)
throw new InvalidOperationException("The syncPoint value is not in the correct state for the timer to start");
lock (__timerLock)
{
System.Threading.Interlocked.Exchange(ref syncPoint, 0);
timer.Start();
}
}
public void StopTimer()
{
if (!this.TimerRunning)
throw new InvalidOperationException("The timer is not running.");
lock (__timerLock)
{
timer.Stop();
}
while (System.Threading.Interlocked.CompareExchange(ref syncPoint, 2, 0) != 2)
System.Threading.Thread.Sleep(10);
}
void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
long sync = System.Threading.Interlocked.CompareExchange(ref syncPoint, 1, 0);
if (sync == 0)
{
try
{
// Broadcast message to all subscribed clients...
server.BroadcastedMessageEventToClients(DateTime.Now.ToString());
}
finally
{
System.Threading.Interlocked.Exchange(ref syncPoint, 0);
}
}
}
}
}
}