/// 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;
/// 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
{
private bool isMulticastAddress = false;
private Ivy bus; /* master bus controler */
private Socket broadcast; /* supervision socket */// To do reuseaddr we must use a Socket not a udp client
private String domainaddr;
private int port;
private volatile Thread listenThread;
private IPAddress group;
/// creates an Ivy watcher
///
/// the bus
///
/// the domain
///
/// the port number
///
internal IvyWatcher(Ivy bus, String domainaddr, int port)
{
this.bus = bus;
this.domainaddr = domainaddr;
this.port = port;
listenThread = new Thread(new ThreadStart(this.Run));
// create the MulticastSocket
try
{
group = Dns.GetHostByName(domainaddr).AddressList[0];
broadcast = new Socket(AddressFamily.InterNetwork, SocketType.Dgram,ProtocolType.Udp);
IPEndPoint EPhost = new IPEndPoint(IPAddress.Any, port);
broadcast.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast,1);
broadcast.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress,1);
broadcast.Bind(EPhost);
//test isMulticastAddress // TODO better check
if ((group.Address & 0xf0000000) == 0xe0000000)
{
isMulticastAddress = true;
broadcast.SetSocketOption(SocketOptionLevel.Udp, SocketOptionName.AddMembership, new MulticastOption( group ));
}
}
catch (IOException e)
{
throw new IvyException("IvyWatcher I/O error" + e);
}
}
/// the behaviour of each thread watching the UDP socket.
///
public void Run()
{
traceDebug("beginning of a watcher Thread");
byte[] buf = new byte[4096];
int len;
IPEndPoint remoteEP = new IPEndPoint(IPAddress.Any, 0);
EndPoint tempRemoteEP = (EndPoint)remoteEP;
remoteEP = null;
try
{
bool running = true;
while (running)
{
int port;
try
{
len = broadcast.ReceiveFrom(buf, ref tempRemoteEP);
// I was summoned to leave during the receive
String msg = Encoding.ASCII.GetString(buf);
remoteEP = (IPEndPoint)tempRemoteEP;
IPAddress remotehost = remoteEP.Address;
traceDebug("BUSWATCHER Receive Broadcast from " + Dns.GetHostName() + ":" + remoteEP.Port);
// TODO if ( !isInDomain( remotehost ) ) continue;
String[] st = msg.Split(new char[]{' ','\n'},3);
if (st.Length != 3)
{
Console.Error.WriteLine("Bad format " + msg);
continue;
}
int version = Int32.Parse(st[0]);
port = Int32.Parse(st[1]);
if (version != Ivy.PROCOCOLVERSION)
{
Console.Error.WriteLine("Ignoring bad protocol version broadcast");
continue;
}
if ((bus.applicationPort == port))
continue;
traceDebug("BUSWATCHER Broadcast de " + Dns.GetHostName() + ":" + remoteEP.Port + " port " + port + " version " + version);
try
{
MyTcpClient socket = new MyTcpClient(remoteEP.Address.ToString(),port);
bus.addClient(socket);
}
catch (Exception e)
{
Console.Error.WriteLine("can't connect to " + remotehost + " port " + port + " \n"+ e.Message);
}
}
catch (IOException jii)
{
running = false;
}
} // while
}
catch (SocketException se)
{
traceDebug( "watcher socket closed" );
}
catch (IOException ioe)
{
Console.Error.WriteLine( ioe.StackTrace );
}
traceDebug("end of a watcher thread");
}
/// stops the thread waiting on the broadcast socket
///
internal virtual void stop()
{
lock(this)
{
traceDebug("begining stopping an IvyWatcher");
Thread t = listenThread;
listenThread = null;
broadcast.Close();
if (t != null)
{
t.Interrupt();
}
// it might not even have been created
traceDebug("ending stopping an IvyWatcher");
}
}
internal virtual void start()
{
lock(this)
{
String hello = Ivy.PROCOCOLVERSION + " " + bus.applicationPort + "\n";
listenThread.Start();
byte[] hellob = Encoding.ASCII.GetBytes( hello );
IPEndPoint EPhost = new IPEndPoint(group, port);
broadcast.SendTo(hellob,0,EPhost); // notifies our arrival on each domain: protocol version + port
}
}
internal static String getDomain(String net)
{
int sep_index = net.LastIndexOf(":");
if (sep_index != - 1)
{
net = net.Substring(0, (sep_index) - (0));
}
try
{
net += ".255.255.255";
Regex exp = new Regex("^(\\d+\\.\\d+\\.\\d+\\.\\d+).*");
net = exp.Replace(net, "$1");
}
catch (ArgumentException e)
{
Console.Out.WriteLine("Bad broascat addr " + net);
return null;
}
// out.println("net: "+net);
return net;
}
internal static int getPort(String net)
{
int sep_index = net.LastIndexOf(":");
int port = (sep_index == - 1)?Ivy.DEFAULT_PORT:Int32.Parse(net.Substring(sep_index + 1));
// out.println("net: ["+net+"]\nsep_index: "+sep_index+"\nport: "+port);
return port;
}
private void traceDebug(String s)
{
if (bus.Debug)
Console.Out.WriteLine("-->ivywatcher<-- " + s);
}
}
// class IvyWatcher
/* EOF */
}