From 8d10e8bbd1e19adc7c70e1101dbb69c213c910dd Mon Sep 17 00:00:00 2001 From: fcolin Date: Fri, 22 Aug 2008 16:44:01 +0000 Subject: optimisation for parsing same regular expression from multiple client using fxCop for code beauty fix bug on concurrent connect --- Ivy/Ivy.cs | 556 +++++++++++++++++++++++++----------- Ivy/Ivy.csproj | 57 +++- Ivy/IvyApplicationBinding.cs | 69 +++-- Ivy/IvyBinding.cs | 39 ++- Ivy/IvyBindingAttribute.cs | 47 ++- Ivy/IvyClient.cs | 373 +++++++++++++----------- Ivy/IvyEventArgs.cs | 16 +- Ivy/IvyException.cs | 26 +- Ivy/IvyProtocol.cs | 29 +- Ivy/IvyTCPStreamV3.cs | 170 ++++++----- Ivy/IvyTCPStreamV4.cs | 107 +++---- Ivy/IvyUDPStream.cs | 72 +++-- Ivy/IvyUDPStreamV3.cs | 29 +- Ivy/IvyUDPStreamV4.cs | 20 +- Ivy/IvyWatcher.cs | 109 ++++--- Ivy/Properties/AssemblyInfo.cs | 6 +- Ivy/Properties/Settings.Designer.cs | 4 +- 17 files changed, 1082 insertions(+), 647 deletions(-) (limited to 'Ivy') diff --git a/Ivy/Ivy.cs b/Ivy/Ivy.cs index 14a3918..bfef8ab 100644 --- a/Ivy/Ivy.cs +++ b/Ivy/Ivy.cs @@ -4,7 +4,7 @@ /// (C) CENA /// * /// - +[assembly: System.CLSCompliant(true)] namespace IvyBus { using System; @@ -21,11 +21,12 @@ namespace IvyBus using System.Text; using System.Reflection; using System.Diagnostics; + using System.Collections.ObjectModel; + using IvyBus.Properties; /// The Main bus Class /// /// - public class Ivy { /* Event */ @@ -36,6 +37,7 @@ namespace IvyBus public event EventHandler BindingAdd; public event EventHandler BindingRemove; public event EventHandler BindingFilter; + public event EventHandler BindingChange; public event EventHandler ErrorMessage; @@ -67,11 +69,11 @@ namespace IvyBus /// IvyClients accesses the list of the connected clients - public List IvyClients + public ReadOnlyCollection IvyClients { get { - return clients; + return new ReadOnlyCollection(clients); } } @@ -100,28 +102,7 @@ namespace IvyBus } } - /// AppPriority the Application Priority: the clients list is sorted against priority - - public ushort AppPriority - { - set - { - applicationPriority = value; - lock (clients) - { - foreach (IvyClient client in clients) - { - client.stream.TokenApplicationId(applicationPriority, AppId); - } - } - - } - get - { - return applicationPriority; - } - - } + public int ProtocolVersion { @@ -146,7 +127,7 @@ namespace IvyBus /// optimise the parsing process when sending messages /// - public List SentMessageFilter + public Collection SentMessageFilter { get { @@ -175,26 +156,37 @@ namespace IvyBus /// 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 - public const ushort DEFAULT_PRIORITY = 100; + internal const ushort DefaultPort = 2010; /// the domain for the UDP rendez vous - private static readonly string DEFAULT_DOMAIN = "127.255.255.255:" + DEFAULT_PORT; + private static readonly string DefaultDomain = "127.255.255.255:" + DefaultPort; 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 static int 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 + private Thread serverThread; // to ensure quick communication of the end + private AutoResetEvent tcplistener_running; + 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 List sent_messageFilter; + private Collection sent_messageFilter; private bool stopped = true; - internal ushort applicationPort; /* Application port number */ + internal int 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"); internal static Encoding ivyEncoding = Encoding.GetEncoding("iso-8859-15"); // ASCII 8 bits iso-8859-2 @@ -212,13 +204,12 @@ namespace IvyBus /// public Ivy() { -#if (!PocketPC) - syncContext = SynchronizationContext.Current; -#endif clients = new List(); bindings = new Dictionary(); - sent_messageFilter = new List(); + client_bindings = new Dictionary(); + sent_messageFilter = new Collection(); #if (!PocketPC) + syncContext = SynchronizationContext.Current; debugProtocol = Properties.Settings.Default.IvyDebug; protocolVersion = Properties.Settings.Default.IvyProtocolVersion; #endif @@ -230,7 +221,7 @@ namespace IvyBus #endif Assembly assembly = Assembly.GetCallingAssembly(); if ( assembly != this.GetType().Assembly ) - BindAttibute(assembly); + BindAttribute(assembly); } /// @@ -256,21 +247,112 @@ namespace IvyBus /// /// The hellow message you will send once ready /// - public Ivy(string name, string rdy_message) + public Ivy(string name, string readyMessage) : this() { appName = name; - ready_message = rdy_message; + this.ready_message = readyMessage; // get binding from Attribute IvyBinding //TODO Autobinding attribute Assembly assembly = Assembly.GetCallingAssembly(); if (assembly != this.GetType().Assembly) - BindAttibute(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)); + + } // Autobinding on static method - public void BindAttibute(Type type) + public void BindAttribute(Type type) { if (type == null) return; //Get instance of the IvyBindingAttribute. @@ -279,7 +361,7 @@ namespace IvyBus foreach (IvyBindingAttribute attr in Attribute.GetCustomAttributes(m, typeof(IvyBindingAttribute))) { //TODO check paramater type MessageHandler - Debug.WriteLine("IvyBinding '" + attr.GetExpression(null) + "' to Method " + m.Name); + Debug.WriteLine("IvyBinding '" + attr.GetFormattedExpression(null) + "' to Method " + m.Name); EventHandler handler; #if (PocketPC) //Createdelegate mydlg = new Createdelegate(m, null, EventArgs.Empty); @@ -288,41 +370,43 @@ namespace IvyBus #else handler = (EventHandler)Delegate.CreateDelegate(typeof(EventHandler), m); #endif - BindMsg(attr.GetExpression(null), handler); + BindMsg(attr.GetFormattedExpression(null), handler); } } } // Autobinding on instance method - public void BindAttibute(object obj) + public void BindAttribute(object target) { - if (obj == null) return; - Type type = obj.GetType(); + 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("Ivy", "BindAttibute " + attr.GetExpression(obj) + "' to Method " + m.Name); + 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), obj, m); + handler = (EventHandler)Delegate.CreateDelegate(typeof(EventHandler), target, m); #endif - BindMsg(attr.GetExpression(obj), handler); + BindMsg(attr.GetFormattedExpression(target), handler); } } } // Autobinding on IvyBindingAttribute method - public void BindAttibute(Assembly assy) + public void BindAttribute(Assembly target) { - foreach (Type typ in assy.GetTypes()) + if (target != null) { - BindAttibute(typ); + foreach (Type typ in target.GetTypes()) + { + BindAttribute(typ); + } } - } /// /// connects the Ivy bus to a domain or list of domains. @@ -337,9 +421,9 @@ namespace IvyBus /// 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) + public void Start(string domainBus) { - domainbus = GetDomain(domainbus); + domainBus = GetDomain(domainBus); try { long seed = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond; @@ -347,8 +431,8 @@ namespace IvyBus app = new MyTcpListener(IPAddress.Any, 0); app.Start(); // should be started before getting port - applicationHost = GetLocalIP(); - applicationPort = (ushort)((IPEndPoint)app.LocalEndpoint).Port; + applicationHost = LocalIP; + applicationPort = ((IPEndPoint)app.LocalEndpoint).Port; applicationUniqueId = string.Format(culture,"{0}:{1}:{2}", rand.Next(), seed, @@ -363,29 +447,22 @@ namespace IvyBus Ivy.traceProtocol("BindAttibute", "Ivy protocol: " + ProtocolVersion + " TCP service open on port " + applicationPort); watchers = new List(); - Domain[] d = parseDomains(domainbus); + 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 = new Thread(new ThreadStart(this.TcpListener)); serverThread.Name = "Ivy Tcp Server Thread"; stopped = false; + tcplistener_running = new AutoResetEvent(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 + tcplistener_running.WaitOne(); + // sends the broadcasts and listen to incoming connexions for (int i = 0; i < watchers.Count; i++) { @@ -402,9 +479,9 @@ namespace IvyBus } /* a small private method for debbugging purposes */ - public static string Domains(string toparse) + public static string Domains(string toParse) { - string domainbus = GetDomain(toparse); + string domainbus = GetDomain(toParse); StringBuilder s = new StringBuilder("broadcasting on "); Ivy.Domain[] d = parseDomains(domainbus); for (int index = 0; index < d.Length; index++) @@ -438,7 +515,7 @@ namespace IvyBus lock (app) { stopped = true; - Ivy.traceProtocol("Ivy", "beginning stopping the bus"); + Ivy.traceProtocol(Resources.Ivy, "beginning stopping the bus"); try { // stopping the serverThread @@ -464,10 +541,10 @@ namespace IvyBus if (clients.Count != 0) { IvyClient[] copyClient; - copyClient = new IvyClient[clients.Count]; lock (clients) { - clients.CopyTo(copyClient, 0); + copyClient = new IvyClient[clients.Count]; + clients.CopyTo(copyClient); } foreach (IvyClient client in copyClient) { @@ -478,13 +555,35 @@ namespace IvyBus } catch (IOException e) { - Ivy.traceError("Ivy", "IOexception Stop " + e.Message); + Ivy.traceError(Resources.Ivy, "IOexception Stop " + e.Message); } - Ivy.traceProtocol("Ivy", "the bus should have stopped so far"); + 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 /// @@ -501,60 +600,116 @@ namespace IvyBus /// 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 - lock (clients) + // Tentative of using BeginInvoke EndInvoke is slower + lock (client_bindings) { - // hash message in V4 protocol only - if (ProtocolVersion == 4) - IvyBindingSimple.Prepare(msg); - foreach (IvyClient client in clients) + foreach (IvyClientBinding clbind in client_bindings.Values) { - count += client.sendMsg(msg); + 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; } // - public ushort BindMsg(IvyApplicationBinding newbind) + public int BindMsg(IvyApplicationBinding newBinding) { - newbind.Key = serial++; + newBinding.Key = Interlocked.Increment(ref serial); - lock (bindings) bindings.Add(newbind.Key, newbind); + lock (bindings) bindings.Add(newBinding.Key, newBinding); // notifies the other clients this new regexp lock (clients) { foreach (IvyClient c in clients) { - c.stream.TokenAddBinding(newbind.Binding, newbind.Key, newbind.FormatedExpression); + c.stream.TokenAddBinding(newBinding.Binding, newBinding.Key, newBinding.FormattedExpression); } } - return newbind.Key; + return newBinding.Key; } /// /// Subscribes to a regular expression. /// /// a regular expression, groups are done with parenthesis - /// any objects implementing the Ivy.MessageListener + /// 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 ushort BindMsg(string regexp, EventHandler callback, params object[] args) + public int BindMsg(string expression, EventHandler callback, params object[] args) { // creates a new binding (regexp,callback) IvyApplicationBinding newbind; - newbind = new IvyApplicationBinding(); - newbind.Binding = BindingType.Regexp; - newbind.Expression = regexp; + newbind = new IvyApplicationBinding(BindingType.RegularExpression, expression, args); newbind.Callback += callback; - newbind.Args = args; return BindMsg(newbind); } @@ -562,7 +717,7 @@ namespace IvyBus /// unsubscribes a regular expression /// /// the id of the regular expression, returned when it was bound - public void UnbindMsg(ushort id) + public void UnbindMsg(int id) { if (!bindings.ContainsKey(id)) { @@ -572,11 +727,43 @@ namespace IvyBus { foreach (IvyClient c in clients) { - c.stream.TokenDelBinding(id); + try + { + c.stream.TokenDelBinding(id); + } + catch (IOException) + { + // client closed ignore it + } } } lock (bindings) bindings.Remove(id); } + 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 @@ -625,11 +812,8 @@ namespace IvyBus { // creates a new binding (regexp,callback) IvyApplicationBinding newbind; - newbind = new IvyApplicationBinding(); - newbind.Binding = BindingType.Simple; - newbind.Expression = expression; + newbind = new IvyApplicationBinding(BindingType.Simple, expression, args); newbind.Callback += callback; - newbind.Args = args; return BindMsg(newbind); } /// @@ -640,9 +824,9 @@ namespace IvyBus /// 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); + ReadOnlyCollection v = GetClientsByName(target); + foreach (IvyClient clnt in v) + clnt.stream.TokenDie(0, message); return v.Count; } /// @@ -653,29 +837,23 @@ namespace IvyBus /// public int Ping(string target, string message) { - List v = GetClientsByName(target); - for (int i = 0; i < v.Count; i++) - v[i].stream.TokenPing(message); + ReadOnlyCollection v = GetClientsByName(target); + foreach (IvyClient clnt in v) + clnt.stream.TokenPing(message); return v.Count; } -#if (PocketPC) + + internal virtual void FireEvent(EventHandler ev, IvyEventArgs e) { if (ev != null) { +#if (PocketPC) 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) @@ -685,12 +863,13 @@ namespace IvyBus }; syncContext.Post(update, e); } +#endif else ev(this, e); } } -#endif + internal virtual void OnDirectMessage(IvyEventArgs e) { // Copy to a temporary variable to be thread-safe. @@ -712,7 +891,7 @@ namespace IvyBus FireEvent(temp,e); } #if (!PocketPC) - catch (SynchronizationLockException ex) + catch (SynchronizationLockException) { // protect terminaison } @@ -735,6 +914,12 @@ namespace IvyBus 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. @@ -786,12 +971,12 @@ namespace IvyBus #endif internal void OnMessage(IvyMessageEventArgs e) { - IvyApplicationBinding bind = bindings[e.Id]; - - if (bind == null) + 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 @@ -808,6 +993,24 @@ namespace IvyBus { 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); + } + } } @@ -816,7 +1019,7 @@ namespace IvyBus /// /// The name of the Ivy agent you're looking for /// - public List GetClientsByName(string name) + public ReadOnlyCollection GetClientsByName(string name) { List v = new List(); foreach (IvyClient ic in clients) @@ -824,7 +1027,7 @@ namespace IvyBus if (ic.ApplicationName.CompareTo(name) == 0) v.Add(ic); } - return v; + return new ReadOnlyCollection(v); } /////////////////////////////////////////////////////////////////: @@ -833,34 +1036,37 @@ namespace IvyBus // /////////////////////////////////////////////////////////////////: - internal void addClient(Socket socket, string appname) + internal void addClient(IvyClient client) { if (stopped) return; - IvyClient client = new IvyClient(this, socket, appname); + lock (clients) { clients.Add(client); } - client.SendBindings(); + } - public static IPAddress GetLocalIP() + public static IPAddress LocalIP { - IPAddress returnaddr = null; - //TODO remove ALL reverse DNS search !!!! - IPHostEntry ip = Dns.GetHostEntry(Dns.GetHostName()); - foreach (IPAddress addr in ip.AddressList) + get { - returnaddr = addr; - if (IPAddress.IsLoopback(addr)) continue; - break; + 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; } - return returnaddr; } - public static string GetDomain(string domainbus) + public static string GetDomain(string domainBus) { #if (PocketPC) // TODO integrate in normal version if (domainbus == null || domainbus.Length == 0) @@ -885,19 +1091,19 @@ namespace IvyBus domainbus = bcast + ":" + Domain.getPort(domainbus); } #else - if (domainbus == null || domainbus.Length == 0 ) + if (domainBus == null || domainBus.Length == 0 ) { - domainbus = Environment.GetEnvironmentVariable("IVYBUS"); + domainBus = Environment.GetEnvironmentVariable("IVYBUS"); } - if (domainbus == null || domainbus.Length == 0) + if (domainBus == null || domainBus.Length == 0) { - domainbus = Properties.Settings.Default.IvyBus; + domainBus = Properties.Settings.Default.IvyBus; } #endif - if (domainbus == null || domainbus.Length == 0) - domainbus = DEFAULT_DOMAIN; - return domainbus; + if (domainBus == null || domainBus.Length == 0) + domainBus = DefaultDomain; + return domainBus; } @@ -928,50 +1134,61 @@ namespace IvyBus * 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) + internal IvyClient checkConnected(IvyClient clnt) { + IvyClient sameClnt = null; if (clnt.AppPort == 0) - return false; + { + Console.WriteLine(" client {0} port == 0 ", clnt.appName); + return sameClnt; + } lock (clients) { foreach (IvyClient client in clients) { - if (clnt != client && client.sameClient(clnt)) - return true; + if (clnt == client) continue; // SKIP himself + if (client.sameIvyClient(clnt)) + { + sameClnt = client; + break; + } } } - return false; + return sameClnt; } /* * the service socket thread reader main loop */ - private void Run() + private void TcpListener() { - Ivy.traceProtocol("Ivy", "Ivy service Thread started"); + 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; - // early disconnexion - addClient(socket, "Unkown(waiting for name reception)"); // the peer called me + // 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("Ivy","Ivy server socket reader caught an exception: " + e.Message); + Ivy.traceError(Resources.Ivy,Resources.IvyIOException + e.Message); } catch (SocketException e) { - Ivy.traceError("Ivy","my server socket has been closed " + e.Message); + Ivy.traceError(Resources.Ivy,Resources.IvySocketException + e.Message); running = false; } } - Ivy.traceProtocol("Ivy", "Ivy service Thread stopped"); + Ivy.traceProtocol(Resources.Ivy, "Ivy service Thread stopped"); } [Conditional("DEBUG")] @@ -1005,14 +1222,13 @@ namespace IvyBus } private string domainaddr; private int port; - public Domain(string net) + public Domain(string net):this( getDomain(net),getPort(net)) { - this.domainaddr = getDomain(net); - this.port = getPort(net); } public Domain(string domainaddr, int port) { - this.domainaddr = domainaddr; this.port = port; + this.domainaddr = domainaddr; + this.port = port; } public override string ToString() { @@ -1033,7 +1249,7 @@ namespace IvyBus } catch (ArgumentException e) { - Ivy.traceError("Ivy","Bad broascat addr " + net + "error " + e.Message); + Ivy.traceError(Resources.Ivy,Resources.BadBroadcast + net + "error " + e.Message); return null; } return net; @@ -1041,18 +1257,18 @@ namespace IvyBus public static int getPort(string net) { - if (net == null) return Ivy.DEFAULT_PORT; + if (net == null) { return Ivy.DefaultPort; } int sep_index = net.LastIndexOf(":"); - int port = (sep_index == -1) ? Ivy.DEFAULT_PORT : Int32.Parse(net.Substring(sep_index + 1)); + int port = (sep_index == -1) ? Ivy.DefaultPort : Int32.Parse(net.Substring(sep_index + 1)); return port; } } - public static bool ValidatingDomain(string p) + public static bool ValidatingDomain(string domain) { // 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+"); + return Regex.IsMatch(domain, @"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+"); } } diff --git a/Ivy/Ivy.csproj b/Ivy/Ivy.csproj index 4977434..5076a47 100644 --- a/Ivy/Ivy.csproj +++ b/Ivy/Ivy.csproj @@ -1,7 +1,7 @@ - + Local - 8.0.50727 + 9.0.21022 2.0 {F2F03CF7-0087-4EDB-AD15-80C9E8DA2617} SAK @@ -15,7 +15,8 @@ IvyBus.Ivy - IvySignKey.pfx + + JScript Grid IE50 @@ -30,6 +31,23 @@ true + 3.5 + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + v3.0 bin\Debug\ @@ -38,8 +56,7 @@ false - - + DEBUG true @@ -140,6 +157,11 @@ Code + + True + True + Resources.resx + True True @@ -150,12 +172,35 @@ - SettingsSingleFileGenerator Settings.Designer.cs + + + False + .NET Framework 2.0 %28x86%29 + true + + + False + .NET Framework 3.0 %28x86%29 + false + + + False + .NET Framework 3.5 + false + + + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + diff --git a/Ivy/IvyApplicationBinding.cs b/Ivy/IvyApplicationBinding.cs index 299f459..b5fe303 100644 --- a/Ivy/IvyApplicationBinding.cs +++ b/Ivy/IvyApplicationBinding.cs @@ -1,11 +1,14 @@ -using System; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.Text; -using System.Threading; namespace IvyBus { + using System; + using System.Collections.Generic; + using System.Collections.Specialized; + using System.Text; + using System.Threading; + using System.Text.RegularExpressions; + using System.Collections.ObjectModel; + /* This is the Application side of binding storage */ /* association of a generated Key and a delegate and the expression */ /* this is SEND to other client */ @@ -14,15 +17,20 @@ namespace IvyBus { private BindingType binding; + // Argument formatting Expression to replace argument in in regular expresion binding + // format %number[:objectFormat]% + // where number in argument index and objectFormat the ToString format passed to argument + private Regex re = new Regex(@"%(\d+)(|:(.*))%"); + public BindingType Binding { get { return binding; } set { binding = value; } } - private ushort key; + private int key; - public ushort Key + public int Key { get { return key; } set { key = value; } @@ -30,11 +38,6 @@ namespace IvyBus private object[] args; - public object[] Args - { - get { return args; } - set { args = value; } - } private string expression; public string Expression @@ -42,22 +45,22 @@ namespace IvyBus get { return expression; } set { expression = value; } } - private string formated_expression; - public string FormatedExpression + private string formatted_expression; + public string FormattedExpression { get { FormatExpression(); - return formated_expression; + return formatted_expression; } } - private List arguments; + private Collection arguments; ///SentMessageClasses the first word token of sent messages /// optimise the parsing process when sending messages /// - public List Arguments + public Collection Arguments { get { @@ -66,12 +69,35 @@ namespace IvyBus } public event EventHandler Callback; - + public IvyApplicationBinding() + :this(BindingType.RegularExpression,string.Empty,null) { - arguments = new List(); + + } + public IvyApplicationBinding(BindingType type, string expression, params object[] args) + { + arguments = new Collection(); + this.Binding = type; + this.Expression = expression; + this.args = args; } + private string ReplaceArgs(Match m) + { + string s = string.Empty; + // Get the matched string. convert to argindex + int argindex = int.Parse(m.Groups[1].Value); + // use %... to specify formating + object arg = args[argindex]; + IFormattable fmt = arg as IFormattable; + if (fmt != null) + s = fmt.ToString(m.Groups[3].Value, null); + else if (arg != null) + s = arg.ToString(); + + return s; + } // translate part of expression to object property public void FormatExpression() { @@ -94,11 +120,12 @@ namespace IvyBus args[i] = arguments[i]; } } - formated_expression = string.Format(expression, args); + + formatted_expression = re.Replace(expression, ReplaceArgs); } else //TODO Abnormal condition Design Time #endif - formated_expression = expression; + formatted_expression = expression; } diff --git a/Ivy/IvyBinding.cs b/Ivy/IvyBinding.cs index 624b401..bb2498a 100644 --- a/Ivy/IvyBinding.cs +++ b/Ivy/IvyBinding.cs @@ -1,11 +1,13 @@ -using System; -using System.Collections; -using System.Collections.Specialized; -using System.Text.RegularExpressions; -using System.Diagnostics; - + namespace IvyBus { + using System; + using System.Collections; + using System.Collections.Specialized; + using System.Text.RegularExpressions; + using System.Diagnostics; + using IvyBus.Properties; + /* This is the Client side of binding storage */ /* association of a generated Key and the expression and a compiled Expression matching */ /* this is RECEIVED from other client */ @@ -16,22 +18,15 @@ namespace IvyBus internal abstract class IvyBindingBase { - private ushort key; - - internal ushort Key - { - get { return key; } - } - protected string expression; + private string expression; internal string Expression { get { return expression; } } - internal IvyBindingBase(ushort id, string exp) + internal IvyBindingBase(string exp) { - key = id; expression = exp; } internal abstract string[] Match(string message); @@ -41,10 +36,10 @@ namespace IvyBus { internal Regex regexp; - public IvyBindingRegexp(ushort id, string exp) - : base(id, exp) + public IvyBindingRegexp( string exp) + : base(exp) { - regexp = new Regex(expression, /* RegexOptions.Compiled | */RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); + regexp = new Regex(Expression, /* RegexOptions.Compiled | */RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); //regexp.Match("###"); // for really compile the expression really slow on 3000 expression } internal override string[] Match(string message) @@ -71,10 +66,10 @@ namespace IvyBus static string msgtag; // send message name static StringDictionary args_values; // send message args[name]=value - internal IvyBindingSimple(ushort id, string exp) - : base(id, exp) + internal IvyBindingSimple(string exp) + : base(exp) { - string[] expr = expression.Split( ' ' ); + string[] expr = Expression.Split( ' ' ); msgname = expr[0]; msgargs = new string[ expr.Length -1 ]; for ( int i = 1; i < expr.Length; i++ ) @@ -92,7 +87,7 @@ namespace IvyBus args_values[arg[0]] = arg[1]; else { - Ivy.traceError("IvyBindingSimple" , "abnormally Formed message expected 'msg champ=valeur champ=valeur....' :" + message); + Ivy.traceError("IvyBindingSimple" , Resources.BadSimpleBinding + message); } } diff --git a/Ivy/IvyBindingAttribute.cs b/Ivy/IvyBindingAttribute.cs index 90c44ce..1380f0f 100644 --- a/Ivy/IvyBindingAttribute.cs +++ b/Ivy/IvyBindingAttribute.cs @@ -1,31 +1,50 @@ -using System; -using System.Collections.Generic; -using System.Text; + namespace IvyBus { - [AttributeUsage(AttributeTargets.Method,AllowMultiple = true)] + using System; + using System.Collections.Generic; + using System.Text; + using System.Collections.ObjectModel; + using System.Globalization; + + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] public sealed class IvyBindingAttribute : Attribute { private string expression; private string[] args; - // translate part of expression to object property - public string GetExpression(object obj) + public string Expression { - if (obj == null) return string.Format(expression); - object[] values = new object[args.Length]; - for (int i = 0; i < args.Length; i++) - { - values[i] = obj.GetType().GetProperty(args[i]).GetValue(obj,null); - } - return string.Format(expression,values); + get { return this.expression; } } - + + public ReadOnlyCollection Args + { + get { return new ReadOnlyCollection(args); } + } + public IvyBindingAttribute(string expression, params string[] args) { this.expression = expression; this.args = args; } + + // translate part of expression to object property + public string GetFormattedExpression(object target) + { + if (target == null) + { + return string.Format(this.expression); + } + + object[] values = new object[this.args.Length]; + for (int i = 0; i < values.Length; i++) + { + values[i] = target.GetType().GetProperty(args[i]).GetValue(target, null); + } + + return string.Format(this.expression, values); + } } } diff --git a/Ivy/IvyClient.cs b/Ivy/IvyClient.cs index 63dc77e..7f29e91 100644 --- a/Ivy/IvyClient.cs +++ b/Ivy/IvyClient.cs @@ -16,6 +16,8 @@ namespace IvyBus using System.Net.Sockets; using System.Configuration; using System.Diagnostics; + using System.Collections.ObjectModel; + using IvyBus.Properties; /// A Class for the the peers on the bus. /// @@ -29,7 +31,10 @@ namespace IvyBus { public int CompareTo(IvyClient other) { - return (other.clientPriority - clientPriority); + if (other == null) + return clientPriority; + else + return (other.clientPriority - clientPriority); } public String ApplicationName @@ -41,20 +46,20 @@ namespace IvyBus } - public List Regexps - { - get - { - List tab = new List(); - lock (bindings) - { - foreach (IvyBindingBase bind in bindings.Values) - tab.Add(bind.Expression); - } - return tab; - } + //public ReadOnlyCollection Regexps + //{ + // get + // { + // List tab = new List(); + // lock (bindings) + // { + // foreach (IvyBindingBase bind in bindings.Values) + // tab.Add(bind.Expression); + // } + // return new ReadOnlyCollection(tab); + // } - } + //} internal int AppPort { get @@ -81,16 +86,18 @@ namespace IvyBus } private Ivy bus; - private Dictionary bindings; + //private Dictionary bindings; private int appPort; - private string clientId; /* an unique ID for each IvyClient */ + private string clientId; /* an unique ID for each IvyClient */ private int clientPriority; /* client priority */ - + private volatile Thread clientThread; // volatile to ensure the quick communication private bool doping; // false by runtime default private const int PINGTIMEOUT = 5000; private volatile Thread pingerThread; + private int localPort; + private int remotePort; private IPAddress remoteHost; @@ -98,38 +105,31 @@ namespace IvyBus internal String appName; internal IvyProtocol stream; - internal IvyClient(Ivy bus, Socket socket, string appname) + internal IvyClient(Ivy bus, Socket socket, string appname, int appPort) { - bindings = new Dictionary(); - appName = appname; + //bindings = new Dictionary(); + this.appName = appname; + this.appPort = appPort; this.bus = bus; + + localPort = ((IPEndPoint)socket.LocalEndPoint).Port; IPEndPoint endpoint = (IPEndPoint)socket.RemoteEndPoint; - remoteHost = endpoint.Address; remotePort = endpoint.Port; -#if (!PocketPC ) - // try to position Keep Alive - //try - //{ - // socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.KeepAlive, 1); - //} - //catch (SocketException) - //{ - //} -#endif - if ( bus.ProtocolVersion == 4 ) stream = new IvyTCPStreamV4( socket, this ); else stream = new IvyTCPStreamV3(socket, this); - clientPriority = Ivy.DEFAULT_PRIORITY; - // spawns a thread to manage the incoming traffic on this + // spawns a thread to manage the incoming traffic on this // socket. We should be ready to receive messages now. clientThread = new Thread(new ThreadStart(this.Run)); clientThread.Name = "Ivy Tcp Client Reader Thread ("+appname+")"; + + bus.addClient(this); + clientThread.Start(); } @@ -137,44 +137,62 @@ namespace IvyBus internal void SendBindings() { try - { - stream.TokenApplicationId(bus.applicationPriority, bus.AppId); - - // sends our ID, whether we initiated the connexion or not - // the ID is the couple "host name,application Port", the host name - // information is in the socket itself, the port is not known if we - // initiate the connexion - stream.TokenStartRegexp(bus.applicationPort, bus.appName); - // sends our regexps to the peer - lock (bus.bindings) { - foreach (IvyApplicationBinding bind in bus.bindings.Values) + + // sends our ID, whether we initiated the connexion or not + // the ID is the couple "host name,application Port", the host name + // information is in the socket itself, the port is not known if we + // initiate the connexion + stream.TokenStartRegexp(bus.applicationPort, bus.appName); + // sends our regexps to the peer + lock (bus.bindings) { - stream.TokenAddBinding(bind.Binding, bind.Key, bind.FormatedExpression); + foreach (IvyApplicationBinding bind in bus.bindings.Values) + { + stream.TokenAddBinding(bind.Binding, bind.Key, bind.FormattedExpression); + } } - } - stream.TokenEndRegexp(); - + // send end of bindings peers can now send ReadyMessage + stream.TokenEndRegexp(); + #if (!PocketPC) - doping = Properties.Settings.Default.IvyPing; + doping = Properties.Settings.Default.IvyPing; #endif - if (doping) - { - pingerThread = new Thread(new ThreadStart(PingerRun)); - pingerThread.Name = "Ivy Pinger Thread"; - pingerThread.Start(); - } + if (doping) + { + pingerThread = new Thread(new ThreadStart(PingerRun)); + pingerThread.Name = "Ivy Pinger Thread"; + pingerThread.Start(); + } - } - catch (IOException ex) - { // the client nous a coupé l'herbe sous le pied - Ivy.traceError("IvyClient","I can't send my message to this client. He probably left " + ex.Message); - // invokes the Disconnected applicationListeners - //bus.OnClientDisconnected(new IvyEventArgs(this,id, message )); - // called by the receiver Thread - close(false); - } + } + catch (NullReferenceException ex) + { + // the client nous a coupé l'herbe sous le pied + Ivy.traceError(Resources.IvyClient, Resources.IvyClientLeft + " " + appName + " " + ex.Message); + // connexion close in rare concurrent connexion + close(false); + } + catch (ObjectDisposedException ex) + { + // the client nous a coupé l'herbe sous le pied + Ivy.traceError(Resources.IvyClient, Resources.IvyClientLeft + " " + appName + " " + ex.Message); + // invokes the Disconnected applicationListeners + //bus.OnClientDisconnected(new IvyEventArgs(this,id, message )); + // called by the receiver Thread + close(false); + } + catch (IOException ex) + { + // the client nous a coupé l'herbe sous le pied + Ivy.traceError(Resources.IvyClient, Resources.IvyClientLeft + " " + appName + " " + ex.Message); + // invokes the Disconnected applicationListeners + //bus.OnClientDisconnected(new IvyEventArgs(this,id, message )); + // called by the receiver Thread + close(false); + } + } /// returns the name of the remote agent. @@ -191,7 +209,7 @@ namespace IvyBus /// the string that will be match-tested /// /// - public void SendDirectMsg(ushort id, string message) + public void SendDirectMsg(int id, string message) { try { @@ -199,7 +217,7 @@ namespace IvyBus } catch (IOException ex) { - Ivy.traceError("IvyClient","I can't send my message to this client. He probably left "+ex.Message); + Ivy.traceError(Resources.IvyClient,Resources.IvyClientLeft+ex.Message); // first, I'm not a first class IvyClient any more bus.removeClient(this); // invokes the Disconnected applicationListeners @@ -217,19 +235,27 @@ namespace IvyBus /// internal void close(bool notify) { - Ivy.traceProtocol("IvyClient","closing connexion to " + appName); - if (doping ) + Ivy.traceProtocol(Resources.IvyClient,Resources.Closing + appName); + if (doping ) { StopPinging(); } if (notify) try { - stream.TokenBye(0, "hasta la vista"); + if (stream != null) + stream.TokenBye(0, Resources.ByeMessage); + } + catch (ObjectDisposedException) + { } catch (IOException ioe) { - throw new IvyException(ioe.Message); + if (!(ioe.InnerException is SocketException)) + throw new IvyException(ioe.Message); + if (((SocketException)ioe.InnerException).SocketErrorCode != SocketError.ConnectionReset) + throw new IvyException(ioe.Message); + } // stop the thread and close the stream if (clientThread == null) @@ -268,36 +294,37 @@ namespace IvyBus /// the number of messages sent to the peer /// /// - internal int sendMsg(String message) + internal int sendMsg(ushort id, string[] args) { int count = 0; - - lock( bindings ) + + + try { - try - { - foreach (IvyBindingBase bind in bindings.Values) - { - string[] args = bind.Match(message); - if (stream != null && args != null) - { - stream.TokenMsg(bind.Key, args); - count++; - } - } - } - catch (IOException ex) - { - Ivy.traceError("IvyClient","I can't send my message to this client. He probably left " + ex.Message); - // first, I'm not a first class IvyClient any more - bus.removeClient(this); - // invokes the Disconnected applicationListeners - // in the receiver thread - close(false); - } + stream.TokenMsg(id, args); + count++; + } + catch (ObjectDisposedException ex) + { + Ivy.traceError(Resources.IvyClient, Resources.IvyClientLeft + ex.Message); + // first, I'm not a first class IvyClient any more + bus.removeClient(this); //TODO trouble in upper loop iter + // invokes the Disconnected applicationListeners + // in the receiver thread + close(false); + } + catch (IOException ex) + { + Ivy.traceError(Resources.IvyClient, Resources.IvyClientLeft + ex.Message); + // first, I'm not a first class IvyClient any more + bus.removeClient(this); //TODO trouble in upper loop iter + // invokes the Disconnected applicationListeners + // in the receiver thread + close(false); + } - } + return count; } @@ -309,25 +336,28 @@ namespace IvyBus /// © ® (tm) /// /// - internal bool sameClient(IvyClient clnt) + internal bool sameIvyClient(IvyClient clnt) { - return (appPort != 0 && appPort == clnt.appPort) && (RemoteAddress == clnt.RemoteAddress); + // clientId est null si le protocol n'envoie pas le client ID + if (clnt.clientId != null && this.clientId != null && clnt.clientId == this.clientId) + return true; + return (this.appPort != 0) && (this.appPort == clnt.appPort) && (this.RemoteAddress.Equals(clnt.RemoteAddress)); } /// the code of the thread handling the incoming messages. /// private void Run() { - Ivy.traceProtocol("IvyClient","Connected from " + RemoteAddress + ":" + RemotePort); + Ivy.traceProtocol(Resources.IvyClient, Resources.Connected + RemoteAddress + ":" + RemotePort); - Ivy.traceProtocol("IvyClient","Thread started"); - - bool running = true; + Ivy.traceProtocol(Resources.IvyClient,"Thread started"); + + bool running = true; while ( running && (stream != null) ) { try { - if ( stream.receiveMsg() ) + if ( stream.ReceiveMsg() ) { // early stop during readLine() if (doping && (pingerThread != null)) @@ -335,26 +365,26 @@ namespace IvyBus } else { - Ivy.traceProtocol("IvyClient","receiveMsg false ! leaving the thread"); + Ivy.traceProtocol(Resources.IvyClient, Resources.BadReceive); running = false; break; } } catch ( ObjectDisposedException ex ) { - Ivy.traceError("IvyClient", "socket closed "+ex.Message ); + Ivy.traceError(Resources.IvyClient, Resources.SocketClosed + ex.Message); running = false; break; } catch (IvyException ie) { - Ivy.traceError("IvyClient","socket closed IvyException" + ie.Message); + Ivy.traceError(Resources.IvyClient, Resources.SocketClosed + ie.Message); running = false; break; } catch (SocketException se) { - Ivy.traceError("IvyClient", "socket closed "+se.Message ); + Ivy.traceError(Resources.IvyClient, Resources.SocketClosed + se.Message ); running = false; break; } @@ -362,21 +392,21 @@ namespace IvyBus { if ( ex.InnerException is SocketException ) { - Ivy.traceProtocol("IvyClient", "socket closed" ); + Ivy.traceProtocol(Resources.IvyClient, Resources.SocketClosed ); } else { - Ivy.traceError("IvyClient","abnormally Disconnected from " + RemoteAddress + ":" + RemotePort); + Ivy.traceError(Resources.IvyClient, Resources.AbDisconnect + RemoteAddress + ":" + RemotePort); } running = false; break; } } - Ivy.traceProtocol("IvyClient","normally Disconnected from " + appName); - Ivy.traceProtocol("IvyClient","Thread stopped"); + Ivy.traceProtocol(Resources.IvyClient,Resources.Disconnected + appName); + Ivy.traceProtocol(Resources.IvyClient,"Thread stopped"); // invokes the Disconnected applicationListeners - bus.OnClientDisconnected(new IvyEventArgs(this,0, "" )); + bus.OnClientDisconnected(new IvyEventArgs(this,0, string.Empty )); // first, I'm not a first class IvyClient any more if (stream != null) { @@ -390,14 +420,14 @@ namespace IvyBus { // never call in this side } - bool IvyProtocol.receiveMsg() + bool IvyProtocol.ReceiveMsg() { // nerver call in this side return false; } - void IvyProtocol.TokenDie(ushort id, string arg) + void IvyProtocol.TokenDie(int id, string arg) { - Ivy.traceProtocol("IvyClient","received die Message from " + appName + "Raison: "+ arg); + Ivy.traceProtocol(Resources.IvyClient, Resources.DieReceive + appName + Resources.Reason + arg); // invokes the die applicationListeners IvyDieEventArgs ev = new IvyDieEventArgs(this, id, arg); bus.OnDie(ev); @@ -413,10 +443,10 @@ namespace IvyBus System.Environment.Exit(0); #endif } - void IvyProtocol.TokenBye(ushort err, string arg) + void IvyProtocol.TokenBye(int err, string arg) { // the peer quits - Ivy.traceProtocol("IvyClient","received bye Message from " + appName + "Raison: "+ arg); + Ivy.traceProtocol(Resources.IvyClient, Resources.ByeReceive + appName + Resources.Reason + arg); // first, I'm not a first class IvyClient any more //bus.removeClient(this); // this is done in the receive thread terminaison!! // invokes the disconnect applicationListeners @@ -424,10 +454,10 @@ namespace IvyBus close(false); // will fire disconnected } - void IvyProtocol.TokenAddBinding(BindingType type, ushort id, string expression) + void IvyProtocol.TokenAddBinding(BindingType type, int id, string expression) { - if (type == BindingType.Regexp && !bus.CheckRegexp(expression)) + if (type == BindingType.RegularExpression && !bus.CheckRegexp(expression)) { bus.OnClientFilterBinding(new IvyEventArgs(this, id, expression )); return; @@ -437,20 +467,14 @@ namespace IvyBus { switch (type) { - case BindingType.Regexp: - bind = new IvyBindingRegexp(id, expression); + case BindingType.RegularExpression: + bind = new IvyBindingRegexp(expression); break; case BindingType.Simple: - bind = new IvyBindingSimple(id, expression); + bind = new IvyBindingSimple(expression); break; } - lock (bindings) - { - bindings.Add(id, bind); - } - - bus.OnClientAddBinding(new IvyEventArgs(this, id, expression)); - + bus.AddBinding(id, this, bind); } catch (ArgumentException ex) { @@ -458,81 +482,88 @@ namespace IvyBus } } - void IvyProtocol.TokenDelBinding(ushort id) + + + void IvyProtocol.TokenDelBinding(int id) { - lock( bindings ) - { - try - { - IvyBindingBase bind = bindings[id]; - bus.OnClientRemoveBinding(new IvyEventArgs(this, bind.Key, bind.Expression)); - bindings.Remove(id); - } - catch (KeyNotFoundException ex) - { - Ivy.traceError("IvyClient","DelBinding " + ex.Message); - } - } + bus.DelBinding(id, this); } - void IvyProtocol.TokenMsg(ushort id, string[] args) + void IvyProtocol.TokenMsg(int id, string[] args) { bus.OnMessage(new IvyMessageEventArgs(this, id, args)); } - void IvyProtocol.TokenError(ushort id, string arg) + void IvyProtocol.TokenError(int id, string arg) { bus.OnError(new IvyEventArgs(this, id, arg)); - Ivy.traceError("IvyClient","Error msg " + id + " " + arg); - } - void IvyProtocol.TokenApplicationId(ushort id, string arg) - { - clientId = arg; - if ( clientPriority != id ) - { - clientPriority = id; - bus.SortClients(); - } + Ivy.traceError(Resources.IvyClient,Resources.ErrorReceive + id + " " + arg); } + void IvyProtocol.TokenEndRegexp() { + bus.OnClientConnected(new IvyEventArgs(this, 0, string.Empty)); /* * the peer is perhaps not ready to handle this message * an assymetric processing should be written */ if (bus.ReadyMessage != null) - sendMsg(bus.ReadyMessage); - bus.OnClientConnected(new IvyEventArgs(this, 0, "")); + { + bus.SendMsgToClient(this, bus.ReadyMessage); + } + } - void IvyProtocol.TokenStartRegexp(ushort id, string arg) + void IvyProtocol.TokenStartRegexp(int id, string arg) { + //bool bindingToSend = appPort == 0; appName = arg; appPort = id; - if (bus.checkConnected(this)) - { - close(false); - throw new IvyException("Rare ! A concurrent connect occured"); - } + IvyClient target = this; + IvyClient client = bus.checkConnected(this); + if (client != null) + { + // Dilemma choose the rigth client to close + // the symetric processing will try to close each other + // only one side may be closed + //Console.WriteLine(" should close {0} this local {1} rem {2} other local {3} rem {4}", this.appName, this.localPort, this.remotePort, client.localPort, client.remotePort); + if (Math.Max(client.localPort, client.remotePort) > Math.Max( this.localPort, this.remotePort )) + { + target = client; + //Console.WriteLine("choose {0} other ports {1},{2}", target.appName, target.localPort, target.remotePort); + } + else + { + target = this; + //Console.WriteLine("choose {0} this ports {1},{2}", target.appName, target.remotePort, target.localPort); + } + bus.removeClient(target); + target.close(false); + //throw new IvyException(Resources.ConcurrentConnect + " " + appName + " " + clientId); + + } + //if ( bindingToSend && target != this) + // SendBindings(); + } - void IvyProtocol.TokenDirectMsg(ushort id, string arg) + void IvyProtocol.TokenDirectMsg(int id, string arg) { bus.OnDirectMessage(new IvyEventArgs(this,id,arg)); } void IvyProtocol.TokenPing(string arg) { // I receive a ping. I can answer a pong. - Ivy.traceProtocol("IvyClient","Ping msg from " + appName + " : " + arg ); + Ivy.traceProtocol(Resources.IvyClient, Resources.PingReceive + appName + " : " + arg ); stream.TokenPong(arg); } void IvyProtocol.TokenPong(string arg) { - Ivy.traceProtocol("IvyClient","Ping msg from " + appName + " : " + arg); + Ivy.traceProtocol(Resources.IvyClient, Resources.PingReceive + appName + " : " + arg); } public override String ToString() { - return "IvyClient " + bus.appName + ":" + appName; + return Resources.IvyClient+ " " + bus.appName + ":" + appName; } /* is the Pinging Thread Runninng */ @@ -541,20 +572,20 @@ namespace IvyBus private void PingerRun() { isPinging = true; - Ivy.traceProtocol("IvyClient","Pinger Thread started"); + Ivy.traceProtocol(Resources.IvyClient,Resources.PingerThreadStarted); while (isPinging) { try { Thread.Sleep(PINGTIMEOUT); - stream.TokenPing("are you here ?"); + stream.TokenPing(Resources.PingerThreadMessage); } catch (ThreadAbortException ie) { - Ivy.traceError("IvyClient","Pinger Thread killed "+ie.Message); + Ivy.traceError(Resources.IvyClient,Resources.PingerThreadKilled + ie.Message); } } - Ivy.traceProtocol("IvyClient","Pinger Thread stopped"); + Ivy.traceProtocol(Resources.IvyClient,Resources.PingerThreadStopped); } public virtual void StopPinging() { diff --git a/Ivy/IvyEventArgs.cs b/Ivy/IvyEventArgs.cs index e394802..a08f314 100644 --- a/Ivy/IvyEventArgs.cs +++ b/Ivy/IvyEventArgs.cs @@ -1,9 +1,11 @@ -using System; -using System.Collections.Generic; -using System.Text; namespace IvyBus { + using System; + using System.Collections.Generic; + using System.Text; + using System.Collections.ObjectModel; + /// The EventArgs Classes /// /// @@ -66,13 +68,13 @@ namespace IvyBus get { return id; } } - public string[] Arguments + public string[] GetArguments() { - get { return args; } + return (string[])args.Clone(); } - public string this[int i] + public string this[int index] { - get { return args[i]; } + get { return args[index]; } } public IvyMessageEventArgs(IvyClient app, int id, string[] args) { diff --git a/Ivy/IvyException.cs b/Ivy/IvyException.cs index 90a91b8..2bdf066 100644 --- a/Ivy/IvyException.cs +++ b/Ivy/IvyException.cs @@ -5,14 +5,30 @@ /// * namespace IvyBus { - using System; - + using System; + using System.Runtime.Serialization; + /// signals that an unrecoverrable Ivy exception has occured. /// - - public class IvyException:System.Exception + [Serializable] + public class IvyException : System.Exception { - public IvyException(System.String s):base(s) + public IvyException() + : base() + { + } + + protected IvyException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + public IvyException(string message, Exception exp) + : base(message,exp) + { + } + + public IvyException(string message) : base(message) { } } diff --git a/Ivy/IvyProtocol.cs b/Ivy/IvyProtocol.cs index 1b1f3aa..6c276e3 100644 --- a/Ivy/IvyProtocol.cs +++ b/Ivy/IvyProtocol.cs @@ -1,25 +1,28 @@ -using System; -using System.Text; namespace IvyBus { - public enum BindingType { Regexp, Simple }; + using System; + using System.Text; + + public enum BindingType + { + RegularExpression, Simple + } internal interface IvyProtocol { void Close(); - bool receiveMsg(); - void TokenStartRegexp(ushort port, string appName); + bool ReceiveMsg(); + void TokenStartRegexp(int port, string appName); void TokenEndRegexp(); - void TokenApplicationId(ushort priority, string appId); - void TokenAddBinding(BindingType type, ushort id, string expression); - void TokenDelBinding(ushort bind); - void TokenDirectMsg(ushort id, string message); + void TokenAddBinding(BindingType type, int id, string expression); + void TokenDelBinding(int bind); + void TokenDirectMsg(int id, string message); void TokenPong(string s); void TokenPing(string s); - void TokenBye(ushort id, string message); - void TokenDie(ushort err, string message); - void TokenMsg(ushort key, string[] args); - void TokenError(ushort id, string message); + void TokenBye(int id, string message); + void TokenDie(int err, string message); + void TokenMsg(int key, string[] args); + void TokenError(int id, string message); } } diff --git a/Ivy/IvyTCPStreamV3.cs b/Ivy/IvyTCPStreamV3.cs index 594f9de..7f90a94 100644 --- a/Ivy/IvyTCPStreamV3.cs +++ b/Ivy/IvyTCPStreamV3.cs @@ -1,36 +1,45 @@ -using System; -using System.Net; -using System.Net.Sockets; -using System.Text; -using System.Collections; -using System.Collections.Specialized; -using System.IO; namespace IvyBus { + using System; + using System.Net; + using System.Net.Sockets; + using System.Text; + using System.Collections; + using System.Collections.Specialized; + using System.IO; + using IvyBus.Properties; + /// /// Description résumée de IvyStream. /// internal class IvyTCPStreamV3 : NetworkStream, IvyProtocol - { - StreamReader input; - StreamWriter output; - IvyProtocol receiver; - + { /// the protocol separator - internal const char ARG_START = '\x02'; - internal const char ARG_END = '\x03'; - internal const char MSG_END = '\n'; + internal const char ArgStart = '\x02'; + internal const char ArgEnd = '\x03'; + internal const char MsgEnd = '\n'; + + private StreamReader input; + private StreamWriter output; + private IvyProtocol receiver; - internal IvyTCPStreamV3(Socket socket, IvyProtocol _receiver) : base ( socket ) + internal IvyTCPStreamV3(Socket socket, IvyProtocol receiver) : base(socket) { - output = new StreamWriter(this, Ivy.ivyEncoding); - output.NewLine = MSG_END.ToString(); - input = new StreamReader(this, Ivy.ivyEncoding); - receiver = _receiver; + this.output = new StreamWriter(this, Ivy.ivyEncoding); + this.output.NewLine = MsgEnd.ToString(); + this.input = new StreamReader(this, Ivy.ivyEncoding); + this.receiver = receiver; } + internal void Close() + { + this.input.Close(); + this.output.Close(); + base.Close(); + } + /* the protocol magic numbers */ - internal enum MessageType : ushort + internal enum MessageType : int { Bye = 0, /* end of the peer */ AddRegexp = 1, /* the peer adds a regexp */ @@ -43,7 +52,7 @@ namespace IvyBus Die = 8, /* the peer wants us to quit */ Ping = 9, /* checks the presence of the other */ Pong = 10, /* checks the presence of the other */ - }; + } /* * message Syntax: @@ -53,37 +62,37 @@ namespace IvyBus MessageType, id , length, string */ - private void sendMsg(MessageType msgType, ushort msgId, string msgData) + private void SendMsg(MessageType msgType, int msgId, string msgData) { // IOException Should be traited upstairs - output.Write((ushort)msgType); - output.Write(' '); - output.Write(msgId); - output.Write(ARG_START); - output.Write(msgData); - output.Write(MSG_END); - output.Flush(); - + lock (this.output) + { + this.output.Write((int)msgType); + this.output.Write(' '); + this.output.Write(msgId); + this.output.Write(ArgStart); + this.output.Write(msgData); + this.output.Write(MsgEnd); + this.output.Flush(); + } } - void IvyProtocol.TokenStartRegexp(ushort port, string appName) + + void IvyProtocol.TokenStartRegexp(int port, string appName) { - sendMsg(MessageType.StartRegexp, port, appName); + this.SendMsg(MessageType.StartRegexp, port, appName); } + void IvyProtocol.TokenEndRegexp() { - sendMsg(MessageType.EndRegexp, 0, ""); - } - void IvyProtocol.TokenApplicationId(ushort priority, string appId) - { - // NOt implemented in this protocol version + this.SendMsg(MessageType.EndRegexp, 0, string.Empty); } - void IvyProtocol.TokenAddBinding(BindingType type, ushort id, string expression) + void IvyProtocol.TokenAddBinding(BindingType type, int id, string expression) { switch (type) { - case BindingType.Regexp: - sendMsg(MessageType.AddRegexp, id, expression); /* perhaps we should perform some checking here */ + case BindingType.RegularExpression: + this.SendMsg(MessageType.AddRegexp, id, expression); /* perhaps we should perform some checking here */ break; case BindingType.Simple: // NO Simple Binding in this protocol @@ -91,66 +100,69 @@ namespace IvyBus } } - void IvyProtocol.TokenDelBinding(ushort id) + void IvyProtocol.TokenDelBinding(int id) { - sendMsg(MessageType.DelBinding, id, ""); + this.SendMsg(MessageType.DelBinding, id, string.Empty); } - void IvyProtocol.TokenDirectMsg(ushort id, string message) + void IvyProtocol.TokenDirectMsg(int id, string message) { - sendMsg(MessageType.DirectMsg, id, message); + this.SendMsg(MessageType.DirectMsg, id, message); } + void IvyProtocol.TokenPong(string s) { - sendMsg(MessageType.Pong, 0, s); + this.SendMsg(MessageType.Pong, 0, s); } + void IvyProtocol.TokenPing(string s) { - sendMsg(MessageType.Ping, 0, s); + this.SendMsg(MessageType.Ping, 0, s); } - void IvyProtocol.TokenBye(ushort id, string message) + void IvyProtocol.TokenBye(int id, string message) { - sendMsg(MessageType.Bye, id, message); + this.SendMsg(MessageType.Bye, id, message); } - void IvyProtocol.TokenDie(ushort id, string message) + void IvyProtocol.TokenDie(int id, string message) { - sendMsg(MessageType.Die, id, message); + this.SendMsg(MessageType.Die, id, message); } - void IvyProtocol.TokenMsg(ushort key, string[] args) + void IvyProtocol.TokenMsg(int key, string[] args) { - string delimiter = "" + ARG_END; + string delimiter = string.Empty + ArgEnd; string data = string.Join(delimiter, args); // a bad protocol implementation in C add a delimiter to the end of each arg // we must add a delimiter to the end data += delimiter; - sendMsg(MessageType.Msg, key, data); + this.SendMsg(MessageType.Msg, key, data); } - void IvyProtocol.TokenError(ushort key, string arg) + void IvyProtocol.TokenError(int key, string arg) { - sendMsg(MessageType.Msg, key, arg); + this.SendMsg(MessageType.Msg, key, arg); } - private ushort DeserializeShort() + private int DeserializeInt() { int read; - ushort ret = 0; + int ret = 0; char digit; // this will eat next non digit char ie space do { - read = input.Read(); + read = this.input.Read(); if (read < 0) throw new EndOfStreamException(); digit = (char)read; if (Char.IsDigit(digit)) - ret = (ushort)(ret * 10 + (digit - 0x30)); + ret = (int)(ret * 10 + (digit - 0x30)); } while (Char.IsDigit(digit)); return ret; } + private string DeserializeString(char sep) { int read; @@ -168,38 +180,38 @@ namespace IvyBus return str.ToString(); } - bool IvyProtocol.receiveMsg() + bool IvyProtocol.ReceiveMsg() { MessageType msgType = MessageType.Die; - ushort msgId = 0; + int msgId = 0; string msgData = null; try { - msgType = (MessageType)DeserializeShort(); - msgId = DeserializeShort(); - msgData = DeserializeString(MSG_END); + msgType = (MessageType)DeserializeInt(); + msgId = DeserializeInt(); + msgData = DeserializeString(MsgEnd); switch (msgType) { case MessageType.Die: - receiver.TokenDie(msgId, msgData); + this.receiver.TokenDie(msgId, msgData); break; case MessageType.Bye: - receiver.TokenBye(msgId, msgData); + this.receiver.TokenBye(msgId, msgData); break; - case MessageType.AddRegexp: - receiver.TokenAddBinding(BindingType.Regexp, msgId, msgData); + case MessageType.AddRegexp: + this.receiver.TokenAddBinding(BindingType.RegularExpression, msgId, msgData); break; case MessageType.DelBinding: - receiver.TokenDelBinding(msgId); + this.receiver.TokenDelBinding(msgId); break; case MessageType.EndRegexp: - receiver.TokenEndRegexp(); + this.receiver.TokenEndRegexp(); break; case MessageType.Msg: @@ -207,30 +219,30 @@ namespace IvyBus // we must remove a delimiter to the end if ( msgData.Length > 0 ) msgData = msgData.Remove(msgData.Length - 1,1); - receiver.TokenMsg(msgId, msgData.Split( ARG_END )); + this.receiver.TokenMsg(msgId, msgData.Split( ArgEnd )); break; case MessageType.Pong: - receiver.TokenPong(msgData); + this.receiver.TokenPong(msgData); break; case MessageType.Ping: - receiver.TokenPing(msgData); + this.receiver.TokenPing(msgData); break; case MessageType.Error: - receiver.TokenError(msgId, msgData); + this.receiver.TokenError(msgId, msgData); break; case MessageType.StartRegexp: - receiver.TokenStartRegexp(msgId, msgData); + this.receiver.TokenStartRegexp(msgId, msgData); break; case MessageType.DirectMsg: - receiver.TokenDirectMsg(msgId, msgData); + this.receiver.TokenDirectMsg(msgId, msgData); break; default: - throw new IvyException("protocol error, unknown message type " + msgType); + throw new IvyException(Resources.UnknownMessage + msgType); } @@ -243,7 +255,7 @@ namespace IvyBus } catch (FormatException) { - throw new IvyException("protocol error on msgType"); + throw new IvyException(Resources.ProtocolError); } diff --git a/Ivy/IvyTCPStreamV4.cs b/Ivy/IvyTCPStreamV4.cs index d477908..2b1640a 100644 --- a/Ivy/IvyTCPStreamV4.cs +++ b/Ivy/IvyTCPStreamV4.cs @@ -1,17 +1,20 @@ -using System; -using System.Net; -using System.Net.Sockets; -using System.Text; -using System.Collections; -using System.Collections.Specialized; -using System.IO; + namespace IvyBus { - /// + using System; + using System.Net; + using System.Net.Sockets; + using System.Text; + using System.Collections; + using System.Collections.Specialized; + using System.IO; + using IvyBus.Properties; + + /// /// Description résumée de IvyStream. /// - internal class IvyTCPStreamV4 : NetworkStream , IvyProtocol + internal class IvyTCPStreamV4 : NetworkStream, IvyProtocol { BinaryReader input; BinaryWriter output; @@ -31,19 +34,24 @@ namespace IvyBus Die = 8, /* the peer wants us to quit */ Ping = 9, /* checks the presence of the other */ Pong = 10, /* checks the presence of the other */ - ApplicationId = 11, /* on start send my ID and priority */ - AddBinding = 12, /* other methods for binding message based on hash table */ + AddBinding = 11, /* other methods for binding message based on hash table */ }; internal IvyTCPStreamV4(Socket socket, IvyProtocol _receiver) : base( socket ) { - input = new BinaryReader(this, Ivy.ivyEncoding ); - output = new BinaryWriter(this, Ivy.ivyEncoding ); - receiver = _receiver; + this.input = new BinaryReader(this, Ivy.ivyEncoding); + this.output = new BinaryWriter(this, Ivy.ivyEncoding); + this.receiver = _receiver; } + internal void Close() + { + this.input.Close(); + this.output.Close(); + base.Close(); + } /* * message Syntax: @@ -52,9 +60,9 @@ namespace IvyBus * message Format: MessageType, id , length, string */ - private void Serialize(short arg) + private void Serialize(int arg) { - output.Write((ushort)IPAddress.HostToNetworkOrder(arg)); + output.Write((short)IPAddress.HostToNetworkOrder(arg)); } private void Serialize(string arg) { @@ -67,7 +75,7 @@ namespace IvyBus { /* serialize count */ - Serialize((short)arg.Length); + Serialize(arg.Length); for (int i = 0; i < arg.Length; i++) { @@ -76,31 +84,29 @@ namespace IvyBus } private void sendMsg(MessageType type, int id, params string[] arg) { - - Serialize( (short)type ); - Serialize( (short)id ); - Serialize(arg); - output.Flush(); - + lock (this.output) + { + Serialize((int)type); + Serialize(id); + Serialize(arg); + output.Flush(); + } } - void IvyProtocol.TokenStartRegexp(ushort port, string appName) + void IvyProtocol.TokenStartRegexp(int port, string appName) { sendMsg(MessageType.StartRegexp, port, appName); } void IvyProtocol.TokenEndRegexp() { - sendMsg(MessageType.EndRegexp, 0, ""); - } - void IvyProtocol.TokenApplicationId(ushort priority, string appId) - { - sendMsg(MessageType.ApplicationId, priority, appId); + sendMsg(MessageType.EndRegexp, 0, string.Empty); } + - void IvyProtocol.TokenAddBinding(BindingType type, ushort id, string expression) + void IvyProtocol.TokenAddBinding(BindingType type, int id, string expression) { switch (type) { - case BindingType.Regexp: + case BindingType.RegularExpression: sendMsg(MessageType.AddRegexp, id, expression); /* perhaps we should perform some checking here */ break; case BindingType.Simple: @@ -109,12 +115,12 @@ namespace IvyBus } } - void IvyProtocol.TokenDelBinding(ushort id) + void IvyProtocol.TokenDelBinding(int id) { sendMsg(MessageType.DelBinding, id, null ); } - void IvyProtocol.TokenDirectMsg(ushort id, string message) + void IvyProtocol.TokenDirectMsg(int id, string message) { sendMsg(MessageType.DirectMsg, id, message); } @@ -127,43 +133,43 @@ namespace IvyBus sendMsg(MessageType.Ping, 0, s); } - void IvyProtocol.TokenBye(ushort id, string message) + void IvyProtocol.TokenBye(int id, string message) { sendMsg(MessageType.Bye, id, message); } - void IvyProtocol.TokenDie(ushort id, string message) + void IvyProtocol.TokenDie(int id, string message) { sendMsg(MessageType.Die, id, message); } - void IvyProtocol.TokenMsg(ushort key, string[] args) + void IvyProtocol.TokenMsg(int key, string[] args) { sendMsg(MessageType.Msg, key, args ); } - void IvyProtocol.TokenError(ushort key, string arg) + void IvyProtocol.TokenError(int key, string arg) { sendMsg(MessageType.Msg, key, arg); } - private short DeserializeShort() + private int DeserializeInt() { - return IPAddress.NetworkToHostOrder((short)input.ReadUInt16()); + return IPAddress.NetworkToHostOrder((short)input.ReadInt16()); } private string DeserializeString() { string arg; int val_len; char[] data; - val_len = (ushort)DeserializeShort(); + val_len = DeserializeInt(); if (val_len != 0) { data = input.ReadChars(val_len); arg = new String(data); } else - arg = ""; + arg = string.Empty; return arg; } @@ -174,7 +180,7 @@ namespace IvyBus string[] arg; /* Deserialize childrens */ - nb_children = (ushort)DeserializeShort(); + nb_children = DeserializeInt(); /* deserialize Value */ arg = new string[nb_children]; @@ -184,16 +190,16 @@ namespace IvyBus } return arg; } - bool IvyProtocol.receiveMsg() + bool IvyProtocol.ReceiveMsg() { MessageType msgType = MessageType.Die; - ushort msgId = 0; + int msgId = 0; string[] msgData = null; try { - msgType = (MessageType)(ushort)DeserializeShort(); - msgId = (ushort)DeserializeShort(); + msgType = (MessageType)DeserializeInt(); + msgId = DeserializeInt(); msgData = DeserializeArgument(); switch (msgType) @@ -207,7 +213,7 @@ namespace IvyBus break; case MessageType.AddRegexp: - receiver.TokenAddBinding(BindingType.Regexp, msgId, msgData[0]); + receiver.TokenAddBinding(BindingType.RegularExpression, msgId, msgData[0]); break; case MessageType.AddBinding: @@ -245,11 +251,8 @@ namespace IvyBus case MessageType.DirectMsg: receiver.TokenDirectMsg(msgId, msgData[0]); break; - case MessageType.ApplicationId: - receiver.TokenApplicationId(msgId, msgData[0]); - break; default: - throw new IvyException("protocol error, unknown message type " + msgType); + throw new IvyException(Resources.UnknownMessage + msgType); } @@ -262,7 +265,7 @@ namespace IvyBus } catch (FormatException) { - throw new IvyException("protocol error on msgType"); + throw new IvyException(Resources.ProtocolError); } diff --git a/Ivy/IvyUDPStream.cs b/Ivy/IvyUDPStream.cs index 0e3f517..986a550 100644 --- a/Ivy/IvyUDPStream.cs +++ b/Ivy/IvyUDPStream.cs @@ -1,34 +1,34 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Net; -using System.Net.Sockets; -using System.IO; namespace IvyBus { - abstract class IvyUDPStream + using System; + using System.Collections.Generic; + using System.Text; + using System.Net; + using System.Net.Sockets; + using System.IO; + + abstract class IvyUDPStream: IDisposable { - Socket socket; - byte[] buffer; - protected MemoryStream out_stream; protected MemoryStream in_stream; + private Socket socket; + private byte[] buffer; + + private int protocolVersion; - ushort protocol_version; - - public ushort ProtocolVersion + public int ProtocolVersion { - get { return protocol_version; } + get { return protocolVersion; } } - public IvyUDPStream(Socket _socket, ushort protocol) + public IvyUDPStream(Socket _socket, int protocol) { socket = _socket; buffer = new byte[4096]; in_stream = new MemoryStream(buffer); out_stream = new MemoryStream(); - protocol_version = protocol; + protocolVersion = protocol; } internal void Close() { @@ -37,7 +37,7 @@ namespace IvyBus socket.Shutdown(SocketShutdown.Both); socket.Close(); } - internal void receiveMsg(out IPEndPoint remote, out ushort version, out ushort port, out string appId, out string appName) + internal void receiveMsg(out IPEndPoint remote, out int version, out int port, out string appId, out string appName) { int len; IPEndPoint remoteEP = new IPEndPoint(IPAddress.Any, 0); @@ -51,7 +51,7 @@ namespace IvyBus //Call Deserialization Deserialize( out version, out port, out appId, out appName ); } - internal void sendMsg(IPEndPoint EPhost, ushort port, string appId, string appName) + internal void sendMsg(IPEndPoint EPhost, int port, string appId, string appName) { // Call Serialisation Serialize(port, appId, appName); @@ -59,8 +59,38 @@ namespace IvyBus byte[] hellob = out_stream.GetBuffer(); socket.SendTo(hellob, (int)out_stream.Length, 0, EPhost); } - abstract internal void Serialize(ushort port, string appId, string appName); - abstract internal void Deserialize(out ushort version, out ushort port, out string appId, out string appName); - + abstract internal void Serialize(int port, string appId, string appName); + abstract internal void Deserialize(out int version, out int port, out string appId, out string appName); + + + #region IDisposable Membres + + //Implement IDisposable. + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + // Free other state (managed objects). + } + // Free your own state (unmanaged objects). + // Set large fields to null. + if (out_stream != null) + { + out_stream.Close(); + out_stream = null; + } + if (in_stream != null) + { + in_stream.Close(); + in_stream = null; + } + } + #endregion } } diff --git a/Ivy/IvyUDPStreamV3.cs b/Ivy/IvyUDPStreamV3.cs index 8f48f3b..a47ad3f 100644 --- a/Ivy/IvyUDPStreamV3.cs +++ b/Ivy/IvyUDPStreamV3.cs @@ -1,12 +1,13 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Net; -using System.Net.Sockets; -using System.IO; namespace IvyBus { + using System; + using System.Collections.Generic; + using System.Text; + using System.Net; + using System.Net.Sockets; + using System.IO; + class IvyUDPStreamV3 : IvyUDPStream { StreamReader input; @@ -27,7 +28,7 @@ namespace IvyBus * message Format: protocol_version, TCP server port , appId, appName */ - private ushort DeserializeShort() + private int DeserializeInt() { int read; ushort ret = 0; @@ -63,15 +64,15 @@ namespace IvyBus return str.ToString(); } - internal override void Deserialize(out ushort version, out ushort port, out string appId, out string appName) + internal override void Deserialize(out int version, out int port, out string appId, out string appName) { version = 0; port = 0; - appId = ""; - appName = ""; + appId = string.Empty; + appName = string.Empty; try { - version = DeserializeShort(); - port = DeserializeShort(); + version = DeserializeInt(); + port = DeserializeInt(); //Optionel in V3 protocol depend on client version appId = DeserializeString(' '); appName = DeserializeString('\n'); @@ -82,7 +83,7 @@ namespace IvyBus } input.DiscardBufferedData(); } - private void Serialize(ushort arg, char sep) + private void Serialize(int arg, char sep) { output.Write(arg); output.Write(sep); @@ -92,7 +93,7 @@ namespace IvyBus output.Write(arg); output.Write(sep); } - internal override void Serialize(ushort port, string appId, string appName) + internal override void Serialize(int port, string appId, string appName) { Serialize(PROCOCOLVERSION, ' '); Serialize(port,' '); diff --git a/Ivy/IvyUDPStreamV4.cs b/Ivy/IvyUDPStreamV4.cs index 4f0a8fc..4cd5cee 100644 --- a/Ivy/IvyUDPStreamV4.cs +++ b/Ivy/IvyUDPStreamV4.cs @@ -27,37 +27,37 @@ namespace IvyBus * message Format: protocol_version, TCP server port , lenAppId, appId, lenAppNameId, appName */ - private ushort DeserializeShort() + private int DeserializeInt() { - return (ushort)IPAddress.NetworkToHostOrder((ushort)input.ReadUInt16()); + return IPAddress.NetworkToHostOrder((short)input.ReadInt16()); } private string DeserializeString() { string arg; int val_len; char[] data; - val_len = (ushort)IPAddress.NetworkToHostOrder((ushort)input.ReadUInt16()); + val_len = DeserializeInt(); if (val_len != 0) { data = input.ReadChars(val_len); arg = new String(data); } else - arg = ""; + arg = string.Empty; return arg; } - internal override void Deserialize(out ushort version, out ushort port, out string appId, out string appName) + internal override void Deserialize(out int version, out int port, out string appId, out string appName) { - version = DeserializeShort(); - port = DeserializeShort(); + version = DeserializeInt(); + port = DeserializeInt(); appId = DeserializeString(); appName = DeserializeString(); } - private void Serialize(ushort arg) + private void Serialize(int arg) { - output.Write((ushort)IPAddress.HostToNetworkOrder(arg)); + output.Write((short)IPAddress.HostToNetworkOrder(arg)); } private void Serialize(string arg) { @@ -67,7 +67,7 @@ namespace IvyBus output.Write(arg.ToCharArray()); } - internal override void Serialize(ushort port, string appId, string appName) + internal override void Serialize(int port, string appId, string appName) { Serialize(PROCOCOLVERSION ); Serialize(port); diff --git a/Ivy/IvyWatcher.cs b/Ivy/IvyWatcher.cs index 5a3afc8..b985ada 100644 --- a/Ivy/IvyWatcher.cs +++ b/Ivy/IvyWatcher.cs @@ -16,6 +16,7 @@ namespace IvyBus using System.Configuration; using System.Text; using System.Diagnostics; + using IvyBus.Properties; /// IvyWatcher, A private Class for the Ivy rendezvous /// @@ -25,7 +26,7 @@ namespace IvyBus /// that the broadcast is done using the same socket, which is not a good /// thing. /// - internal class IvyWatcher + internal class IvyWatcher: IDisposable { private Ivy bus; /* master bus controler */ private int port; @@ -43,6 +44,7 @@ namespace IvyBus /// internal IvyWatcher(Ivy bus, String domainaddr, int port) { + int multicast_ttl = 64; // region this.bus = bus; this.port = port; listenThread = new Thread(new ThreadStart(this.Run)); @@ -59,11 +61,13 @@ namespace IvyBus broadcast.Bind(EPhost); //test isMulticastAddress // TODO better check - //if ( group.IsIPv6Multicast ) yes but in IPV4 how to do + //if ( group.IsIPv6Multicast ) //yes but in IPV4 how to do byte[] addr = group.GetAddressBytes(); if ((addr[0] & 0xf0) == 0xe0) { - broadcast.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption( group )); + broadcast.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, multicast_ttl); + broadcast.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(group)); + } // TODO support the Two protocol if (bus.protocolVersion == 4) @@ -73,7 +77,7 @@ namespace IvyBus } catch (IOException e) { - throw new IvyException("IvyWatcher I/O error" + e); + throw new IvyException(Resources.WatcherIOError + e); } } @@ -81,15 +85,15 @@ namespace IvyBus /// public void Run() { - Ivy.traceProtocol("IvyWatcher", "beginning of a watcher Thread"); - - try - { - bool running = true; - while (running) - { - ushort version; - ushort appPort; + Ivy.traceProtocol(Resources.IvyWatcher, "beginning of a watcher Thread"); + + try + { + bool running = true; + while (running) + { + int version; + int appPort; string appId; string appName; IPEndPoint remoteEP; @@ -97,26 +101,26 @@ namespace IvyBus stream.receiveMsg(out remoteEP, out version, out appPort, out appId, out appName); IPAddress remotehost = remoteEP.Address; - Ivy.traceProtocol("IvyWatcher", "Receive Broadcast from " + Dns.GetHostEntry(remotehost).HostName + ":" + remoteEP.Port); - + Ivy.traceProtocol(Resources.IvyWatcher, Resources.WatcherReceive + Dns.GetHostEntry(remotehost).HostName + ":" + remoteEP.Port); + //TODO if ( !isInDomain( remotehost ) ) continue; if (version != stream.ProtocolVersion) { - Ivy.traceError("IvyWatcher","Ignoring bad protocol version " + version + " expected " + stream.ProtocolVersion); + Ivy.traceError(Resources.IvyWatcher, Resources.BadVersion + version + " expected " + stream.ProtocolVersion); continue; } - // filtrage des self Broadcast + /* check if we received our own message. SHOULD ALSO TEST THE HOST */ if (appId == bus.AppId) continue; if ((appPort == bus.applicationPort) && (remotehost.Equals(bus.applicationHost))) continue; - Ivy.traceProtocol("IvyWatcher", "reponse au Broadcast de " + Dns.GetHostEntry(remotehost).HostName + ":" + remoteEP.Port + " port " + appPort + + Ivy.traceProtocol(Resources.IvyWatcher, "reponse au Broadcast de " + Dns.GetHostEntry(remotehost).HostName + ":" + remoteEP.Port + " port " + appPort + " version " + version + " id " + appId + - " name " + appName); + " name " + appName); try { @@ -124,24 +128,29 @@ namespace IvyBus IPEndPoint hostEndPoint = new IPEndPoint(remoteEP.Address, appPort); socket.Blocking = true; socket.Connect(hostEndPoint); - bus.addClient(socket, appName); + IvyClient client = new IvyClient(this.bus, socket, appName, appPort); + client.SendBindings(); } - catch (Exception e) + catch (SocketException e) { - Ivy.traceError("IvyWatcher","can't connect to " + remotehost + " port " + appPort + " \n" + e.Message); + Ivy.traceError(Resources.IvyWatcher, Resources.WatcherConnectError + remotehost + " port " + appPort + " \n" + e.Message); } - - } // while - } - catch (SocketException se) - { - Ivy.traceError("IvyWatcher","watcher socket closed: " + se.Message); - } - catch (IOException ioe) - { - Ivy.traceError("IvyWatcher","watcher thread ended: " + ioe.Message); - } - Ivy.traceProtocol("IvyWatcher", "end of a watcher thread"); + + } // while + } + catch (ObjectDisposedException ex) + { + Ivy.traceError(Resources.IvyWatcher, Resources.WatcherSocketClosed + ex.Message); + } + catch (SocketException se) + { + Ivy.traceError(Resources.IvyWatcher, Resources.WatcherSocketClosed + se.Message); + } + catch (IOException ioe) + { + Ivy.traceError(Resources.IvyWatcher, Resources.WatcherIOException + ioe.Message); + } + Ivy.traceProtocol(Resources.IvyWatcher, "end of a watcher thread"); } /// stops the thread waiting on the broadcast socket @@ -150,7 +159,7 @@ namespace IvyBus { lock (stream) { - Ivy.traceProtocol("IvyWatcher", "begining stopping an IvyWatcher"); + Ivy.traceProtocol(Resources.IvyWatcher, "begining stopping an IvyWatcher"); stream.Close(); if (listenThread != null) { @@ -160,7 +169,7 @@ namespace IvyBus listenThread = null; } // it might not even have been created - Ivy.traceProtocol("IvyWatcher", "ending stopping an IvyWatcher"); + Ivy.traceProtocol(Resources.IvyWatcher, "ending stopping an IvyWatcher"); } } @@ -174,6 +183,30 @@ namespace IvyBus } } - - } + + #region IDisposable Membres + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + // Free other state (managed objects). + } + // Free your own state (unmanaged objects). + // Set large fields to null. + if (stream != null) + { + stream.Close(); + stream = null; + } + } + + #endregion + } } \ No newline at end of file diff --git a/Ivy/Properties/AssemblyInfo.cs b/Ivy/Properties/AssemblyInfo.cs index 96176ce..79c0aa6 100644 --- a/Ivy/Properties/AssemblyInfo.cs +++ b/Ivy/Properties/AssemblyInfo.cs @@ -1,6 +1,7 @@ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Resources; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information @@ -10,7 +11,7 @@ using System.Runtime.InteropServices; [assembly: AssemblyDescription("Dll de communications sur le bus IVY")] [assembly: AssemblyCompany("DTI/R&D PII")] [assembly: AssemblyProduct("Ivy")] -[assembly: AssemblyCopyright("SDER")] +[assembly: AssemblyCopyright("DTI/R&D")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -25,7 +26,7 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Revision and Build Numbers // by using the '*' as shown below: -[assembly: AssemblyVersion("2.0.*")] +[assembly: AssemblyVersion("2.2.*")] // // In order to sign your assembly you must specify a key to use. Refer to the @@ -60,3 +61,4 @@ using System.Runtime.InteropServices; [assembly: ComVisibleAttribute(false)] +[assembly: NeutralResourcesLanguageAttribute("en")] diff --git a/Ivy/Properties/Settings.Designer.cs b/Ivy/Properties/Settings.Designer.cs index 3356385..6e12b49 100644 --- a/Ivy/Properties/Settings.Designer.cs +++ b/Ivy/Properties/Settings.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // Ce code a été généré par un outil. -// Version du runtime :2.0.50727.312 +// Version du runtime :2.0.50727.1434 // // Les modifications apportées à ce fichier peuvent provoquer un comportement incorrect et seront perdues si // le code est régénéré. @@ -12,7 +12,7 @@ namespace IvyBus.Properties { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "8.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "9.0.0.0")] internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); -- cgit v1.1