/// François-Régis Colin /// http://www.tls.cena.fr/products/ivy/ /// * /// (C) CENA /// * /// [assembly: System.CLSCompliant(true)] 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.Diagnostics; using System.Collections.ObjectModel; using IvyBus.Properties; /// The Main bus Class /// /// public class Ivy : IDisposable { /* Event */ /// /// raise when an new IvyClient is connected /// public event EventHandler ClientConnected; /// /// raise when an new IvyClient is disconnected /// public event EventHandler ClientDisconnected; /// /// raise when a direct message is received /// public event EventHandler DirectMessageReceived; /// /// raise when some IvyClient ask me to die /// public event EventHandler DieReceived; /// /// raise when an IvyClient register a binding /// public event EventHandler BindingAdd; /// /// raise when an IvyClient unregister a binding /// public event EventHandler BindingRemove; /// /// raise when an IvyClient binding is filteredout /// public event EventHandler BindingFilter; /// /// raise when an IvyClient changer a binding /// public event EventHandler BindingChange; /// /// raise when an error message is received /// public event EventHandler ErrorMessage; /// /// Flag for displaying internal debug message of the protocol intrinsics /// public static bool DebugProtocol { get { return debugProtocol; } set { debugProtocol = value; } } /// /// Language for the Ivy SendMsg formating default en-US /// ie: send point on float value sent /// public CultureInfo Culture { get { return culture; } set { culture = value; } } /// IvyClients accesses the list of the connected clients public ReadOnlyCollection IvyClients { get { return new ReadOnlyCollection(clients); } } /// AppName the application name public string AppName { set { appName = value; } get { return appName; } } /// /// AppId the Application Unique ID /// public string AppId { get { return applicationUniqueId; } } /// /// the Ivy Protocol version /// public int ProtocolVersion { get { return protocolVersion; } } /// /// IsSynchronous is the bus Synchronous of the main thread ( ie callback running in the main thread) ? /// private bool synchronous = true; public bool Synchronous { set { synchronous = value && syncContext != null; } get { return synchronous; } } /// /// IsRunning is the bus Started and connected ? /// public bool IsRunning { get { return !stopped; } } ///SentMessageClasses the first word token of sent messages /// optimise the parsing process when sending messages /// public Collection SentMessageFilter { get { return sent_messageFilter; } } /// ReadyMessage message send when Application receive all the regexp at the connection of the client public string ReadyMessage { get { return ready_message; } set { ready_message = value; } } 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 DefaultPort = 2010; /// the domain for the UDP rendez vous private static readonly string DefaultDomain = "127.0.0.1:" + DefaultPort; internal int protocolVersion = 3; private static bool debugProtocol; // false by default runtime private static int serial = -1; /* an unique ID for each regexp */ // -1 to start numbering at 0 private MyTcpListener app; private List watchers; private Thread serverThread; // to ensure quick communication of the end private AutoResetEvent tcplistener_running; private bool ipv6; /// /// the Ivy IP version to use /// public bool IpV6 { get { return ipv6; } } /// /// the Ivy Assembly version /// static public Version Version { get { return Assembly.GetExecutingAssembly().GetName().Version; } } // Class representing Ivy Binding receive from client internal class IvyClientBinding { internal IvyBindingBase binding; internal Dictionary> client_bindind_ids; } // The Application bindings internal Dictionary bindings; // the clients bindings private Dictionary client_bindings; private List clients; private Collection sent_messageFilter; private bool stopped = true; internal int applicationPort; /* Application port number */ internal IPAddress applicationHost; /* Application host number */ internal string applicationUniqueId; /* identifier Application unique timestamp-ipaddress-port */ private string ready_message; private CultureInfo culture = new CultureInfo("en-us"); internal static Encoding ivyEncoding = Encoding.GetEncoding("iso-8859-15"); // ASCII 8 bits iso-8859-2 // for synchronous event #if (PocketPC) private System.Windows.Forms.ContainerControl parentControl; #else private readonly SynchronizationContext syncContext; #endif /// /// Initializes a new instance of the Ivy Bus. /// Readies the structures for the software bus connexion. /// public Ivy() { this.ipv6 = false; clients = new List(); bindings = new Dictionary(); client_bindings = new Dictionary(); sent_messageFilter = new Collection(); #if (!PocketPC) syncContext = SynchronizationContext.Current; if (syncContext == null) synchronous = false; 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 ) BindAttribute(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 readyMessage) : this() { appName = name; this.ready_message = readyMessage; // get binding from Attribute IvyBinding //TODO Autobinding attribute Assembly assembly = Assembly.GetCallingAssembly(); if (assembly != this.GetType().Assembly) BindAttribute(assembly); } internal void AddBinding(int id, IvyClient clnt, IvyBindingBase bindexp) { bool change = false; IvyClientBinding clientbind; lock (client_bindings) { if (client_bindings.ContainsKey(bindexp.Expression)) { // check if id exits to remove it clientbind = client_bindings[bindexp.Expression]; foreach (IvyClientBinding clbind in client_bindings.Values) { lock (clbind.client_bindind_ids) { if (clbind.client_bindind_ids.ContainsKey(clnt)) { if (clbind.client_bindind_ids[clnt].Contains(id)) { // remove from old clbind.client_bindind_ids[clnt].Remove(id); // add to new later change = true; //mark to call right handler break; } } } } } else { clientbind = new IvyClientBinding(); clientbind.binding = bindexp; clientbind.client_bindind_ids = new Dictionary>(); clientbind.client_bindind_ids[clnt] = new List(); client_bindings[bindexp.Expression] = clientbind; } } lock (clientbind.client_bindind_ids) { if (!clientbind.client_bindind_ids.ContainsKey(clnt)) { clientbind.client_bindind_ids[clnt] = new List(); } clientbind.client_bindind_ids[clnt].Add(id); } if (change) { OnClientChangeBinding(new IvyEventArgs(clnt, id, bindexp.Expression)); } else { OnClientAddBinding(new IvyEventArgs(clnt, id, bindexp.Expression)); } } internal void DelBinding(int id, IvyClient clnt) { IvyClientBinding clientbind = null; lock (client_bindings) { foreach (IvyClientBinding clbind in client_bindings.Values) { lock (clbind.client_bindind_ids) { if (clbind.client_bindind_ids.ContainsKey(clnt)) { clbind.client_bindind_ids[clnt].Remove(id); clientbind = clbind; break; } } } if (clientbind != null) { lock (clientbind.client_bindind_ids) { if (clientbind.client_bindind_ids.Count == 0) { client_bindings.Remove(clientbind.binding.Expression); } } } } if (clientbind != null ) OnClientRemoveBinding(new IvyEventArgs(clnt, id, clientbind.binding.Expression)); } /// /// Automatic binding on static method of a Type /// /// public void BindAttribute(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.GetFormattedExpression(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.GetFormattedExpression(null), handler); } } } /// /// Autobinding on instance method /// /// public void BindAttribute(object target) { if (target == null) return; Type type = target.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(Resources.Ivy, "BindAttibute " + attr.GetFormattedExpression(target) + "' to Method " + m.Name); EventHandler handler; #if (PocketPC) handler = null; // TODO #else handler = (EventHandler)Delegate.CreateDelegate(typeof(EventHandler), target, m); #endif BindMsg(attr.GetFormattedExpression(target), handler); } } } // Autobinding on IvyBindingAttribute method /// /// Automatic binding of IvyBindAttribute in an assembly /// /// public void BindAttribute(Assembly target) { if (target != null) { foreach (Type typ in target.GetTypes()) { BindAttribute(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) { if (string.IsNullOrEmpty(AppName)) { AppName = "LazyDevelopper"; } // check for ReadyMessage empty if (string.IsNullOrEmpty(ready_message)) { if ( string.IsNullOrEmpty ( Properties.Settings.Default.ReadyMessage ) ) ready_message = AppName + " READY"; else ready_message = Properties.Settings.Default.ReadyMessage; } domainBus = GetDomain(domainBus); // check for IPV6 mode Domain[] d = parseDomains(domainBus); IPAddress group = IPAddress.Parse(d[0].Domainaddr); ipv6 = group.IsIPv6Multicast; try { long seed = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond; Random rand = new Random( (int)seed ); app = new MyTcpListener(ipv6 ? IPAddress.IPv6Any :IPAddress.Any, 0); app.Start(); // should be started before getting port applicationHost = LocalIP; applicationPort = ((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(); // 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, ipv6 ); watchers.Add(watcher); } serverThread = new Thread(new ThreadStart(this.TcpListener)); serverThread.Name = "Ivy Tcp Server Thread"; stopped = false; tcplistener_running = new AutoResetEvent(false); serverThread.Start(); // Wait for readyness tcplistener_running.WaitOne(); // 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(); } } 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(Resources.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; lock (clients) { copyClient = new IvyClient[clients.Count]; clients.CopyTo(copyClient); } 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(Resources.Ivy, "IOexception Stop " + e.Message); } Ivy.traceProtocol(Resources.Ivy, "the bus should have stopped so far"); } } /// /// join the main thread of Ivy ( ie wait until stopped ) /// /// /// Performs a join on the Principal Thread. /// /// Number of millisecondes to wait before the Thread Stop. /// /// true if the thread stopped; false if it did not stop after the expiry of the period specified by the parameter millisecondsTimeout /// public bool Join(int millisecondsTimeout) { return serverThread.Join(millisecondsTimeout); } /// /// Block the calling Thread until Ivy Stop /// /// /// Performs a join on the Principal Thread. /// public void MainLoop() { this.Join(Timeout.Infinite); } /// /// 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) { // send to everybody string msg = string.Format(culture, format, args); int count = 0; // hash message in V4 protocol only if (ProtocolVersion == 4) IvyBindingSimple.Prepare(msg); // an alternate implementation would one sender thread per client // instead of one for all the clients. It might be a performance issue // Tentative of using BeginInvoke EndInvoke is slower lock (client_bindings) { foreach (IvyClientBinding clbind in client_bindings.Values) { string[] matches = clbind.binding.Match(msg); if (matches != null) { lock (clbind.client_bindind_ids) { foreach (KeyValuePair> assoc in clbind.client_bindind_ids) { foreach (ushort id in assoc.Value) { count += assoc.Key.sendMsg(id, matches); } } } } } } return count; } internal int SendMsgToClient(IvyClient clnt, string format, params object[] args) { string msg = string.Format(culture, format, args); int count = 0; // hash message in V4 protocol only if (ProtocolVersion == 4) IvyBindingSimple.Prepare(msg); // an alternate implementation would one sender thread per client // instead of one for all the clients. It might be a performance issue lock (client_bindings) { foreach (IvyClientBinding clbind in client_bindings.Values) { string[] matches = clbind.binding.Match(msg); if (matches != null) { lock (clbind.client_bindind_ids) { if (clbind.client_bindind_ids.ContainsKey(clnt)) { List ids = clbind.client_bindind_ids[clnt]; foreach (ushort id in ids) { count += clnt.sendMsg(id, matches); } } } } } } return count; } /// /// Make a binding to a message using regular expresssion /// /// /// public int BindMsg(IvyApplicationBinding newBinding) { newBinding.Key = Interlocked.Increment(ref serial); lock (bindings) bindings.Add(newBinding.Key, newBinding); // notifies the other clients this new regexp lock (clients) { foreach (IvyClient c in clients) { c.stream.TokenAddBinding(newBinding.Binding, newBinding.Key, newBinding.FormattedExpression); } } return newBinding.Key; } /// /// Subscribes to a regular expression. /// /// a regular expression, groups are done with parenthesis /// any objects implementing the EventHandler /// 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. /// the expression cant containt some dynamics argument replacement of the form %number[:objectFormat]% /// the string will be replaced by arg[number].ToString(objectFormat) /// // public int BindMsg(string expression, EventHandler callback, params object[] args) { // creates a new binding (regexp,callback) IvyApplicationBinding newbind; newbind = new IvyApplicationBinding(BindingType.RegularExpression, expression, args); newbind.Callback += callback; return BindMsg(newbind); } /// /// unsubscribes a regular expression /// /// the id of the regular expression, returned when it was bound public void UnbindMsg(int id) { if (!bindings.ContainsKey(id)) { throw new IvyException("client wants to remove an unexistant regexp " + id); } lock (clients) { foreach (IvyClient c in clients) { try { if ( c.stream != null ) c.stream.TokenDelBinding(id); } catch (IOException) { // client closed ignore it } } } lock (bindings) bindings.Remove(id); } /// /// Change a binding already registred /// /// /// /// public int ChangeMsg(int id, string expression) { IvyApplicationBinding newbind; if (!bindings.ContainsKey(id)) { throw new IvyException("client wants to change an unexistant regexp " + id); } // change binding (regexp,callback) lock (bindings) { newbind = bindings[id]; newbind.Expression = expression; } // notifies the other clients this new regexp lock (clients) { foreach (IvyClient c in clients) { c.stream.TokenAddBinding(newbind.Binding, newbind.Key, newbind.FormattedExpression); } } return 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(BindingType.Simple, expression, args); newbind.Callback += callback; return BindMsg(newbind); } /// /// Dies the specified target. /// /// The target. /// The reason message. /// public int Die(string target, string message) { ReadOnlyCollection v = GetClientsByName(target); foreach (IvyClient clnt in v) clnt.stream.TokenDie(0, message); return v.Count; } /// /// Pings the specified target. /// /// The target. /// The message. /// public int Ping(string target, string message) { int id = (int)DateTime.Now.Ticks; ReadOnlyCollection v = GetClientsByName(target); foreach (IvyClient clnt in v) { clnt.stream.TokenPing(id, message); } return v.Count; } internal virtual void FireEvent(EventHandler ev, IvyEventArgs e) { if (ev != null) { #if (PocketPC) if (parentControl != null) { parentControl.Invoke(ev, this, e); } #else if (synchronous) { SendOrPostCallback update = delegate(object state) { IvyEventArgs args = (IvyEventArgs)state; ev(this, args); }; syncContext.Post(update, e); } #endif else ev(this, e); } } 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) { // 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 OnClientChangeBinding(IvyEventArgs e) { // Copy to a temporary variable to be thread-safe. EventHandler temp = BindingChange; 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) { if (!bindings.ContainsKey( e.Id)) { throw new IvyException("(callCallback) Not regexp matching id " + e.Id); } IvyApplicationBinding bind = bindings[e.Id]; #if(PocketPC) bind.Firevent(parentControl, e); #else bind.Firevent(syncContext,synchronous, e); #endif } /* * removes a client from the list */ internal void removeClient(IvyClient c) { lock (clients) { clients.Remove(c); } List purge_bind = new List(); lock (client_bindings) { foreach (IvyClientBinding clbind in client_bindings.Values) { lock (clbind.client_bindind_ids) { clbind.client_bindind_ids.Remove(c); if ( clbind.client_bindind_ids.Count == 0 ) purge_bind.Add( clbind.binding.Expression); } } // remove Binding if no client foreach ( string expr in purge_bind ) { client_bindings.Remove(expr); } } } /// /// gives a list of IvyClient(s) with the name given in parameter /// /// The name of the Ivy agent you're looking for /// public ReadOnlyCollection GetClientsByName(string name) { List v = new List(); foreach (IvyClient ic in clients) { if (ic.ApplicationName.CompareTo(name) == 0) v.Add(ic); } return new ReadOnlyCollection(v); } /////////////////////////////////////////////////////////////////: // // Protected methods // /////////////////////////////////////////////////////////////////: internal void addClient(IvyClient client) { if (stopped) return; lock (clients) { clients.Add(client); } } /// /// return the first non loopback adress /// on a one network interface machine it will be my IP adresse /// public static IPAddress LocalIP { get { 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; } } /// /// Get the FDQN Domain from a string /// /// /// 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 = DefaultDomain; 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. */ internal IvyClient checkConnected(IvyClient clnt) { IvyClient sameClnt = null; if (clnt.AppPort == 0) { Console.WriteLine(" client {0} port == 0 ", clnt.appName); return sameClnt; } lock (clients) { foreach (IvyClient client in clients) { if (clnt == client) continue; // SKIP himself if (client.sameIvyClient(clnt)) { sameClnt = client; break; } } } return sameClnt; } /* * the service socket thread reader main loop */ private void TcpListener() { Ivy.traceProtocol(Resources.Ivy, "Ivy service Thread started"); bool running = true; tcplistener_running.Set(); while (running) { try { Socket socket = app.AcceptSocket(); // early disconnexion if (stopped) break; // the peer called me IvyClient client = new IvyClient(this, socket, "Unkown(waiting for name reception)",0); client.SendBindings(); //TODO in a Thread or wait for the peer to sent his before sending mine } catch (IOException e) { Ivy.traceError(Resources.Ivy,Resources.IvyIOException + e.Message); } catch (SocketException e) { Ivy.traceError(Resources.Ivy,Resources.IvySocketException + e.Message); running = false; } } Ivy.traceProtocol(Resources.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( getDomain(net),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 { if (!net.Contains("::")) // if not IPV6 multicast paste with 255 { net += ".255.255.255"; Regex exp = new Regex("^(\\d+\\.\\d+\\.\\d+\\.\\d+).*"); net = exp.Replace(net, "$1"); } } catch (ArgumentException e) { Ivy.traceError(Resources.Ivy,Resources.BadBroadcast + net + "error " + e.Message); return null; } return net; } public static int getPort(string net) { if (net == null) { return Ivy.DefaultPort; } int sep_index = net.LastIndexOf(":"); int port = (sep_index == -1) ? Ivy.DefaultPort : Int32.Parse(net.Substring(sep_index + 1)); return port; } } /// /// check Validity of Ivy Bus Domain /// /// /// public static bool ValidatingDomain(string domain) { IPAddress result; bool valid = false; string addr = Domain.getDomain(domain); try { int port = Domain.getPort(domain); if (port < IPEndPoint.MinPort || port > IPEndPoint.MaxPort) return false; } catch (OverflowException e) { return false; } if (String.IsNullOrEmpty(addr)) { // address wasnt provided so return false valid = false; } else { //use TryParse to see if this is a //valid ip address. TryParse returns a //boolean based on the validity of the //provided address, so assign that value //to our boolean variable valid = IPAddress.TryParse(addr, out result); } return valid; } #region IDisposable Membres // needed by tcplistener_running public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (disposing) { // Free other state (managed objects). if (tcplistener_running != null) { tcplistener_running.Close(); tcplistener_running = null; } } // Free your own state (unmanaged objects). // Set large fields to null. } #endregion } }