WCF Application Manager example
part 1:http://bresleveloper.blogspot.com/2012/05/wcf-broadcast-from-server-example.html
quote myself:
"lets say that i want to control from my computer (\machine) all the programs, that i create, of all the users in all the cpu's in our LAN... so i need a server that all the programs will connect to him and he will be able to tell them to do stuff... that sound like FUN! and i'll show it in WCF!"
this is part 2 the actual manager:
the Architecture: since i want to put the client component in all my programs i wanna do this smart and dynamicly so i'll do a dll with both interfaces (cuz server and client must both know both interfaces), the client implementation, and (suprise!) i want the client class to be private to the dll and expose an AutoClient object to do everything w/o exposing a single method.
*notice that for multiple computer u'll need the TCP binding, at the very end...
lets start from the begining:
the interfaces (each in his own CS):
for the manager(server)
[ServiceContract(SessionMode = SessionMode.Required,
CallbackContract = typeof(IClient))]
public interface IManager
{
[OperationContract]
bool Register();
[OperationContract]
bool IsServerAlive();
}
u'll soon notice that the class will have more method, in the interface i need to put only the methods that i want to expose for the client (other wise the client will be able to broadcast, like in part 1);
for the client:
public interface IClient
{
[OperationContract]
string ReturnClientName();
[OperationContract(IsOneWay = true)]
void CloseClient();
[OperationContract(IsOneWay = true)]
void RaiseFormMsgBox(string msg);
}
as you can see if i want the client to return something i need the contract not to be one way.
client class (yep, MsgBox in the dll, using System.Windows.Forms) class cClient : IClient
{
public string ReturnClientName()
{
return string.Format("{0}/{1}/{2}",
Environment.UserDomainName,
Environment.UserName,
Process.GetCurrentProcess().ProcessName);
}
public void CloseClient()
{
Process.GetCurrentProcess().Kill();
}
public void RaiseFormMsgBox(string msg)
{
try
{
object oMsg = msg;
Thread t = new Thread(
new ParameterizedThreadStart(RaiseMsgBoxThread));
t.Start(oMsg);
} catch { }//it is important to do a thread otherwise the server will wait for the
OK click from the client!
}
public void RaiseFormMsgBoxThread(object oMsg)
{
try
{
MessageBox.Show(oMsg.ToString());
} catch { }//you can do here cw or whatever for WPF ect.
}
}
manager class (not in the dll, should not be exposed...)
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]
class cManager : IManager
{
List<IClient> clientsPrograms = new List<IClient>();
private void VefiryList()
{
List<IClient> newList = new List<IClient>();
clientsPrograms.ForEach(delegate(IClient c)
{
if (((ICommunicationObject)c).State !=
CommunicationState.Opened)
clientsPrograms.Remove(c);
});
}
public bool Register()
{
clientsPrograms.Add(
OperationContext.Current.GetCallbackChannel<IClient>());
return true;
}
public bool IsServerAlive()
{ return true; }
public List<string> GetAllClientsNames()
{
VefiryList();
List<string> names = new List<string>();
clientsPrograms.ForEach(delegate(IClient c)
{
names.Add(c.ReturnClientName());
});
return names;
}
public void CloseAllClients()
{
VefiryList();
clientsPrograms.ForEach(delegate(IClient c)
{
c.CloseClient();
});
}
public void SendMsgBoxToClients()
{
VefiryList();
clientsPrograms.ForEach(delegate(IClient c)
{ c.RaiseFormMsgBox(); });
}
}
now all you need to do is make a proj that implement the cManage, another the cClient, both should have/know the interfaces and open the host/channels just like in part 1 and ta da!!
what i did is a winform that implement cManager, and a dll with 4 CSs:
1 - IManager.cs (public interface)
2 - IClient.cs (public interface)
3 - cClient.cs (private class)
4 - AutoClient.cs (public class)
so that the client program wont have much to do but a simple
AutoClient client = new AutoClient();
and thats it, so here is AutoClient:
public class AutoClient
{
cClient client;
DuplexChannelFactory<IManager> pipeFactory;
IManager pipeProxy;
Timer timerAllAlive;
public AutoClient()
{
client = new cClient(); timerAllAlive = new Timer();
//Interval is in milliseconds
int mlsToSec = 1000, mlsToMin = 60 * mlsToSec;
timerAllAlive.Interval = 5 * mlsToMin;
timerAllAlive.Elapsed += new ElapsedEventHandler(timerAllAlive_Elapsed);
timerAllAlive.Start();
AwakeAll();
}
void timerAllAlive_Elapsed(object sender, ElapsedEventArgs e)
{
if (TestAllAlive())
return;
else
AwakeAll();
}
bool TestAllAlive()
{
if (pipeFactory.State != CommunicationState.Opened)
return false;
try
{
return pipeProxy.IsServerAlive();
}
catch
{
return false;
}
}
bool AwakeAll()
{
try
{
NetTcpBinding tcp = new NetTcpBinding(SecurityMode.None);
pipeFactory =
new DuplexChannelFactory<IManager>(
client,
tcp, new EndpointAddress(
"net.tcp://vm-*****:1710/Networking/AppLord"));
pipeProxy = pipeFactory.CreateChannel();
pipeProxy.Register(); return true;
}
catch
{
return false;
}
}
}
*notice i put here the TCP binding, cuz for more than 1 machine u cant use the NamedPipe. yes you can use pipe even with the internet but as for the WCF NetNamedPipeBinding its something special, read more in msdn, use it only when your developing this on ur cpu.
as for the host that the Uri (p.s. i did in w/o security cuz i dont need it but notice it)
"net.tcp://localhost:1710/Networking", while "vm-*****" is the computer name.
i was very proud with myself, considering 4 days ago i didnt know a thing about WCF except some theory, i hope u r 2 :D
quote myself:
"lets say that i want to control from my computer (\machine) all the programs, that i create, of all the users in all the cpu's in our LAN... so i need a server that all the programs will connect to him and he will be able to tell them to do stuff... that sound like FUN! and i'll show it in WCF!"
this is part 2 the actual manager:
the Architecture: since i want to put the client component in all my programs i wanna do this smart and dynamicly so i'll do a dll with both interfaces (cuz server and client must both know both interfaces), the client implementation, and (suprise!) i want the client class to be private to the dll and expose an AutoClient object to do everything w/o exposing a single method.
*notice that for multiple computer u'll need the TCP binding, at the very end...
lets start from the begining:
the interfaces (each in his own CS):
for the manager(server)
[ServiceContract(SessionMode = SessionMode.Required,
CallbackContract = typeof(IClient))]
public interface IManager
{
[OperationContract]
bool Register();
[OperationContract]
bool IsServerAlive();
}
u'll soon notice that the class will have more method, in the interface i need to put only the methods that i want to expose for the client (other wise the client will be able to broadcast, like in part 1);
for the client:
public interface IClient
{
[OperationContract]
string ReturnClientName();
[OperationContract(IsOneWay = true)]
void CloseClient();
[OperationContract(IsOneWay = true)]
void RaiseFormMsgBox(string msg);
}
as you can see if i want the client to return something i need the contract not to be one way.
client class (yep, MsgBox in the dll, using System.Windows.Forms) class cClient : IClient
{
public string ReturnClientName()
{
return string.Format("{0}/{1}/{2}",
Environment.UserDomainName,
Environment.UserName,
Process.GetCurrentProcess().ProcessName);
}
public void CloseClient()
{
Process.GetCurrentProcess().Kill();
}
public void RaiseFormMsgBox(string msg)
{
try
{
object oMsg = msg;
Thread t = new Thread(
new ParameterizedThreadStart(RaiseMsgBoxThread));
t.Start(oMsg);
} catch { }//it is important to do a thread otherwise the server will wait for the
OK click from the client!
}
public void RaiseFormMsgBoxThread(object oMsg)
{
try
{
MessageBox.Show(oMsg.ToString());
} catch { }//you can do here cw or whatever for WPF ect.
}
}
manager class (not in the dll, should not be exposed...)
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]
class cManager : IManager
{
List<IClient> clientsPrograms = new List<IClient>();
private void VefiryList()
{
List<IClient> newList = new List<IClient>();
clientsPrograms.ForEach(delegate(IClient c)
{
if (((ICommunicationObject)c).State !=
CommunicationState.Opened)
clientsPrograms.Remove(c);
});
}
public bool Register()
{
clientsPrograms.Add(
OperationContext.Current.GetCallbackChannel<IClient>());
return true;
}
public bool IsServerAlive()
{ return true; }
public List<string> GetAllClientsNames()
{
VefiryList();
List<string> names = new List<string>();
clientsPrograms.ForEach(delegate(IClient c)
{
names.Add(c.ReturnClientName());
});
return names;
}
public void CloseAllClients()
{
VefiryList();
clientsPrograms.ForEach(delegate(IClient c)
{
c.CloseClient();
});
}
public void SendMsgBoxToClients()
{
VefiryList();
clientsPrograms.ForEach(delegate(IClient c)
{ c.RaiseFormMsgBox(); });
}
}
now all you need to do is make a proj that implement the cManage, another the cClient, both should have/know the interfaces and open the host/channels just like in part 1 and ta da!!
what i did is a winform that implement cManager, and a dll with 4 CSs:
1 - IManager.cs (public interface)
2 - IClient.cs (public interface)
3 - cClient.cs (private class)
4 - AutoClient.cs (public class)
so that the client program wont have much to do but a simple
AutoClient client = new AutoClient();
and thats it, so here is AutoClient:
public class AutoClient
{
cClient client;
DuplexChannelFactory<IManager> pipeFactory;
IManager pipeProxy;
Timer timerAllAlive;
public AutoClient()
{
client = new cClient(); timerAllAlive = new Timer();
//Interval is in milliseconds
int mlsToSec = 1000, mlsToMin = 60 * mlsToSec;
timerAllAlive.Interval = 5 * mlsToMin;
timerAllAlive.Elapsed += new ElapsedEventHandler(timerAllAlive_Elapsed);
timerAllAlive.Start();
AwakeAll();
}
void timerAllAlive_Elapsed(object sender, ElapsedEventArgs e)
{
if (TestAllAlive())
return;
else
AwakeAll();
}
bool TestAllAlive()
{
if (pipeFactory.State != CommunicationState.Opened)
return false;
try
{
return pipeProxy.IsServerAlive();
}
catch
{
return false;
}
}
bool AwakeAll()
{
try
{
NetTcpBinding tcp = new NetTcpBinding(SecurityMode.None);
pipeFactory =
new DuplexChannelFactory<IManager>(
client,
tcp, new EndpointAddress(
"net.tcp://vm-*****:1710/Networking/AppLord"));
pipeProxy = pipeFactory.CreateChannel();
pipeProxy.Register(); return true;
}
catch
{
return false;
}
}
}
*notice i put here the TCP binding, cuz for more than 1 machine u cant use the NamedPipe. yes you can use pipe even with the internet but as for the WCF NetNamedPipeBinding its something special, read more in msdn, use it only when your developing this on ur cpu.
as for the host that the Uri (p.s. i did in w/o security cuz i dont need it but notice it)
"net.tcp://localhost:1710/Networking", while "vm-*****" is the computer name.
i was very proud with myself, considering 4 days ago i didnt know a thing about WCF except some theory, i hope u r 2 :D
Can I get working sample of the above code.
ReplyDelete