/// François-Régis Colin /// http://www.tls.cena.fr/products/ivy/ /// * /// (C) CENA /// * namespace IvyBus { using System; using System.Threading; using System.IO; using System.Net; using System.Net.Sockets; using System.Text.RegularExpressions; using System.Configuration; using System.Text; using System.Diagnostics; using IvyBus.Properties; /// IvyWatcher, A private Class for the Ivy rendezvous /// /// right now, the rendez vous is either an UDP socket or a TCP multicast. /// The watcher will answer to /// each peer advertising its arrival on the bus. The intrinsics of Unix are so /// that the broadcast is done using the same socket, which is not a good /// thing. /// internal class IvyWatcher: IDisposable { private Ivy bus; /* master bus controler */ private int port; private volatile Thread listenThread; private IPAddress group; private IvyUDPStream stream; /// creates an Ivy watcher /// /// the bus /// /// the domain /// /// the port number /// 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)); listenThread.Name = "Ivy UDP Listener Thread"; try { group = IPAddress.Parse(domainaddr); /* supervision socket */ // To do reuseaddr we must use a Socket not a udp client Socket broadcast = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); IPEndPoint EPhost = new IPEndPoint(IPAddress.Any, port); broadcast.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast,true); broadcast.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress,true); broadcast.Bind(EPhost); //test isMulticastAddress // TODO better check //if ( group.IsIPv6Multicast ) //yes but in IPV4 how to do byte[] addr = group.GetAddressBytes(); if ((addr[0] & 0xf0) == 0xe0) { 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) stream = new IvyUDPStreamV4(broadcast); else stream = new IvyUDPStreamV3(broadcast); } catch (IOException e) { throw new IvyException(Resources.WatcherIOError + e); } } /// /// the behaviour of each thread watching the UDP socket. /// private void Run() { 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; stream.receiveMsg(out remoteEP, out version, out appPort, out appId, out appName); IPAddress remotehost = remoteEP.Address; Ivy.traceProtocol(Resources.IvyWatcher, Resources.WatcherReceive + Dns.GetHostEntry(remotehost).HostName + ":" + remoteEP.Port); //TODO if ( !isInDomain( remotehost ) ) continue; if (version != stream.ProtocolVersion) { Ivy.traceError(Resources.IvyWatcher, Resources.BadVersion + version + " expected " + stream.ProtocolVersion); continue; } /* 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(Resources.IvyWatcher, "reponse au Broadcast de " + Dns.GetHostEntry(remotehost).HostName + ":" + remoteEP.Port + " port " + appPort + " version " + version + " id " + appId + " name " + appName); try { Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); IPEndPoint hostEndPoint = new IPEndPoint(remoteEP.Address, appPort); socket.Blocking = true; socket.Connect(hostEndPoint); IvyClient client = new IvyClient(this.bus, socket, appName, appPort); client.SendBindings(); } catch (SocketException e) { Ivy.traceError(Resources.IvyWatcher, Resources.WatcherConnectError + remotehost + " port " + appPort + " \n" + e.Message); } } // 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 /// internal virtual void stop() { lock (stream) { Ivy.traceProtocol(Resources.IvyWatcher, "begining stopping an IvyWatcher"); stream.Close(); if (listenThread != null) { // Wait for Thread to end. bool term = listenThread.Join(10000); if (!term && (listenThread != null)) listenThread.Abort(); listenThread = null; } // it might not even have been created Ivy.traceProtocol(Resources.IvyWatcher, "ending stopping an IvyWatcher"); } } /// /// send the boradcst message on all domains /// internal virtual void start() { lock (stream) { listenThread.Start(); IPEndPoint EPhost = new IPEndPoint(group, port); stream.sendMsg(EPhost, bus.applicationPort, bus.AppId, bus.AppName);// notifies our arrival on each domain: protocol version + port } } #region IDisposable Membres public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (disposing) { // Free other state (managed objects). if (stream != null) { stream.Close(); stream = null; } } // Free your own state (unmanaged objects). // Set large fields to null. } #endregion } }