I am in the process of developing an application which sends information to client machines from another application (some sort of an Internal Messaging System or Desktop Alerts). Initially, I had thought of using the old school NETSEND
command but there are issues with it, and the main hindrance for me not using it is that it is not in the newer versions of Windows and you cannot really control how it looks and feels so I ended up developing my own.
There are a lot of options before I started like Message Queuing, Remoting and others but I ended up with two methods of delivering my message. This post will show you the basics on how to do it in those two methods.
Socket Programming
First, we go to Socket Programming. With this method, you will be requiring a lot of work and testing as this is the most Raw as you can get. Having said that this will be a lot faster as you develop things that you only need, so using this method, you will have to create your own protocol, serialization (if you need), security features, etc.
Now for our sample, we need only 2 components - the Receiver and Sender. We will be developing it using Windows forms and here is how it goes.
Receiver Form - This will be responsible for receiving messages from the sender, so this should always run in the background and listens in full-time so any messages sent are received instantaneously.
using System;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
namespace Receiver
{
public partial class Form1 : Form
{
public AsyncCallback oWorkerCallBack;
public Socket oSocketListener;
public Socket oSocketWorker;
public int ListeningPort = 8000;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
Listen();
}
private void Listen()
{
try
{
oSocketListener = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
IPEndPoint oLocalIP = new IPEndPoint(IPAddress.Any, ListeningPort);
oSocketListener.Bind(oLocalIP);
oSocketListener.Listen(10);
oSocketListener.BeginAccept(new AsyncCallback(OnConnect), null);
lblApplicationMessage.Text = "Listening on Port : " + ListeningPort;
}
catch (SocketException ex)
{
MessageBox.Show(ex.Message);
}
}
public void OnConnect(IAsyncResult oAsyncResult)
{
try
{
oSocketWorker = oSocketListener.EndAccept(oAsyncResult);
WaitForData(oSocketWorker);
}
catch (ObjectDisposedException)
{
System.Diagnostics.Debugger.Log(0, "1",
"\n OnConnect: Socket has been closed\n");
}
catch (SocketException ex)
{
MessageBox.Show(ex.Message);
}
}
public void WaitForData(System.Net.Sockets.Socket oSocket)
{
try
{
if (oWorkerCallBack == null)
oWorkerCallBack = new AsyncCallback(OnReceive);
SocketPacket oSocketPacket = new SocketPacket();
oSocketPacket.oSocket = oSocket;
oSocket.BeginReceive(oSocketPacket.bDataBuffer, 0,
oSocketPacket.bDataBuffer.Length, SocketFlags.None, oWorkerCallBack,
oSocketPacket);
}
catch (SocketException ex)
{
MessageBox.Show(ex.Message);
}
}
public void OnReceive(IAsyncResult oAsyncResult)
{
try
{
SocketPacket oSocketID = (SocketPacket)oAsyncResult.AsyncState;
int iRecieve = 0;
iRecieve = oSocketID.oSocket.EndReceive(oAsyncResult);
char[] chars = new char[iRecieve + 1];
System.Text.Decoder d = System.Text.Encoding.UTF8.GetDecoder();
int iCharLen = d.GetChars(oSocketID.bDataBuffer, 0, iRecieve, chars, 0);
string sData = new string(chars);
txtMessageRecieved.Text = txtMessageRecieved.Text + sData;
WaitForData(oSocketWorker);
}
catch (ObjectDisposedException)
{
System.Diagnostics.Debugger.Log(0, "1",
"\n OnReceive: Socket has been closed\n");
}
catch (SocketException ex)
{
MessageBox.Show(ex.Message);
}
}
public class SocketPacket
{
public System.Net.Sockets.Socket oSocket;
public byte[] bDataBuffer = new byte[1];
}
}
}
Sender Form – This will be the one sending the messages to the receiver to this is only used when needed.
using System;
using System.Windows.Forms;
using System.Net.Sockets;
namespace Sender
{
public partial class Form1 : Form
{
Socket oSocket = null;
public string sRecieverIP = "10.10.10.10";
public int iRecieverPort = 8000;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
Connect();
}
private void btnSend_Click(object sender, EventArgs e)
{
try
{
Object oData = txtMessage.Text;
byte[] bData = System.Text.Encoding.ASCII.GetBytes(oData.ToString());
oSocket.Send(bData);
}
catch (SocketException se)
{
MessageBox.Show(se.Message);
}
}
private Socket Connect()
{
try
{
oSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream,
ProtocolType.Tcp);
System.Net.IPAddress oIPAddress = System.Net.IPAddress.Parse(sRecieverIP);
System.Net.IPEndPoint oEndPoint = new System.Net.IPEndPoint(oIPAddress,
iRecieverPort);
oSocket.Connect(oEndPoint);
return oSocket;
}
catch (SocketException se)
{
MessageBox.Show(se.Message);
return null;
}
}
private void Disconnect(Socket oSocket)
{
oSocket.Shutdown(SocketShutdown.Both);
oSocket.Close();
}
}
}
WCF or Windows Communication Foundation
Now let’s go to WCF or Windows Communication Foundation, it is an application programming interface in the .NET Framework for building connected, service-oriented applications like the one we want to achieve. We also need to know that the base of WCF is also Socket programming that’s why if you use this approach, most of the work is done for you and you will see how easy it is to implement from the codes below. With WCF, you get plumbing, serialization, protocols, security features, etc. and all you need is to choose
the features you need and configure. Now with that overhead, this will be slower than the first method.
We will be creating the same 2 forms in this method but we need an additional one to implement a service and we also need an Application configuration to define the endpoint configuration.
WCF Main Receiver Form – This is used for service implementation, I used Windows Form for ease of use in debugging but you can implement it as a service, console or any way you want the invoke the receiver form.
using System;
using System.Windows.Forms;
using System.ServiceModel;
using System.ServiceProcess;
namespace WCFReceiver
{
public partial class Form1 : Form
{
public ServiceHost oServiceHost = null;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
if (oServiceHost != null)
{
oServiceHost.Close();
}
oServiceHost = new ServiceHost(typeof(NotificationService));
oServiceHost.Open();
}
}
[ServiceContract(Namespace = "http://WCFReceiver")]
public interface INotifyClient
{
[OperationContract]
bool SetPopUp(string sMessage);
}
public class NotificationService : INotifyClient
{
public bool SetPopUp(string sMessage)
{
try
{
FormMsgReciever oPopup = new FormMsgReciever(sMessage);
oPopup.Show();
return true;
}
catch
{
return false;
}
}
}
}
Pop Up Form - This is the receiver form to show the messages coming from the sender.
using System;
using System.Windows.Forms;
namespace WCFReceiver
{
public partial class FormMsgReciever : Form
{
private string sRecievedMessage;
public FormMsgReciever(string sMessage)
{
InitializeComponent();
sRecievedMessage = sMessage;
}
private void FormMsgReciever_Load(object sender, EventArgs e)
{
txtMessageRecieved.Text = sRecievedMessage;
}
}
}
Application Config - This is the configuration file to define endpoints
="1.0"="utf-8"
<configuration>
<system.serviceModel>
<services>
<service name="WCFReceiver.NotificationService"
behaviorConfiguration="NotificationServiceBehavior">
<host>
<baseAddresses>
<add baseAddress="http://localhost:8005/Notifier/service"/>
</baseAddresses>
</host>
<endpoint address="" binding="wsHttpBinding"
contract="WCFReceiver.INotifyClient" />
<endpoint address="mex" binding="mexHttpBinding"
contract="IMetadataExchange" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="NotificationServiceBehavior">
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="False"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
Once you have all of the 3 items above ready, build it then execute it so you can set a reference for the service reference your sender application.
Make sure you run it as an administrator.
Otherwise, you will encounter this message “HTTP could not register URL http://+:portnumber/Directory/. Your process does not have access rights to this namespace
“.
Once all OK, add it as a reference to your Sender project.
WCF Sender – This will be the form used in sending the messages.
using System;
using System.Windows.Forms;
namespace WCFSender
{
public partial class Form1 : Form
{
public string sRecieverIP = "10.10.10.10";
public int iRecieverPort = 8000;
public string sEndpointConfigName = "WSHttpBinding_INotifyClient";
public Form1()
{
InitializeComponent();
}
private void btnSend_Click(object sender, EventArgs e)
{
string sServiceAddress = "http://" + sRecieverIP + ":" + 8000 +
"/Notifier/service";
ServiceReference1.NotifyClientClient oNotify =
new ServiceReference1.NotifyClientClient(sEndpointConfigName, sServiceAddress);
bool bSuccess = oNotify.SetPopUp(txtMessage.Text);
if (!bSuccess)
{
MessageBox.Show("Error in sending message");
}
Application.Exit();
}
}
}
Now for my choice of method, it’s all up to you what suits best as I had given the pros and cons of each method, you just have to weigh which best works in your situation.