/// François-Régis Colin
/// http://www.tls.cena.fr/products/ivy/
/// *
/// (C) CENA
/// *
///
namespace IvyBus
{
using System;
using System.IO;
using System.Collections;
using System.Collections.Specialized;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Configuration;
using System.Globalization;
using System.Text.RegularExpressions;
using System.Text;
using System.Reflection;
using System.ComponentModel;
using System.Diagnostics;
#if (!PocketPC)
using System.ComponentModel.Design;
#endif
// using System.Drawing.Design;
/// The Main bus Class
///
///
#if (!PocketPC)
// Show this property in the property grid.
[ToolboxItemFilter("System.Windows.Forms.Form", ToolboxItemFilterType.Allow)]
[Description("IVY Main API")]
#endif
[DesignerCategory("Component")]
public class Ivy : System.ComponentModel.Component, ISupportInitialize
{
/* Event */
/// fires when a new client connect to the bus
public event EventHandler ClientConnected;
/// fires when a client discconnect from the bus
public event EventHandler ClientDisconnected;
/// fires when a client receive a direct message from another client
public event EventHandler DirectMessageReceived;
/// fires when somebody ask for killing every client on the bus
public event EventHandler DieReceived;
/// fires when a client receive a add binding from another client
public event EventHandler BindingAdd;
/// fires when a client receive a remove binding from another client
public event EventHandler BindingRemove;
/// fires when a client receive a binding from another client and it as been filtered
public event EventHandler BindingFilter;
/// fires when a client receive a remove binding from another client
public event EventHandler ErrorMessage;
#if (!PocketPC)
[Bindable(true)]
[Category("Ivy")]
#endif
[DefaultValue(false)]
public static bool DebugProtocol
{
get
{
return debugProtocol;
}
set
{
debugProtocol = value;
}
}
#if (!PocketPC)
[Category("Ivy")]
#endif
public CultureInfo Culture
{
get
{
return culture;
}
set
{
culture = value;
}
}
/// IvyClients accesses the list of the connected clients
#if (!PocketPC)
[Browsable(false)]
#endif
public List IvyClients
{
get
{
return clients;
}
}
/// AppName the application name
#if (!PocketPC)
[Category("Ivy")]
[Bindable(true)]
#endif
[DefaultValue(null)]
public string AppName
{
set
{
appName = value;
}
get
{
return appName;
}
}
/// AppId the Application Unique ID
#if (!PocketPC)
[Browsable(false)]
#endif
public string AppId
{
get
{
return applicationUniqueId;
}
}
/// AppPriority the Application Priority: the clients list is sorted against priority
#if (!PocketPC)
[Category("Ivy")]
#endif
[DefaultValue(DEFAULT_PRIORITY)]
public ushort AppPriority
{
set
{
applicationPriority = value;
lock (clients)
{
foreach (IvyClient client in clients)
{
client.stream.TokenApplicationId(applicationPriority, AppId);
}
}
}
get
{
return applicationPriority;
}
}
#if (!PocketPC)
[Browsable(false)]
#endif
public int ProtocolVersion
{
get
{
return protocolVersion;
}
}
/// IsRunning is the bus Started and connected ?
#if (!PocketPC)
[Browsable(false)]
#endif
public bool IsRunning
{
get
{
return !stopped;
}
}
///SentMessageClasses the first word token of sent messages
/// optimise the parsing process when sending messages
///
#if (!PocketPC)
[Category("Ivy")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
// sinon bug System.String constructor not found !
[Editor("System.Windows.Forms.Design.StringCollectionEditor, System.Design", "System.Drawing.Design.UITypeEditor, System.Drawing")]
#endif
public List SentMessageFilter
{
get
{
return sent_messageFilter;
}
}
/// ReadyMessage message send when Application receive all the regexp at the connection of the client
#if (!PocketPC)
[Bindable(true)]
[Category("Ivy")]
#endif
[DefaultValue(null)]
public string ReadyMessage
{
get { return ready_message; }
set { ready_message = value; }
}
#if (PocketPC)
#if (!PocketPC)
[Category("Ivy")]
#endif
[DefaultValue(null)]
public System.Windows.Forms.ContainerControl ContainerControl
{
get { return parentControl; }
set
{
parentControl = value;
}
}
#endif
#if (!PocketPC)
[Category("Ivy")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
#endif
public List Bindings
{
get { return app_bindings; }
}
internal class MyTcpListener : TcpListener
{
public MyTcpListener(IPAddress address, int port)
: base(address, port)
{
}
public bool IsActive()
{
return this.Active;
}
}
/// the name of the application on the bus
internal string appName;
/// the port for the UDP rendez vous, if none is supplied
internal const ushort DEFAULT_PORT = 2010;
// client default priority
internal const ushort DEFAULT_PRIORITY = 100;
/// the domain for the UDP rendez vous
private static readonly string DEFAULT_DOMAIN = "127.255.255.255:" + DEFAULT_PORT;
internal int protocolVersion = 3;
private static bool debugProtocol; // false by default runtime
private static ushort serial; /* an unique ID for each regexp */ // 0 by default runtime
private MyTcpListener app;
private List watchers;
private volatile Thread serverThread; // to ensure quick communication of the end
internal Dictionary bindings;
//TODO should be remove samve as above
private List app_bindings;
private List clients;
private List sent_messageFilter;
private bool stopped = true;
internal ushort applicationPort; /* Application port number */
internal IPAddress applicationHost; /* Application host number */
internal string applicationUniqueId; /* identifier Application unique timestamp-ipaddress-port */
internal ushort applicationPriority = DEFAULT_PRIORITY;
private string ready_message;
private CultureInfo culture = new CultureInfo("en-us");
// for synchronous event
#if (PocketPC)
private System.Windows.Forms.ContainerControl parentControl;
#else
private readonly SynchronizationContext syncContext;
#endif
///
/// Initializes a new instance of the class.
///
public Ivy()
{
#if (!PocketPC)
syncContext = SynchronizationContext.Current;
#endif
clients = new List();
bindings = new Dictionary();
app_bindings = new List();
sent_messageFilter = new List();
#if (!PocketPC)
debugProtocol = Properties.Settings.Default.IvyDebug;
protocolVersion = Properties.Settings.Default.IvyProtocolVersion;
#endif
// get binding from Attribute IvyBinding
//TODO Autobinding attribute
#if (PocketPC)
if (parentControl != null)
BindAttibute(parentControl);
#endif
Assembly assembly = Assembly.GetCallingAssembly();
if ( assembly != this.GetType().Assembly )
BindAttibute(assembly);
}
public Ivy(IContainer container)
: this()
{
container.Add(this);
// get binding from Attribute IvyBinding
//TODO Autobinding attribute
Assembly assembly = Assembly.GetCallingAssembly();
if (assembly != this.GetType().Assembly)
BindAttibute(assembly);
}
///
/// Readies the structures for the software bus connexion.
///
/// This sample shows how to start working with Ivy.
///
/// Ivy bus = new Ivy("Dummy agent","ready");
/// bus.bindMsg("(.*)",myMessageListener);
/// bus.start(null);
///
/// How to send & receive:
/// the Ivy agent A performs b.bindMsg("^Hello (*)",cb);
/// the Ivy agent B performs b2.sendMsg("Hello world");
/// a thread in A will run the callback cb with its second argument set
/// to a array of string, with one single element, "world"
///
///
/// the real work begin in the start() method
///
///
/// The name of your Ivy agent on the software bus
///
/// The hellow message you will send once ready
///
public Ivy(string name, string rdy_message)
: this()
{
appName = name;
ready_message = rdy_message;
// get binding from Attribute IvyBinding
//TODO Autobinding attribute
Assembly assembly = Assembly.GetCallingAssembly();
if (assembly != this.GetType().Assembly)
BindAttibute(assembly);
}
protected override void Dispose(bool disposing)
{
try
{
if (disposing)
{
Stop();
}
}
finally
{
base.Dispose(disposing);
}
}
// Autobinding on static method
public void BindAttibute(Type type)
{
if (type == null) return;
//Get instance of the IvyBindingAttribute.
foreach (MethodInfo m in type.GetMethods(BindingFlags.Static | BindingFlags.NonPublic))
{
foreach (IvyBindingAttribute attr in Attribute.GetCustomAttributes(m, typeof(IvyBindingAttribute)))
{
//TODO check paramater type MessageHandler
Debug.WriteLine("IvyBinding '" + attr.GetExpression(null) + "' to Method " + m.Name);
EventHandler handler;
#if (PocketPC)
//Createdelegate mydlg = new Createdelegate(m, null, EventArgs.Empty);
//bindMsg(attr.GetExpression(null), mydlg);
handler = null;
#else
handler = (EventHandler)Delegate.CreateDelegate(typeof(EventHandler), m);
#endif
BindMsg(attr.GetExpression(null), handler);
}
}
}
// Autobinding on instance method
public void BindAttibute(object obj)
{
if (obj == null) return;
Type type = obj.GetType();
//Get instance of the IvyBindingAttribute.
foreach (MethodInfo m in type.GetMethods(BindingFlags.Instance | BindingFlags.NonPublic))
{
foreach (IvyBindingAttribute attr in Attribute.GetCustomAttributes(m, typeof(IvyBindingAttribute)))
{
//TODO check paramater type MessageHandler
Ivy.traceProtocol("Ivy", "BindAttibute " + attr.GetExpression(obj) + "' to Method " + m.Name);
EventHandler handler;
#if (PocketPC)
handler = null; // TODO
#else
handler = (EventHandler)Delegate.CreateDelegate(typeof(EventHandler), obj, m);
#endif
BindMsg(attr.GetExpression(obj), handler);
}
}
}
// Autobinding on IvyBindingAttribute method
public void BindAttibute(Assembly assy)
{
foreach (Type typ in assy.GetTypes())
{
BindAttibute(typ);
}
}
///
/// connects the Ivy bus to a domain or list of domains.
///
/// a domain of the form 10.0.0:1234, it is similar to the
/// netmask without the trailing .255. This will determine the meeting point
/// of the different applications. Right now, this is done with an UDP
/// broadcast. Beware of routing problems ! You can also use a comma
/// separated list of domains.
///
/// One thread (IvyWatcher) for each traffic rendezvous (either UDP broadcast or TCP Multicast).
/// One thread (serverThread/Ivy) to accept incoming connexions on server socket.
/// a thread for each IvyClient when the connexion has been done.
///
public void Start(string domainbus)
{
domainbus = GetDomain(domainbus);
try
{
long seed = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
Random rand = new Random( (int)seed );
app = new MyTcpListener(IPAddress.Any, 0);
app.Start(); // should be started before getting port
applicationHost = GetLocalIP();
applicationPort = (ushort)((IPEndPoint)app.LocalEndpoint).Port;
applicationUniqueId = string.Format(culture,"{0}:{1}:{2}",
rand.Next(),
seed,
//applicationHost.ToString().Replace(".", ""),
applicationPort);
}
catch (IOException e)
{
throw new IvyException("can't open TCP service socket " + e);
}
Ivy.traceProtocol("BindAttibute", "Ivy protocol: " + ProtocolVersion + " TCP service open on port " + applicationPort);
watchers = new List();
Domain[] d = parseDomains(domainbus);
// readies the rendezvous : an IvyWatcher (thread) per domain bus
for (int index = 0; index < d.Length; index++)
{
IvyWatcher watcher = new IvyWatcher(this, d[index].Domainaddr, d[index].Port);
watchers.Add(watcher);
}
serverThread = new Thread(new ThreadStart(this.Run));
serverThread.Name = "Ivy Tcp Server Thread";
stopped = false;
serverThread.Start();
#if (PocketPC )
Ivy.traceProtocol("Ivy", "Threading start in progress...");
Thread.Sleep(100);
#else
// Wait for readyness
while ( serverThread.ThreadState != System.Threading.ThreadState.Running || !app.IsActive())
{
Ivy.traceError("BindAttibute", " Ivy Threading start in progress..." );
Thread.Sleep( 100 );
}
#endif
// sends the broadcasts and listen to incoming connexions
for (int i = 0; i < watchers.Count; i++)
{
watchers[i].start();
}
}
internal void SortClients()
{
lock (clients)
{
//clients.Sort(new IvyClient.IvyClientPriority());
clients.Sort();
}
}
/* a small private method for debbugging purposes */
public static string Domains(string toparse)
{
string domainbus = GetDomain(toparse);
StringBuilder s = new StringBuilder("broadcasting on ");
Ivy.Domain[] d = parseDomains(domainbus);
for (int index = 0; index < d.Length; index++)
{
s.Append(d[index].Domainaddr);
s.Append(":");
s.Append(d[index].Port);
s.Append(" ");
}
return s.ToString();
}
internal static Domain[] parseDomains(string domainbus)
{
string[] st = domainbus.Split(',');
Domain[] d = new Domain[st.Length];
for (int i = 0; i < st.Length; i++)
{
d[i] = new Domain(st[i]);
}
return d;
}
///
/// disconnects from the Ivy bus
///
public void Stop()
{
if (stopped)
return;
lock (app)
{
stopped = true;
Ivy.traceProtocol("Ivy", "beginning stopping the bus");
try
{
// stopping the serverThread
if (serverThread != null)
{
app.Stop();
// Wait for Thread to end.
bool term = serverThread.Join(10000);
if (!term && (serverThread != null)) serverThread.Abort();
serverThread = null;
}
// The serverThread might be stopped even before having been created
if (app != null)
app.Stop();
// stopping the IvyWatchers
if (watchers != null)
for (int i = 0; i < watchers.Count; i++)
{
((IvyWatcher)watchers[i]).stop();
}
// stopping the remaining IvyClients
// copy the values in temporary variable to eliminate Thread modifying collection
if (clients.Count != 0)
{
IvyClient[] copyClient;
copyClient = new IvyClient[clients.Count];
lock (clients)
{
clients.CopyTo(copyClient, 0);
}
foreach (IvyClient client in copyClient)
{
client.close(true); // will notify the reading thread
//removeClient(client); already donne in the thread
}
}
}
catch (IOException e)
{
Ivy.traceError("Ivy", "IOexception Stop " + e.Message);
}
Ivy.traceProtocol("Ivy", "the bus should have stopped so far");
}
}
///
/// Send a formated message to someone on the bus
///
///
/// Performs a pattern matching according to everyone's regexps, and sends
/// the results to the relevant ivy agents.
/// There is one thread for each client connected, we could also
/// create another thread each time we send a message.
///
/// A string message format to build the message
/// args used in message format
///
/// the number of messages actually sent
///
public int SendMsg(string format, params object[] args)
{
string msg = string.Format(culture, format, args);
int count = 0;
// an alternate implementation would one sender thread per client
// instead of one for all the clients. It might be a performance issue
lock (clients)
{
// hash message in V4 protocol only
if (ProtocolVersion == 4)
IvyBindingSimple.Prepare(msg);
foreach (IvyClient client in clients)
{
count += client.sendMsg(msg);
}
}
return count;
}
//
public ushort BindMsg(IvyApplicationBinding newbind)
{
newbind.Key = serial++;
lock (bindings) bindings.Add(newbind.Key, newbind);
// notifies the other clients this new regexp
lock (clients)
{
foreach (IvyClient c in clients)
{
c.stream.TokenAddBinding(newbind.Binding, newbind.Key, newbind.FormatedExpression);
}
}
return newbind.Key;
}
///
/// Subscribes to a regular expression.
///
/// a regular expression, groups are done with parenthesis
/// any objects implementing the Ivy.MessageListener
/// The args.
/// the id of the regular expression
///
/// The callback will be executed with
/// the saved parameters of the regexp as arguments when a message will sent
/// by another agent. A program doesn't receive its own messages.
///
//
public ushort BindMsg(string regexp, EventHandler callback, params object[] args)
{
// creates a new binding (regexp,callback)
IvyApplicationBinding newbind;
newbind = new IvyApplicationBinding();
newbind.Binding = BindingType.Regexp;
newbind.Expression = regexp;
newbind.Callback += callback;
newbind.Args = args;
return BindMsg(newbind);
}
///
/// unsubscribes a regular expression
///
/// the id of the regular expression, returned when it was bound
public void UnbindMsg(ushort id)
{
if (!bindings.ContainsKey(id))
{
throw new IvyException("client wants to remove an unexistant regexp " + id);
}
lock (clients)
{
foreach (IvyClient c in clients)
{
c.stream.TokenDelBinding(id);
}
}
lock (bindings) bindings.Remove(id);
}
///
/// unsubscribes a regular expression
///
/// the string for the regular expression
///
/// a boolean, true if the regexp existed, false otherwise or
/// whenever an exception occured during unbinding
///
public bool UnbindMsg(string re)
{
foreach (IvyApplicationBinding bind in bindings.Values)
{
if (bind.Expression == re)
{
try
{
UnbindMsg(bind.Key);
}
catch (IvyException)
{
return false;
}
return true;
}
}
return false;
}
///
/// Subscribes to a simple expression ( msg ar1 arg2 arg3 etc).
///
///
/// The callback will be executed with
/// the saved parameters of the regexp as arguments when a message will sent
/// by another agent. A program doesn't receive its own messages.
///
///
/// a regular expression, groups are done with parenthesis
/// any objects implementing the EventHandler
///
/// the id of the regular expression
///
public int BindSimpleMsg(string expression, EventHandler callback, params object[] args)
{
// creates a new binding (regexp,callback)
IvyApplicationBinding newbind;
newbind = new IvyApplicationBinding();
newbind.Binding = BindingType.Simple;
newbind.Expression = expression;
newbind.Callback += callback;
newbind.Args = args;
return BindMsg(newbind);
}
///
/// Dies the specified target.
///
/// The target.
/// The reason message.
///
public int Die(string target, string message)
{
List v = GetClientsByName(target);
for (int i = 0; i < v.Count; i++)
v[i].stream.TokenDie(0, message);
return v.Count;
}
///
/// Pings the specified target.
///
/// The target.
/// The message.
///
public int Ping(string target, string message)
{
List v = GetClientsByName(target);
for (int i = 0; i < v.Count; i++)
v[i].stream.TokenPing(message);
return v.Count;
}
#if (PocketPC)
internal virtual void FireEvent(EventHandler ev, IvyEventArgs e)
{
if (ev != null)
{
if (parentControl != null)
{
parentControl.Invoke(ev, this, e);
}
else
ev(this, e);
}
}
#else
internal virtual void FireEvent(EventHandler ev, IvyEventArgs e)
{
if (ev != null)
{
if (syncContext != null)
{
SendOrPostCallback update = delegate(object state)
{
IvyEventArgs args = (IvyEventArgs)state;
ev(this, args);
};
syncContext.Post(update, e);
}
else
ev(this, e);
}
}
#endif
internal virtual void OnDirectMessage(IvyEventArgs e)
{
// Copy to a temporary variable to be thread-safe.
EventHandler temp = DirectMessageReceived;
FireEvent(temp,e);
}
internal virtual void OnClientConnected(IvyEventArgs e)
{
// Copy to a temporary variable to be thread-safe.
EventHandler temp = ClientConnected;
FireEvent(temp,e);
}
internal virtual void OnClientDisconnected(IvyEventArgs e)
{
// Copy to a temporary variable to be thread-safe.
EventHandler temp = ClientDisconnected;
try
{
FireEvent(temp,e);
}
#if (!PocketPC)
catch (SynchronizationLockException ex)
{
// protect terminaison
}
#endif
catch (ObjectDisposedException)
{
// protect terminaison
}
}
internal virtual void OnClientAddBinding(IvyEventArgs e)
{
// Copy to a temporary variable to be thread-safe.
EventHandler temp = BindingAdd;
FireEvent(temp,e);
}
internal virtual void OnClientRemoveBinding(IvyEventArgs e)
{
// Copy to a temporary variable to be thread-safe.
EventHandler temp = BindingRemove;
FireEvent(temp,e);
}
internal virtual void OnClientFilterBinding(IvyEventArgs e)
{
// Copy to a temporary variable to be thread-safe.
EventHandler temp = BindingFilter;
FireEvent(temp,e);
}
internal virtual void OnError(IvyEventArgs e)
{
// Copy to a temporary variable to be thread-safe.
EventHandler temp = ErrorMessage;
FireEvent(temp, e);
}
#if (PocketPC)
internal virtual void OnDie(IvyDieEventArgs e)
{
// Copy to a temporary variable to be thread-safe.
EventHandler temp = DieReceived;
if (temp != null)
{
if (parentControl != null)
{
parentControl.Invoke(temp, this, e);
}
else
temp(this, e);
}
}
#else
internal virtual void OnDie(IvyDieEventArgs e)
{
// Copy to a temporary variable to be thread-safe.
EventHandler temp = DieReceived;
if (temp != null)
{
if (syncContext != null)
{
SendOrPostCallback update = delegate(object state)
{
IvyDieEventArgs args = (IvyDieEventArgs)state;
temp(this, args);
};
syncContext.Post(update, e);
}
else
temp(this, e);
}
}
#endif
internal void OnMessage(IvyMessageEventArgs e)
{
IvyApplicationBinding bind = bindings[e.Id];
if (bind == null)
{
throw new IvyException("(callCallback) Not regexp matching id " + e.Id);
}
#if(PocketPC)
bind.Firevent(parentControl, e);
#else
bind.Firevent(syncContext, e);
#endif
}
/*
* removes a client from the list
*/
internal void removeClient(IvyClient c)
{
lock (clients)
{
clients.Remove(c);
}
}
///
/// gives a list of IvyClient(s) with the name given in parameter
///
/// The name of the Ivy agent you're looking for
///
public List GetClientsByName(string name)
{
List v = new List();
foreach (IvyClient ic in clients)
{
if (ic.ApplicationName.CompareTo(name) == 0)
v.Add(ic);
}
return v;
}
/////////////////////////////////////////////////////////////////:
//
// Protected methods
//
/////////////////////////////////////////////////////////////////:
internal void addClient(Socket socket, string appname)
{
if (stopped)
return;
IvyClient client = new IvyClient(this, socket, appname);
lock (clients)
{
clients.Add(client);
}
client.SendBindings();
}
public static IPAddress GetLocalIP()
{
IPAddress returnaddr = null;
//TODO remove ALL reverse DNS search !!!!
IPHostEntry ip = Dns.GetHostEntry(Dns.GetHostName());
foreach (IPAddress addr in ip.AddressList)
{
returnaddr = addr;
if (IPAddress.IsLoopback(addr)) continue;
break;
}
return returnaddr;
}
public static string GetDomain(string domainbus)
{
#if (PocketPC) // TODO integrate in normal version
if (domainbus == null || domainbus.Length == 0)
{
IPAddress addr = GetLocalIP();
//TODO Find Braodcast addr from IP;
byte[] bytes = addr.GetAddressBytes();
if (IPAddress.IsLoopback(addr))
{
// 127.255.255.255
bytes[1] = 255;
bytes[2] = 255;
bytes[3] = 255;
}
else
{
// else assume class C network
// TODO find exact netmask
bytes[3] = 255;
}
IPAddress bcast = new IPAddress(bytes);
domainbus = bcast + ":" + Domain.getPort(domainbus);
}
#else
if (domainbus == null || domainbus.Length == 0 )
{
domainbus = Environment.GetEnvironmentVariable("IVYBUS");
}
if (domainbus == null || domainbus.Length == 0)
{
domainbus = Properties.Settings.Default.IvyBus;
}
#endif
if (domainbus == null || domainbus.Length == 0)
domainbus = DEFAULT_DOMAIN;
return domainbus;
}
/// checks the "validity" of a regular expression. //TODO put in IvyBinding
internal bool CheckRegexp(string exp)
{
bool regexp_ok = true;
// Attention Bug
// ClockStop ClockStart & ^Clock(Start|Pause)
// should Stop to the first parent
if ((sent_messageFilter.Count != 0) && exp.StartsWith("^"))
{
regexp_ok = false;
// extract first word from regexp...
string token = Regex.Replace(exp, @"^\^(?[a-zA-Z_0-9-]+).*", @"${token}");
foreach (string exp_class in sent_messageFilter)
{
if (exp_class.StartsWith(token))
return true;
}
}
return regexp_ok;
}
/*
* prevents two clients from connecting to each other at the same time
* there might still be a lingering bug here, that we could avoid with the
* SchizoToken.
*/
//TODO bug multiple instance Reco car en 127.0.0.1
internal bool checkConnected(IvyClient clnt)
{
if (clnt.AppPort == 0)
return false;
lock (clients)
{
foreach (IvyClient client in clients)
{
if (clnt != client && client.sameClient(clnt))
return true;
}
}
return false;
}
/*
* the service socket thread reader main loop
*/
private void Run()
{
Ivy.traceProtocol("Ivy", "Ivy service Thread started");
bool running = true;
while (running)
{
try
{
Socket socket = app.AcceptSocket();
if (stopped)
break;
// early disconnexion
addClient(socket, "Unkown(waiting for name reception)"); // the peer called me
}
catch (IOException e)
{
Ivy.traceError("Ivy","Ivy server socket reader caught an exception: " + e.Message);
}
catch (SocketException e)
{
Ivy.traceError("Ivy","my server socket has been closed " + e.Message);
running = false;
}
}
Ivy.traceProtocol("Ivy", "Ivy service Thread stopped");
}
[Conditional("DEBUG")]
internal static void traceProtocol(string name, string message)
{
if ( debugProtocol )
Console.WriteLine( "-->{0}<-- {1}", name, message);
}
internal static void traceError(string name, string message)
{
Console.WriteLine("-->{0}<-- {1}", name, message);
}
internal class Domain
{
public virtual string Domainaddr
{
get
{
return domainaddr;
}
}
public virtual int Port
{
get
{
return port;
}
}
private string domainaddr;
private int port;
public Domain(string net)
{
this.domainaddr = getDomain(net);
this.port = getPort(net);
}
public Domain(string domainaddr, int port)
{
this.domainaddr = domainaddr; this.port = port;
}
public override string ToString()
{
return domainaddr + ":" + port;
}
public static string getDomain(string net)
{
int sep_index = net.LastIndexOf(":");
if (sep_index != -1)
{
net = net.Substring(0, (sep_index) - (0));
}
try
{
net += ".255.255.255";
Regex exp = new Regex("^(\\d+\\.\\d+\\.\\d+\\.\\d+).*");
net = exp.Replace(net, "$1");
}
catch (ArgumentException e)
{
Ivy.traceError("Ivy","Bad broascat addr " + net + "error " + e.Message);
return null;
}
return net;
}
public static int getPort(string net)
{
if (net == null) return Ivy.DEFAULT_PORT;
int sep_index = net.LastIndexOf(":");
int port = (sep_index == -1) ? Ivy.DEFAULT_PORT : Int32.Parse(net.Substring(sep_index + 1));
return port;
}
}
public static bool ValidatingDomain(string p)
{
// domain if of the form ip1[:port][,ip2[:port]] with ip of the form n1.n2.n3.n4
return Regex.IsMatch(p, @"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+");
}
#region ISupportInitialize Members
public void BeginInit()
{
}
public void EndInit()
{
// TODO ugly should be added directly the bindings Dictionary !
foreach (IvyApplicationBinding bind in app_bindings)
{
BindMsg(bind);
}
}
#endregion
}
}