I have a working code and its a window wpf application that start and stop the timer. What i am struggling now to create TCPClient from my code, that will read a serialized and deserialized json object using TCP Socket.
// Requirement is when button is pressed from the start button on my window application.
Pressing Start should start the echo process as described further below;
// TCPClient i took from the microsoft documentation.
static void Connect(String server, String message)
{
try
{
// Create a TcpClient.
// Note, for this client to work you need to have a TcpServer
// connected to the same address as specified by the server, port
// combination.
Int32 port = 13000;
TcpClient client = new TcpClient(server, port);
// Translate the passed message into ASCII and store it as a Byte array.
Byte[] data = System.Text.Encoding.ASCII.GetBytes(message);
// Get a client stream for reading and writing.
// Stream stream = client.GetStream();
NetworkStream stream = client.GetStream();
// Send the message to the connected TcpServer.
stream.Write(data, 0, data.Length);
Console.WriteLine("Sent: {0}", message);
// Receive the TcpServer.response.
// Buffer to store the response bytes.
data = new Byte[256];
// String to store the response ASCII representation.
String responseData = String.Empty;
// Read the first batch of the TcpServer response bytes.
Int32 bytes = stream.Read(data, 0, data.Length);
responseData = System.Text.Encoding.ASCII.GetString(data, 0, bytes);
Console.WriteLine("Received: {0}", responseData);
// Close everything.
stream.Close();
client.Close();
}
catch (ArgumentNullException e)
{
Console.WriteLine("ArgumentNullException: {0}", e);
}
catch (SocketException e)
{
Console.WriteLine("SocketException: {0}", e);
}
Console.WriteLine("\n Press Enter to continue...");
Console.Read();
}
// Front end XAML
<StackPanel Grid.Row="1" Orientation="Horizontal">
<Button Padding="15 5" Margin="5" Content="Start"
Command="{x:Static local:WpfTimerWindow.StartCommand}"
CommandParameter="{x:Static local:WpfTimerWindow.Default}"/>
// Back end C# WPF
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;
using System.Diagnostics;
using System.Timers;
using System.ComponentModel;
namespace PingApplication
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class WpfTimerWindow : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
private readonly Timer timer = new Timer();
private readonly Stopwatch stopwatch = new Stopwatch();
public DateTime Now => DateTime.Now;
public TimeSpan Elasped => stopwatch.Elapsed;
public WpfTimerWindow()
{
//InitializeComponent();
timer.Interval = 50;
timer.Elapsed += OnTick;
timer.Start();
stopwatch.Start();
}
private void OnTick(object? sender, ElapsedEventArgs e)
{
if (PropertyChanged is PropertyChangedEventHandler propertyChanged)
{
propertyChanged(this, NowArgs);
propertyChanged(this, ElaspedArgs);
}
}
public static PropertyChangedEventArgs NowArgs { get; } = new PropertyChangedEventArgs(nameof(Now));
public static PropertyChangedEventArgs ElaspedArgs { get; } = new PropertyChangedEventArgs(nameof(Elasped));
public static RoutedUICommand StartCommand { get; } = new RoutedUICommand("Timer Start", "TimerStart", typeof(WpfTimerWindow));
public static RoutedUICommand ResetCommand { get; } = new RoutedUICommand("Timer Stop", "TimerStop", typeof(WpfTimerWindow));
public static ExecutedRoutedEventHandler ExecuteCommand { get; } = (_, e) =>
{
if (e.Parameter is WpfTimerWindow timer)
{
if (e.Command == StartCommand)
{
timer.stopwatch.Start();
}
else if (e.Command == ResetCommand)
{
timer.stopwatch.Stop();
}
else return;
timer.OnTick(null, null);
}
};
public static CanExecuteRoutedEventHandler CanExecuteCommand { get; } = (_, e) =>
{
if (e.Parameter is WpfTimerWindow timer)
{
if (e.Command == StartCommand)
{
e.CanExecute = !timer.stopwatch.IsRunning;
}
else if (e.Command == ResetCommand)
{
e.CanExecute = timer.stopwatch.IsRunning;
}
}
};
public static WpfTimerWindow Default { get; } = new WpfTimerWindow();
}
}
Try to create separate model:
public class MyModel
{
public Guid Guid { get; set; }
public DateTime Started { get; set; }
public DateTime? Processed { get; set; }
public DateTime? Elapsed { get; set; }
}
Usage (in your client code):
...
var myModel = new MyModel
{
Guid = Guid.NewGuid(),
Started = DateTime.Now,
};
var message = Newtonsoft.Json.JsonConvert.SerializeObject(myModel);
var stream = client.GetStream();
var buffer = Encoding.Unicode.GetBytes(message);
stream.Write(buffer, 0, buffer.Length);
stream.Flush();
...
Usage (in your another client or server code):
...
var stream = client.GetStream();
var buffer = new byte[1024];
var response = new StringBuilder();
var bytes = stream.Read(buffer, 0, buffer.Length);
if (bytes != 0)
{
response.Append(Encoding.Unicode.GetString(buffer, 0, bytes));
while (stream.DataAvailable)
{
bytes = stream.Read(buffer, 0, buffer.Length);
response.Append(Encoding.Unicode.GetString(buffer, 0, bytes));
}
var myModel = Newtonsoft.Json.JsonConvert.DeserializeObject<MyModel>(response);
myModel.Processed = DateTime.Now;
myModel.Elapsed = myModel.Processed - myModel.Started;
// add the myModel to UI
// In case of main thread issues, use App.Current.Dispatcher.Invoke(() => { addition here });
}
...
To develop more complex solution refer to this article about multi-threaded TCPSocket chat server.
Based on the given requirements, we need to create two separate projects (e.g. WPFServer and WPFClient).
The WPFClient project has to include "client code", whenever you want, either in xaml.cs or even in a separate service class (e.g. MyCustomClientSocketService). Notice that you have to have an access to the TcpClient functionality.
So-called echo-tool (according to the question). The project includes "server code". Generally it can be put right in the xaml.cs, but creating separate service class can simplify and increase maintainability of the feature.
Take into account that we have to broadcast the incoming message to available clients after the addition (// add the myModel to UI
), therefore consider to store connected clients in System.Collections.Concurrent.ConcurrentDictionary
. I highly recommend to explore the code from the stated article.