Introduction
In a WPF application, inter-process connectivity typically involves communication between different components or modules within the same application or between separate WPF applications running on the same system or across different systems.
Methods of Inter-Process Communication in WPF
There are various methods to achieve inter-process communication (IPC) in WPF
1. Named Pipes
Named pipes enable two processes to communicate by creating a named pipe. WPF applications can utilize named pipes to establish communication between them. .NET provides classes such as NamedPipeServerStream and NamedPipeClientStream for working with named pipes.
2. WCF (Windows communication foundation)
WCF is a framework designed for building service-oriented applications. WPF applications can utilize WCF to communicate with other applications or services over different transport protocols like TCP/IP, HTTP, named pipes, etc.
3. TCP/IP Sockets
WPF applications can communicate with each other or with other applications/services using TCP/IP sockets. .NET provides classes like TcpListener and TcpClient for implementing socket-based communication.
Using TCP/IP sockets in C# offers several advantages and disadvantages
Advantages
- Platform compatibility: TCP/IP sockets enable network communication between different platforms, making it versatile for building networked applications that can run on various operating systems.
- Wide protocol support: TCP/IP is a widely supported protocol suite, making it a standard choice for networking applications. This ensures compatibility with a wide range of devices and systems.
- Reliability: TCP/IP provides reliable, ordered, and error-checked delivery of data, ensuring that data sent over the network is received accurately and in the correct order.
- Full duplex communication: TCP/IP sockets support full-duplex communication, allowing data to be transmitted and received simultaneously, which is essential for many types of client-server applications.
- Built-in .NET framework support: C# provides built-in support for TCP/IP sockets through classes like `TcpListener` and `TcpClient`, making it relatively easy to implement network communication in C# applications.
Disadvantages
- Complexity: Implementing TCP/IP socket communication can be complex, particularly for beginners, due to the need to manage connections, handle timeouts, and address potential network issues such as packet loss or latency.
- Performance overhead: TCP/IP introduces some overhead to data transmission due to its reliability features, such as error-checking and retransmission of lost packets. This can result in slightly higher latency compared to other protocols like UDP.
- Firewall and security concerns: TCP/IP socket communication may require configuration changes to firewalls and network security settings to allow incoming and outgoing connections, which can pose security risks if not properly configured.
- Scalability: While TCP/IP sockets are suitable for many applications, they may not be the optimal choice for highly scalable applications with thousands or millions of simultaneous connections.
I have incorporated the TCP/IP sockets process within the WPF application. To successfully implement this, you must adhere to the subsequent steps.
Implementation of using TCP/IP sockets
Step 1. Develop a project similar to the one outlined below.
Step 2. To facilitate collaboration between the "TCP Server" and "TCP Client", a shared project needs to be established. To address this requirement, I have developed a project called " CommonHelper". Its purpose is to enable seamless communication and cooperation between the Server and Client.
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
namespace CommonHelper
{
public class HelperUtility
{
#region Events
public static event EventHandler<EventMessage> DispatchClientAcknowledgementEvent;
private void OnDispatchClientAcknowledgementEventTrigger(string message)
{
EventMessage eventMessage = new EventMessage
{
DispatchedMessage = message,
};
DispatchClientAcknowledgementEvent?.Invoke(null, eventMessage);
}
public static event EventHandler<EventMessage> TCPReceivedMessageFromEvent;
private void TCPReceivedMessageFromEventTrigger(string message)
{
EventMessage eventMessage = new EventMessage
{
DispatchedMessage = message,
};
TCPReceivedMessageFromEvent?.Invoke(null, eventMessage);
}
#endregion
private const string ServerIpAddress = "127.0.0.9";
private const int ServerPort = 8888;
public TcpClient client;
private TcpListener listener;
private static HelperUtility instance;
private static readonly object objLock = new object();
private HelperUtility() { }
public static HelperUtility Instance
{
get
{
if (instance == null)
{
lock (objLock)
{
if (instance == null)
{
instance = new HelperUtility();
}
}
}
return instance;
}
}
#region TCP CLIENT
public void Initialize()
{
client = new TcpClient();
}
public async void ConnectClientConnection()
{
client = new TcpClient();
if (!client.Connected)
{
await client.ConnectAsync(ServerIpAddress, ServerPort);
}
}
public void CloseClientConnection()
{
client.Close();
}
public async Task SendDataToTCPServer(string sentMessage)
{
try
{
if (client.Connected)
{
string message = sentMessage;
byte[] data = Encoding.ASCII.GetBytes(message);
using (NetworkStream stream = client.GetStream())
{
await stream.WriteAsync(data, 0, data.Length);
byte[] responseBuffer = new byte[1024];
int bytesRead = await stream.ReadAsync(responseBuffer, 0, responseBuffer.Length);
string response = Encoding.ASCII.GetString(responseBuffer, 0, bytesRead);
OnDispatchClientAcknowledgementEventTrigger(response);
CloseClientConnection();
}
}
}
catch (Exception ex)
{
MessageBox.Show($"Error sending message: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
#endregion
#region TCP SERVER
private async Task AcceptClients()
{
while (true)
{
TcpClient client = await listener.AcceptTcpClientAsync();
ReceivedDataByTCPServerFromClient(client);
}
}
public async Task StartTcpServer()
{
try
{
listener = new TcpListener(IPAddress.Any, ServerPort);
if (!listener.Server.Connected)
{
listener.Start();
TCPReceivedMessageFromEventTrigger("Server Started");
await AcceptClients();
}
}
catch (Exception)
{
}
}
public async void ReceivedDataByTCPServerFromClient(TcpClient client)
{
try
{
using (NetworkStream stream = client.GetStream())
{
byte[] buffer = new byte[1024];
int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
string dataReceived = Encoding.ASCII.GetString(buffer, 0, bytesRead);
TCPReceivedMessageFromEventTrigger(dataReceived);
byte[] response = Encoding.ASCII.GetBytes($"Server has received data: {dataReceived}");
await stream.WriteAsync(response, 0, response.Length);
client.Close();
}
}
catch (Exception)
{
}
}
#endregion
}
public class EventMessage : EventArgs
{
public string DispatchedMessage { get; set; }
}
}
Step 3. Develop a “TCP Server” that will receive messages from the TCP Client
Xaml View
<Window x:Class="TcpServer.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TcpServer"
mc:Ignorable="d"
Title="Tcp Server" Height="650" Width="800">
<Grid>
<StackPanel Orientation="Vertical">
<Button x:Name="BtnServerStart" Content="Start Server" Height="50" Click="BtnServerStart_Click" Margin="10"/>
<TextBox Foreground="DarkGreen" FontSize="30" FontWeight="Bold" x:Name="TxtbxMessageReceived" Height="286" BorderBrush="Gray" BorderThickness="2" Margin="10" VerticalScrollBarVisibility="Auto" AcceptsReturn="True"/>
<TextBlock Foreground="Blue" FontSize="20" FontWeight="Bold" x:Name="TxtBlckInformation" Height="230" Margin="10" />
</StackPanel>
</Grid>
</Window>
Code Behind
using CommonHelper;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Windows;
using System.Collections.Generic;
namespace TcpServer
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
StringBuilder sb;
List<string> stringList;
public MainWindow()
{
InitializeComponent();
sb = new StringBuilder();
stringList = new List<string>();
HelperUtility.Instance.Initialize();
HelperUtility.TCPReceivedMessageFromEvent += HelperUtility_TCPReceivedMessageFromEvent;
}
private void HelperUtility_TCPReceivedMessageFromEvent(object? sender, EventMessage e)
{
if (e != null)
{
sb.Append(e.DispatchedMessage);
if (e.DispatchedMessage.Contains("Server Started"))
{
TxtBlckInformation.Text = sb.ToString();
}
else
{
stringList.Add(e.DispatchedMessage + Environment.NewLine);
string concatenatedString = string.Join(" ", stringList);
TxtbxMessageReceived.Text = concatenatedString;
}
}
}
private async void BtnServerStart_Click(object sender, RoutedEventArgs e)
{
try
{
await HelperUtility.Instance.StartTcpServer();
}
catch (Exception)
{
}
}
}
}
Step 4. Develop a “TCP Client” that will send messages to the TCP Server.
Xaml View
<Window x:Class="TcpClientExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TcpClientExample"
mc:Ignorable="d"
Title="Tcp Client" Height="650" Width="800">
<Grid>
<StackPanel Orientation="Vertical">
<Button x:Name="BtnTcpClientConnect" Content="Connect with TCP/IP server" Height="50" Click="BtnTcpClientConnect_Click" Margin="10"/>
<Button Cursor="Hand" x:Name="BtnSendClientData" Content="Send Data" Height="50" Click="BtnSendClientData_Click" Margin="10"/>
<TextBox Foreground="Red" FontSize="30" FontWeight="Bold" AcceptsReturn="True" VerticalScrollBarVisibility="Auto" AcceptsTab="True" x:Name="TxtBxMessage" Height="220" Margin="10" BorderBrush="Gray" BorderThickness="2"/>
<TextBlock Foreground="Blue" FontSize="20" FontWeight="Bold" x:Name="TxtBlckInformation" Height="235" Margin="10" />
</StackPanel>
</Grid>
</Window>
Code Behind
using CommonHelper;
using System.Text;
using System.Windows;
namespace TcpClientExample
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
StringBuilder sb;
public MainWindow()
{
InitializeComponent();
HelperUtility.Instance.Initialize();
sb = new StringBuilder();
HelperUtility.DispatchClientAcknowledgementEvent += HelperUtility_DispatchClientAcknowledgementEvent;
}
private async void BtnSendClientData_Click(object sender, RoutedEventArgs e)
{
HelperUtility.Instance.ConnectClientConnection();
await HelperUtility.Instance.SendDataToTCPServer(TxtBxMessage.Text);
//HelperUtility.Instance.CloseClientConnection();
}
private void HelperUtility_DispatchClientAcknowledgementEvent(object? sender, EventMessage e)
{
if (e != null)
{
sb.AppendLine(e.DispatchedMessage.ToString());
TxtBlckInformation.Text = sb.ToString();
}
}
private void BtnTcpClientConnect_Click(object sender, RoutedEventArgs e)
{
try
{
HelperUtility.Instance.ConnectClientConnection();
sb.AppendLine("Tcp Client Connected to server...");
TxtBlckInformation.Text = sb.ToString();
}
catch (Exception ex)
{
MessageBox.Show($"Error connecting to server: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
}
Step 5. Appearance of the implementation
Note. Kindly adhere to the instructions below to execute the application
- Initiate both the client and server at the same time
- Click on the "Start server" button to commence the server
- Select "Connect with TCP/IP server" to establish a connection between the TCP client and server
- Enter the message in the Textbox that you wish to send to the TCP Server.
- Press the "Send Data" option to transfer data from the client to the server.
Repository path: https://github.com/OmatrixTech/TCpIPExample