Introduction
Many people have used IPMessager, an old Win32 program, which can send messages and files between machines of a Local Area Network. I redeveloped the application according to its protocol using C# 3.5 and WPF. It's a good way to learn both WPF programming and Network programming with .NET Framework.
Background
IP Messenger is a Send/Receive message service using the TCP/UDP Port (default:2425). Messages are sent by UDP and files are sent by TCP. All messages are of the following format:
Version : PacketNo : SenderName : SenderHost : CommandNo : AdditionalSection
For example, "1:100:shirouzu:jupiter:32:Hello
".
Currently the version number is 1, PacketNo
is used to identify the message, the message type and options are determined by the CommandNo
.
For details of Member recognition, Send/Receive Message, transfer file attachment, etc., you can read the protocol document.
Using the Code
One added functionality is to choose Network interface in case you have multiple network cards.
The following code can get all active IPv4 network addresses:
public static List<IPAddress> GetIPv4NetworkInterfaces()
{
List<IPAddress> IPv4Addresses = new List<IPAddress>();
NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces();
foreach (NetworkInterface adapter in nics)
{
if (adapter.NetworkInterfaceType == NetworkInterfaceType.Loopback
|| adapter.OperationalStatus != OperationalStatus.Up)
{
continue;
}
if (adapter.Supports(NetworkInterfaceComponent.IPv4))
{
IPInterfaceProperties adapterProperties = adapter.GetIPProperties();
foreach (UnicastIPAddressInformation ipAddress in
adapterProperties.UnicastAddresses)
{
if (ipAddress.Address.AddressFamily == AddressFamily.InterNetwork)
{
IPv4Addresses.Add(ipAddress.Address);
}
}
}
}
return IPv4Addresses;
}
Use UdpClient
to send/receive message.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
namespace IPMessager
{
public delegate void UdpMessageReceivedDelegate(byte[] message, IPEndPoint remoteEP);
public class UdpService
{
public int PortNumber;
bool listening = false;
UdpClient udpClient;
public UdpMessageReceivedDelegate MessageReceived;
public void Start(IPEndPoint ipEndPoint)
{
if (udpClient != null)
{
Stop();
}
PortNumber = ipEndPoint.Port;
udpClient = new UdpClient(ipEndPoint);
udpClient.EnableBroadcast = true;
listening = true;
udpClient.BeginReceive(new AsyncCallback(OnRreceive), null);
}
public void Stop()
{
listening = false;
if (udpClient != null)
{
udpClient.Client.Shutdown(SocketShutdown.Both);
udpClient.Close();
}
}
void OnRreceive(IAsyncResult result)
{
if (listening)
{
try
{
IPEndPoint remoteEP = new IPEndPoint(IPAddress.Any, PortNumber);
byte[] data = udpClient.EndReceive(result, ref remoteEP);
if (MessageReceived != null)
{
MessageReceived(data, remoteEP);
}
}
catch (ArgumentException)
{
}
}
if (listening)
{
udpClient.BeginReceive(new AsyncCallback(OnRreceive), null);
}
}
public void SendMessage(byte[] message)
{
SendMessage(message, new IPEndPoint(IPAddress.Broadcast, PortNumber));
}
public void SendMessage(byte[] data, IPEndPoint target)
{
udpClient.Send(data, data.Length, target);
}
}
}
Use TcpClient
to send/receive files. TcpService
is for listening to TCP connection.
using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace IPMessager
{
public delegate void TcpConnectionAcceptedDelegate(TcpConnection tcpConnection);
public class TcpService
{
IPEndPoint selfIPEndPoint;
TcpListener tcpListener;
bool listening = false;
public TcpConnectionAcceptedDelegate TcpConnectionAccepted;
public void Start(IPEndPoint ipEndPoint)
{
selfIPEndPoint = ipEndPoint;
tcpListener = new TcpListener(selfIPEndPoint);
tcpListener.Server.SetSocketOption(SocketOptionLevel.Socket,
SocketOptionName.ReuseAddress, true);
tcpListener.Server.NoDelay = true;
tcpListener.Start();
listening = true;
tcpListener.BeginAcceptTcpClient(new AsyncCallback(OnAcceptTcpCliet), null);
}
void OnAcceptTcpCliet(IAsyncResult result)
{
if (listening)
{
TcpClient client = tcpListener.EndAcceptTcpClient(result);
client.Client.SetSocketOption(SocketOptionLevel.Socket,
SocketOptionName.ReuseAddress, true);
client.Client.NoDelay = true;
if (TcpConnectionAccepted != null)
{
Thread thread = new Thread(new ParameterizedThreadStart(
delegate(object obj)
{
TcpConnectionAccepted((TcpConnection)obj);
}));
thread.Start(new TcpConnection(client));
}
}
if (listening)
{
tcpListener.BeginAcceptTcpClient
(new AsyncCallback(OnAcceptTcpCliet), null);
}
}
public void Stop()
{
listening = false;
if (tcpListener != null)
{
tcpListener.Stop();
}
}
}
}
TcpConnection
is for sending/receiving data through TCP.
using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Net;
using System.Net.Sockets;
namespace IPMessager
{
public class TcpConnection
{
IPEndPoint selfIPEndPoint;
TcpClient tcpClient;
NetworkStream networkStream;
public TcpConnection(IPEndPoint ipEndPoint)
{
selfIPEndPoint = ipEndPoint;
}
public TcpConnection(TcpClient client)
{
tcpClient = client;
networkStream = tcpClient.GetStream();
}
public IPEndPoint RemoteEndPoint
{
get { return (IPEndPoint)tcpClient.Client.RemoteEndPoint; }
}
public bool Open(IPEndPoint ipEndPoint)
{
tcpClient = new TcpClient();
tcpClient.Client.SetSocketOption
(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
tcpClient.Connect(ipEndPoint);
if (tcpClient.Connected)
{
networkStream = tcpClient.GetStream();
return true;
}
return false;
}
public void Close()
{
tcpClient.Client.Shutdown(SocketShutdown.Both);
networkStream.Close();
tcpClient.Close();
}
public void Send(byte[] data)
{
networkStream.Write(data, 0, data.Length);
}
public void Send(Stream stream, Action<long> progressUpdate)
{
long bytesTransferred = 0;
int bytesRead = 1;
byte[] buffer = new byte[tcpClient.SendBufferSize];
while (stream.CanRead && bytesRead > 0)
{
bytesRead = stream.Read(buffer, 0, buffer.Length);
networkStream.Write(buffer, 0, bytesRead);
bytesTransferred += bytesRead;
if (progressUpdate != null)
{
progressUpdate(bytesTransferred);
}
}
}
public byte[] Receive()
{
int loop = 0;
while (loop < 1000)
{
if (networkStream.DataAvailable)
{
byte[] data = new byte[tcpClient.Available];
networkStream.Read(data, 0, data.Length);
return data;
}
System.Threading.Thread.Sleep(1);
loop++;
}
return null;
}
public long Receive(Stream stream, long bytesToReceive, Action<long> progressUpdate)
{
long bytesTransferred = 0;
int bytesRead = 0;
byte[] buffer = new byte[tcpClient.ReceiveBufferSize];
int loop = 0;
while (loop < 1000 && bytesTransferred < bytesToReceive)
{
while (networkStream.DataAvailable && bytesTransferred < bytesToReceive)
{
loop = 0;
int bytesToRead = (int)(bytesToReceive - bytesTransferred);
if (bytesToRead > buffer.Length)
{
bytesToRead = buffer.Length;
}
bytesRead = networkStream.Read(buffer, 0, bytesToRead);
stream.Write(buffer, 0, bytesRead);
bytesTransferred += bytesRead;
if (progressUpdate != null)
{
progressUpdate(bytesTransferred);
}
}
System.Threading.Thread.Sleep(1);
loop++;
}
stream.Flush();
return bytesTransferred;
}
}
}
Points of Interest
I encountered some problems when using wireless network, any improvement is welcome. The latest source code is available on the Google Code website.
History
- 2010-01-19
- Created User Interface
- Implemented Member recognition and Send/Receive Message
- 2010-01-20
- Implemented Receive files
- 2010-01-21
- Did code refactoring
- Implemented Send files
- 2010-01-22
- 2010-01-25
- Improved attachment list of send message window:
- remove attachment
- display file icon
- attach folder
- clear all
- Implemented Receive and Send folder
- 2010-01-26
- Added two Tabs to show sending files and logs
- 2010-01-27
- Added an option dialog to configure some options
- 2010-01-28
- Allowed drag files to attachment list
- Added a Reply button to MessageWindows
- Ensured only one instance of the application is running
- 2010-01-29
- 2010-02-08
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.