summaryrefslogtreecommitdiff
path: root/Ivy/Ivy.cs
diff options
context:
space:
mode:
authorfcolin2007-02-01 12:04:16 +0000
committerfcolin2007-02-01 12:04:16 +0000
commit92757a8d629812303ff3665343bd098917cca611 (patch)
treecd995c9863aa6fc4c32ec5ce247e4c3119eb44a3 /Ivy/Ivy.cs
parent98ab5d0164040427f7c554febae125686284e2a7 (diff)
downloadivy-csharp-92757a8d629812303ff3665343bd098917cca611.zip
ivy-csharp-92757a8d629812303ff3665343bd098917cca611.tar.gz
ivy-csharp-92757a8d629812303ff3665343bd098917cca611.tar.bz2
ivy-csharp-92757a8d629812303ff3665343bd098917cca611.tar.xz
modification structure svn
Diffstat (limited to 'Ivy/Ivy.cs')
-rw-r--r--Ivy/Ivy.cs1183
1 files changed, 1183 insertions, 0 deletions
diff --git a/Ivy/Ivy.cs b/Ivy/Ivy.cs
new file mode 100644
index 0000000..1c38396
--- /dev/null
+++ b/Ivy/Ivy.cs
@@ -0,0 +1,1183 @@
+/// 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;
+
+
+ /// <summary> The Main bus Class
+ /// </summary>
+ ///
+#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 */
+ /// <summary>fires when a new client connect to the bus</summary>
+ public event EventHandler<IvyEventArgs> ClientConnected;
+ /// <summary>fires when a client discconnect from the bus</summary>
+ public event EventHandler<IvyEventArgs> ClientDisconnected;
+ /// <summary>fires when a client receive a direct message from another client</summary>
+ public event EventHandler<IvyEventArgs> DirectMessageReceived;
+ /// <summary>fires when somebody ask for killing every client on the bus</summary>
+ public event EventHandler<IvyDieEventArgs> DieReceived;
+ /// <summary>fires when a client receive a add binding from another client</summary>
+ public event EventHandler<IvyEventArgs> BindingAdd;
+ /// <summary>fires when a client receive a remove binding from another client</summary>
+ public event EventHandler<IvyEventArgs> BindingRemove;
+ /// <summary>fires when a client receive a binding from another client and it as been filtered </summary>
+ public event EventHandler<IvyEventArgs> BindingFilter;
+ /// <summary>fires when a client receive a remove binding from another client</summary>
+ public event EventHandler<IvyEventArgs> 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;
+ }
+
+ }
+
+ /// <summary>IvyClients accesses the list of the connected clients</summary>
+#if (!PocketPC)
+ [Browsable(false)]
+#endif
+ public List<IvyClient> IvyClients
+ {
+ get
+ {
+ return clients;
+ }
+
+ }
+
+ /// <summary>AppName the application name</summary>
+
+#if (!PocketPC)
+ [Category("Ivy")]
+ [Bindable(true)]
+#endif
+ [DefaultValue(null)]
+ public string AppName
+ {
+ set
+ {
+ appName = value;
+ }
+ get
+ {
+ return appName;
+ }
+
+ }
+ /// <summary>AppId the Application Unique ID</summary>
+
+#if (!PocketPC)
+ [Browsable(false)]
+#endif
+ public string AppId
+ {
+ get
+ {
+ return applicationUniqueId;
+ }
+
+ }
+ /// <summary>AppPriority the Application Priority: the clients list is sorted against priority</summary>
+#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;
+ }
+
+ }
+
+ /// <summary>IsRunning is the bus Started and connected ?</summary>
+
+#if (!PocketPC)
+ [Browsable(false)]
+#endif
+ public bool IsRunning
+ {
+ get
+ {
+ return !stopped;
+ }
+
+ }
+ ///<summary>SentMessageClasses the first word token of sent messages
+ ///<remarks> optimise the parsing process when sending messages </remarks>
+ ///</summary>
+#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<string> SentMessageFilter
+ {
+ get
+ {
+ return sent_messageFilter;
+ }
+ }
+ /// <summary>ReadyMessage message send when Application receive all the regexp at the connection of the client</summary>
+
+#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<IvyApplicationBinding> 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<IvyWatcher> watchers;
+ private volatile Thread serverThread; // to ensure quick communication of the end
+
+ internal Dictionary<int, IvyApplicationBinding> bindings;
+ //TODO should be remove samve as above
+ private List<IvyApplicationBinding> app_bindings;
+ private List<IvyClient> clients;
+ private List<string> 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
+
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="T:Ivy"/> class.
+ /// </summary>
+ public Ivy()
+ {
+#if (!PocketPC)
+ syncContext = SynchronizationContext.Current;
+#endif
+ clients = new List<IvyClient>();
+ bindings = new Dictionary<int, IvyApplicationBinding>();
+ app_bindings = new List<IvyApplicationBinding>();
+ sent_messageFilter = new List<string>();
+#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);
+ }
+ /// <summary>
+ /// Readies the structures for the software bus connexion.
+ /// </summary>
+ /// <example> This sample shows how to start working with Ivy.
+ /// <code>
+ /// Ivy bus = new Ivy("Dummy agent","ready");
+ /// bus.bindMsg("(.*)",myMessageListener);
+ /// bus.start(null);
+ /// </code>
+ /// How to send & receive:
+ /// the Ivy agent A performs <c>b.bindMsg("^Hello (*)",cb);</c>
+ /// the Ivy agent B performs <c>b2.sendMsg("Hello world");</c>
+ /// a thread in A will run the callback cb with its second argument set
+ /// to a array of string, with one single element, "world"
+ /// </example>
+ /// <remarks>
+ /// the real work begin in the start() method
+ /// </remarks>
+ /// <seealso cref=" Ivy.start"/>
+ /// <param name='name'>The name of your Ivy agent on the software bus
+ /// </param>
+ /// <param name='message'>The hellow message you will send once ready
+ /// </param>
+ 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<IvyMessageEventArgs> handler;
+#if (PocketPC)
+ //Createdelegate mydlg = new Createdelegate(m, null, EventArgs.Empty);
+ //bindMsg(attr.GetExpression(null), mydlg);
+ handler = null;
+#else
+ handler = (EventHandler<IvyMessageEventArgs>)Delegate.CreateDelegate(typeof(EventHandler<IvyMessageEventArgs>), 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<IvyMessageEventArgs> handler;
+#if (PocketPC)
+ handler = null; // TODO
+#else
+ handler = (EventHandler<IvyMessageEventArgs>)Delegate.CreateDelegate(typeof(EventHandler<IvyMessageEventArgs>), 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);
+ }
+
+ }
+ /// <summary>
+ /// connects the Ivy bus to a domain or list of domains.
+ /// </summary>
+ /// <param name="domainbus">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.</param>
+ /// <remarks>
+ /// 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.
+ /// </remarks>
+ 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<IvyWatcher>();
+
+ 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;
+ }
+
+ /// <summary>
+ /// disconnects from the Ivy bus
+ /// </summary>
+ 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");
+ }
+ }
+
+
+ /// <summary>
+ /// Send a formated message to someone on the bus
+ /// </summary>
+ /// <remarks>
+ /// 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.
+ /// </remarks>
+ /// <param name='format'>A string message format to build the message
+ /// <param name='args'>args used in message format
+ /// <returns>
+ /// the number of messages actually sent
+ /// </returns>
+ 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;
+ }
+ /// <summary>
+ /// Subscribes to a regular expression.
+ /// </summary>
+ /// <param name="regexp">a regular expression, groups are done with parenthesis</param>
+ /// <param name="callback">any objects implementing the Ivy.MessageListener</param>
+ /// <param name="args">The args.</param>
+ /// <returns>the id of the regular expression</returns>
+ /// <remarks>
+ /// 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.
+ /// </remarks>
+ //
+ public ushort BindMsg(string regexp, EventHandler<IvyMessageEventArgs> 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);
+ }
+
+ /// <summary>
+ /// unsubscribes a regular expression
+ /// </summary>
+ /// <param name="id">the id of the regular expression, returned when it was bound</param>
+ 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);
+ }
+
+ /// <summary>
+ /// unsubscribes a regular expression
+ /// </summary>
+ /// <param name="re">the string for the regular expression</param>
+ /// <returns>
+ /// a boolean, true if the regexp existed, false otherwise or
+ /// whenever an exception occured during unbinding
+ /// </returns>
+ 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;
+ }
+
+
+ /// <summary>
+ /// Subscribes to a simple expression ( msg ar1 arg2 arg3 etc).
+ /// </summary>
+ /// <remarks>
+ /// 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.
+ /// </remarks>
+ ///
+ /// <param name='regexp'>a regular expression, groups are done with parenthesis
+ /// <param name='callback'>any objects implementing the EventHandler<IvyMessageEventArgs>
+ /// <returns>
+ /// the id of the regular expression
+ /// </returns>
+ public int BindSimpleMsg(string expression, EventHandler<IvyMessageEventArgs> 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);
+ }
+ /// <summary>
+ /// Dies the specified target.
+ /// </summary>
+ /// <param name="target">The target.</param>
+ /// <param name="message">The reason message.</param>
+ /// <returns></returns>
+ public int Die(string target, string message)
+ {
+ List<IvyClient> v = GetClientsByName(target);
+ for (int i = 0; i < v.Count; i++)
+ v[i].stream.TokenDie(0, message);
+ return v.Count;
+ }
+ /// <summary>
+ /// Pings the specified target.
+ /// </summary>
+ /// <param name="target">The target.</param>
+ /// <param name="message">The message.</param>
+ /// <returns></returns>
+ public int Ping(string target, string message)
+ {
+ List<IvyClient> 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<IvyEventArgs> ev, IvyEventArgs e)
+ {
+ if (ev != null)
+ {
+ if (parentControl != null)
+ {
+ parentControl.Invoke(ev, this, e);
+ }
+ else
+ ev(this, e);
+ }
+ }
+#else
+ internal virtual void FireEvent(EventHandler<IvyEventArgs> 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<IvyEventArgs> temp = DirectMessageReceived;
+ FireEvent(temp,e);
+ }
+ internal virtual void OnClientConnected(IvyEventArgs e)
+ {
+ // Copy to a temporary variable to be thread-safe.
+ EventHandler<IvyEventArgs> temp = ClientConnected;
+ FireEvent(temp,e);
+ }
+ internal virtual void OnClientDisconnected(IvyEventArgs e)
+ {
+ // Copy to a temporary variable to be thread-safe.
+ EventHandler<IvyEventArgs> 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<IvyEventArgs> temp = BindingAdd;
+ FireEvent(temp,e);
+ }
+ internal virtual void OnClientRemoveBinding(IvyEventArgs e)
+ {
+ // Copy to a temporary variable to be thread-safe.
+ EventHandler<IvyEventArgs> temp = BindingRemove;
+ FireEvent(temp,e);
+ }
+ internal virtual void OnClientFilterBinding(IvyEventArgs e)
+ {
+ // Copy to a temporary variable to be thread-safe.
+ EventHandler<IvyEventArgs> temp = BindingFilter;
+ FireEvent(temp,e);
+ }
+ internal virtual void OnError(IvyEventArgs e)
+ {
+ // Copy to a temporary variable to be thread-safe.
+ EventHandler<IvyEventArgs> temp = ErrorMessage;
+ FireEvent(temp, e);
+ }
+#if (PocketPC)
+ internal virtual void OnDie(IvyDieEventArgs e)
+ {
+ // Copy to a temporary variable to be thread-safe.
+ EventHandler<IvyDieEventArgs> 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<IvyDieEventArgs> 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);
+ }
+ }
+
+
+ /// <summary>
+ /// gives a list of IvyClient(s) with the name given in parameter
+ /// </summary>
+ /// <param name="name">The name of the Ivy agent you're looking for</param>
+ /// <returns></returns>
+ public List<IvyClient> GetClientsByName(string name)
+ {
+ List<IvyClient> v = new List<IvyClient>();
+ 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, @"^\^(?<token>[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
+
+ }
+} \ No newline at end of file