WPF Socket client structuring - c#

my question might seem a bit odd, but what would be the best approach to create a WPF Socket client using the MVVM pattern.
Right now on my ViewModel, i create a thread, which attempts to connect to the server in a while loop, and wait for connection, after it's connected it gets data from the server.
Is there a better way to make it so i won't have to use a new Thread while not blocking the main UI thread?
Relevant ViewModel code:
serverInfo.ClientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
serverInfo.PORT = 1488;
//Initialize LeecherList
p_LeecherList = new ObservableCollection<LeecherDetails>();
//Subscribe to CollectionChanged Event
p_LeecherList.CollectionChanged += OnLeecherListchanged;
AccountsInfo = JsonConvert.DeserializeObject<RootObject>(File.ReadAllText(Path.Combine(Directory.GetCurrentDirectory(), "Accounts.json")));
foreach (var x in AccountsInfo.Accounts)
{
p_LeecherList.Add(new LeecherDetails("N/A", x.Email, x.Password, false, "Collapsed"));
}
base.RaisePropertyChangedEvent("LeecherList");
Thread ConnectionLoop = new Thread(() =>
{
ServerStatus = "Connecting...";
while (true)
{
if (!serverInfo.HasConnectedOnce && !serverInfo.ClientSocket.Connected)
{
try
{
serverInfo.ClientSocket.Connect(IPAddress.Loopback, serverInfo.PORT);
}
catch (SocketException)
{
}
}
else if (serverInfo.ClientSocket.Connected && !serverInfo.HasConnectedOnce)
{
serverInfo.HasConnectedOnce = true;
ServerStatus = "Online";
break;
}
}
while (true)
{
try
{
var buffer = new byte[8000];
int received = serverInfo.ClientSocket.Receive(buffer, SocketFlags.None);
if (received == 0) return;
var data = new byte[received];
Array.Copy(buffer, data, received);
var st = helper.ByteToObject(data);
if (st is string info)
{
}
else
{
}
}
catch
{
continue;
}
}
});
ConnectionLoop.IsBackground = true;
ConnectionLoop.Start();
Thanks in advance.

Right now on my ViewModel, i create a thread, which attempts to connect to the server in a while loop, and wait for connection, after it's connected it gets data from the server.
Is there a better way to make it so I won't have to use a new Thread while not blocking the main UI thread?
Well; you should put this logic in a kind of Service.
Example:
So, you could create a service like this, note: the start, stop and the event to pass the data.
The basic idea is to move the more business like communication logic in a separate service, so that, if you need to change it later on, the logic is more isolated and not mixed with your viewmodel.
//note, you could encapsulate functionality in an interface, e.g.: IService
public class SomeService
{
public event EventHandler<YourEventData> OnSomethingHappening;
public SomeService(some parameters)
{
//some init procedure
}
public void Stop()
{
//your stop logic
}
public void Start()
{
//your start logic
}
private void Runner() //this might run on a seperate thread
{
while(running)
{
if (somecondition)
{
OnSomethingHappening?.Invoke(this, new YourEventData());
}
}
}
public string SomeFooPropertyToGetOrSetData {get;set;}
}
Create one of these somewhere in your application, perhaps at startup.
Then pass it to your viewmodel, maybe through the constructor. In that case your viewmodel will look something like this:
public class YourViewModel
{
private SomeService_service;
public YourViewModel(SomeServiceyourService)
{
_service = yourService;
_service.OnSomethingHappening += (s,a) =>
{
//event handling
};
}
}
//etc.

Related

How can I update my Window controls from an STA Thread when calling from an async operation?

My server application is a WPF project which uses asynchronous callbacks to handle client requests and to send responses back to the client.
The server is to update its database based on the data received from the client and, depending on the nature of that data, push an alert through its own UI to reflect the new alert.
When it comes time to update the UI, I'm getting an error saying that the calling thread must be an STA thread or some such thing.
How can I make sure that the thread that's trying to update the UI is properly set up to do it without causing errors?
See below for all of my code and commentry about what the code is doing.
Client Helper
The ClientHelper class is a wrapper for client requests.
public class ClientHelper
{
private readonly TcpClient _Client;
private readonly byte[] _Buffer;
public Client(TcpClient client)
{
_Client = client;
int BufferSize = _Client.ReceiveBufferSize;
_Buffer = new byte[BufferSize];
}
public TcpClient TcpClient
{
get { return _Client; }
}
public byte[] Buffer
{
get { return _Buffer; }
}
public NetworkStream NetworkStream
{
get { return TcpClient.GetStream(); }
}
}
FooServer
The server uses a TcpListener that's running on its own thread so as to avoid locking the UI.
public class FooServer
{
private TcpListener Svr;
public void StartServer()
{
Thread ListenerThread = new Thread(new ThreadStart(() =>
{
Svr = new TcpListener(IPAddress.Parse("127.0.0.1"), 13000);
Svr.Start();
Svr.BeginAcceptTcpClient(AcceptClientCallback, null);
}));
ListenerThread.SetApartmentState(ApartmentState.STA);
ListenerThread.IsBackground = true;
ListenerThread.Start();
}
The server keeps track of its connected clients by maintaining a list of them.
private List<Client> ConnectedClients = new List<Client>();
private void AcceptClientCallback(IAsyncResult result)
{
TcpClient Client;
try
{
Client = Svr.EndAcceptTcpClient(result);
}
catch (Exception ex)
{
OnError(Svr, ex);
//Svr.Stop();
return;
}
Svr.BeginAcceptTcpClient(AcceptClientCallback, null);
ClientHelper _Client = new ClientHelper(Client);
ConnectedClients.Add(_Client);
NetworkStream Stream = _Client.NetworkStream;
Stream.BeginRead(_Client.Buffer, 0, _Client.Buffer.Length, ReadCallback, _Client);
}
After it reads the client's data, the server executes functions that manipulate the data and forward the alert to the UI. HandleClientData is where all of this starts. Its the last read that the server does.
private void ReadCallback(IAsyncResult result)
{
ClientHelper Client = result.AsyncState as ClientHelper;
if (Client != null)
{
NetworkStream Stream = Client.NetworkStream;
int Read;
try
{
Read = Stream.EndRead(result);
}
catch (Exception ex)
{
OnError(Client, ex);
return;
}
if (Read == 0)
{
Client.TcpClient.Close();
ConnectedClients.Remove(Client);
return;
}
byte[] Data = new byte[Read];
Buffer.BlockCopy(Client.Buffer, 0, Data, 0, Read); // copy read data to the client's buffer
Stream.BeginRead(Client.Buffer, 0, Read, ReadCallback, Client); // read data
HandleClientData(Stream, Encoding.ASCII.GetString(Client.Buffer, 0, Data.Length));
}
}
private void HandleClientData(NetworkStream stream, string data)
{
byte[] value = null;
try
{
string[] Data = data.Split(',');
if (String.Equals(Data[0], "GetAllFoo"))
{
value = Encoding.ASCII.GetBytes(GetFoo());
}
else if (String.Equals(Data[0], "GetAFoo"))
{
int FooId;
Int32.TryParse(Data[1], out FooId);
value = Encoding.ASCII.GetBytes(GetFoo(FooId));
}
else
{
// Update the Foo in the database to reflect the latest state of every component.
UpdateFoo(Data);
// evaluate the data for a fault and raise an alert if there's something wrong.
if (!EvaluateFooData(Data[1]))
{
AddAlert();
}
value = Encoding.ASCII.GetBytes("SUCCESS,The Foo was successfully updated.|");
}
stream.Write(value, 0, value.Length);
}
catch (Exception ex)
{
string Error = String.Format("ERR,{0}", ex.Message);
byte[] ErrorBytes = Encoding.ASCII.GetBytes(Error);
stream.Write(ErrorBytes, 0, ErrorBytes.Length);
return;
}
}
}
EvaluateFooData checks the client data against acceptable norms and adds any deviation to a list that gets read by AddAlert below which adds the alerts to the database.
public void AddAlert()
{
ApplicationDbContext Context = new ApplicationDbContext();
foreach (Alert Alert in Alerts)
{
Context.Alerts.Add(Alert);
}
Context.SaveChanges();
OnRaiseAlert();
}
public event EventHandler RaiseAlert;
protected virtual void OnRaiseAlert()
{
RaiseAlert?.Invoke(this, null);
}
Using the EventHandler which is registered on the UI, the server pushes an alert to the UI:
public MainWindow()
{
InitializeComponent();
Server.RaiseAlert += Server_RaiseAlert;
}
private void Server_RaiseAlert(object sender, EventArgs e)
{
ApplicationDbContext Context = new ApplicationDbContext();
var Alerts = Context.Alerts.Where(x => x.IsResolved == false).ToList();
StackPanel FooStackPanel = new StackPanel();
spFoo.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() => { FooStackPanel = spFoo; }));
if (Alerts != null && Alerts.Count >= 1)
{
foreach (Alert Alert in Alerts)
{
Button Btn = (Button)FooStackPanel.Children[FooId];
Btn.Style = FindResource("FooAlertIcon") as Style;
}
}
}
Server_RaiseAlert updates the UI by changing the style of buttons that were created during initialization of the Window so that those buttons now indicate a problem with that Foo. The basic concept is green = good, red = bad.
Do everything that manipulates UI elements inside the Dispatcher Action:
private void Server_RaiseAlert(object sender, EventArgs e)
{
var context = new ApplicationDbContext();
var alerts = context.Alerts.Where(x => x.IsResolved == false).ToList();
if (alerts.Count > 0)
{
spFoo.Dispatcher.Invoke(new Action(() =>
{
foreach (var alert in alerts)
{
var button = (Button)spFoo.Children[FooId];
button.Style = FindResource("FooAlertIcon") as Style;
}
}));
}
}
Note however that from your question it isn't clear where FooId comes from.

TCP listener not working when it is behind a load balancer

I have a TCP listener in a windows service that listens for any incoming TCP requests on a specific port and processes the message. It works fine when it is accessed directly. But once this is running behind a load balancer (in intranet), then it is not accepting any requests. I get errors like "unable to connect to remote server" OR "operation timed out". After a while the service terminates with "out of memory" exception. Please let me know what could be the reason for this. Pasting the code below. I even tried async mode as well (to avoid explicit thread launching). but that didn't help.
public class SampleListener: IDisposable
{
public delegate void JobRecieved(HttpMessage msg);
public event JobRecieved OnJobRecieved;
#region Property
private TcpListener _tcpListener;
private Thread _listenerThread;
public int Port { get; private set; }
public string Url
{
get
{
return new UriBuilder { Scheme = "http", Port = Port, Host = Dns.GetHostName() }.ToString();
}
}
#endregion
public SampleListener(int port)
{
Port = port;
}
~SampleListener()
{
DisposeImpl(false);
}
public void Start()
{
_tcpListener = new TcpListener(IPAddress.Any, Port);
_tcpListener.Start();
_listenerThread = new Thread(ListenCallback);
_listenerThread.Start();
}
public void ListenCallback()
{
try
{
while (true)
{
using (TcpClient client = _tcpListener.AcceptTcpClient())
using (var clientStream = client.GetStream())
{
var msg = new HttpMessage();
msg.Receive(clientStream);
SendOKResponse(client, "");
OnJobRecieved(msg);
client.Close();
}
}
}
catch (System.Net.Sockets.SocketException e)
{
// Expected, TcpClient.Stop called
}
catch (System.Threading.ThreadAbortException)
{
// Expected, thread going away
}
catch (System.IO.IOException)
{
// Expected, shutdown while reading
}
}
private void SendOKResponse(TcpClient tcpClient, String responseBody)
{
var response = new HttpMessage
{
Status = "200",
Reason = "OK",
Version = "HTTP/1.1"
};
response.Send(tcpClient.GetStream(), responseBody);
}
public void Shutdown()
{
lock (this)
{
if (_listenerThread != null)
{
_listenerThread.Abort();
_listenerThread = null;
}
if (_tcpListener != null)
{
_tcpListener.Stop();
_tcpListener.Server.Close();
_tcpListener = null;
}
}
}
#region IDisposable Members
private void DisposeImpl(Boolean bDisposing)
{
lock (this)
{
Shutdown();
}
}
public void Dispose()
{
GC.SuppressFinalize(this);
DisposeImpl(true);
}
#endregion
}
That's because NLB on Windows needs your application be a clustered one by default. And if it is not (which it is your case) you must use Sticky Sessions. Apparently your NLB is not using Sticky Sessions so requests may travel to different servers on each pass. That's why you get those exceptions (Take a look at this).
That happened to me on one of my own projects (a high performance TCP Server - the opposite of what you are doing).

Return feedback from an event which is being waited on

In it's simplicity what I am trying to do is handle "Doing Something" by firing off a process on a seperate thread to do what I need to do and waiting for an event to be raised to say "I have finished doing what I need to do". In the EventArgs though I will have a property for any errors which may be encountered during the process. Here is a simplified example of my situation.
public class MessageHandler
{
private AutoResetEvent MessageHasSent = new AutoResetEvent(false);
public void SendMessage()
{
MessageSender ms = new MessageSender();
ms.MessageSent += new EventHandler<MessageSentEventArgs>(MessageHandler_MessageSent);
Thread t = new Thread(ms.Send());
t.Start();
MessageHasSent.WaitOne();
//Do some check here
//Same again but for "Message recieved"
}
void MessageHandler_MessageSent(object sender, MessageSentEventArgs e)
{
if (e.Errors.Count != 0)
{
//What can I do here to return to the next step after waitone?
}
else
MessageHasSent.Set();
}
}
public class MessageSender
{
public event EventHandler<MessageSentEventArgs> MessageSent;
public void Send()
{
//Do some method which could potentiallialy return a List<Error>
MessageSent(this, new MessageSentEventArgs() { Errors = new List<Error>() });
}
}
public class Error { }
public class MessageSentEventArgs : EventArgs
{
public List<Error> Errors;
}
Essentially once the event has been raised from Send the code will continute, however I want some way of the event giving feedback, potentially using the MessageHasSent. I have tried different methods, I thought if I called Close instead of Set it would perhaps allow me to access something such as IsClosed. You could throw an exception or set a flag outside of the scope of the event to check but I feel like this is dirty.
Any suggestions?
Using the TPL isn't applicable in my case as I am using .NET 3.5.
Since it seems that this entire section of code is already running in a background thread, and you're doing nothing more than starting up a new thread just so that you can wait for it to finish, you'd be better off just calling Send directly, rather than asynchronously.
You don't need to fire off an event when you're completed.
You don't need to signal the main thread when it needs to continue.
You don't need to log the exceptions in a List, you can just throw them and catch them in SendMessage with a try/catch block.
This will do what you want:
public class MessageHandler
{
private AutoResetEvent MessageHasSent = new AutoResetEvent(false);
private bool IsSuccess = false;
public void SendMessage()
{
MessageSender ms = new MessageSender();
ms.MessageSent += new EventHandler<MessageSentEventArgs>(MessageHandler_MessageSent);
Thread t = new Thread(ms.Send());
t.Start();
MessageHasSent.WaitOne();
if(IsSuccess)
//wohooo
else
//oh crap
//Same again but for "Message recieved"
}
void MessageHandler_MessageSent(object sender, MessageSentEventArgs e)
{
IsSuccess = e.Errors.Count == 0;
MessageHasSent.Set();
}
}
public class MessageSender
{
public event EventHandler<MessageSentEventArgs> MessageSent;
public void Send()
{
//Do some method which could potentiallialy return a List<Error>
MessageSent(this, new MessageSentEventArgs() { Errors = new List<Error>() });
}
}
public class Error { }
public class MessageSentEventArgs : EventArgs
{
public List<Error> Errors;
}

A While Loop Thread

So I've been trying to create a bit of code that sends data on a while loop, specifically an alive packet to a server through a UdpClient.
static void doSend(string ip, int port)
{
while (isSending)
{
_sockMain = new UdpClient(ip, port);
// Code for datagram here, took it out
_sockMain.Send(arr_bData, arr_bData.Length);
}
}
But when I call the "Stop" method, it gets stuck in a constant loop and doesn't come out. How can I put the while loop into a Thread? So I can abort the thread on stop, cancelling the loop?
It hangs because your doSend method works on UI thread. You can use something like the below class to make it run on a seperate thread or you can use BackgroundWorkerClass
public class DataSender
{
public DataSender(string ip, int port)
{
IP = ip;
Port = port;
}
private string IP;
private int Port;
System.Threading.Thread sender;
private bool issending = false;
public void StartSending()
{
if (issending)
{
// it is already started sending. throw an exception or do something.
}
issending = true;
sender = new System.Threading.Thread(SendData);
sender.IsBackground = true;
sender.Start();
}
public void StopSending()
{
issending = false;
if (sender.Join(200) == false)
{
sender.Abort();
}
sender = null;
}
private void SendData()
{
System.Net.Sockets.UdpClient _sockMain = new System.Net.Sockets.UdpClient(IP, Port);
while (issending)
{
// Define and assign arr_bData somewhere in class
_sockMain.Send(arr_bData, arr_bData.Length);
}
}
}
You can use the backgroundworker thread http://www.dotnetperls.com/backgroundworker
and inside dowork() put your while loop.
You can stop the code by using CancelAsync() and set backgroundWorker1.WorkerSupportsCancellation == true
BackgroundWorker bw = new BackgroundWorker();
if (bw.IsBusy != true)
{
bw.RunWorkerAsync();
}
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
// Run your while loop here and return result.
result = // your time consuming function (while loop)
}
// when you click on some cancel button
bw.CancelAsync();
static bool _isSending;
static void doSend(string ip, int port)
{
_isSending = true;
while (_isSending)
{
_sockMain = new UdpClient(ip, port);
// ...
_sockMain.Send(arr_bData, arr_bData.Length);
}
}
static void Stop()
{
// set flag for exiting loop here
_isSending = false;
}
Also consider to name your methods in PascalCase, i.e. DoSend (even StartSending will be better), StopSending.
How about using BREAK statement?

Change viewmodel property from another thread

Please can you help me with advice or demo code for the following task:
I had a program in WPF which constantly listen on a serial port, If it received a specific signal it should change a property in a ViewModel. The listener is start on another thread so I had wonder how can I change a ViewModel property from another thread, I try to pass a property by reference but that was not possible.
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
//My property in the view model
private Boolean _Lock;
public Boolean Lock
{
get { return _Lock; }
set
{
_Lock = value;
OnPropertyChanged("Lock");
}
}
//Start the listener thread
Thread ComListenThread = new Thread(delegate()
{
Com cm = new Com(Path,Lock);
cm.Start();
});
ComListenThread.Start();
class Com
{
private Uri web { get; set; }
private Boolean Lock { get; set; }
public Com(Uri Path,Boolean _Lock)
{
web = Path;
Lock = _Lock;
}
public void Start()
{
try
{
port = new SerialPort(portName, baudRate, parity, dataBits, stopBits);
}
catch (Exception e)
{
MessageBox.Show("Reason {0}:", e.Message);
}
port.ReadTimeout = 500;
port.Open();
int position = 0;
while (true)
{
try
{
int len = port.Read(data, position, data.Length - position);
position += len;
}
catch (TimeoutException)
{
//How to change Lock property
Lock = (data[2]==5)?true:false;
position = 0;
}
}
}
}
So my question is how can I pass the property which will be changed on another thread in constant loop.
By passing the parent object you should have access to the property to change it; however, you may want to switch back to the UI thread (Dispatcher.Invoke) before doing this, as cross-threaded mutation of "observer" models rarely ends well.
Another approach is for your code to simply raise an event (nothing to do with this property), and have your UI code switch to the UI tread and update the view-model. This approach separates the UI code cleanly from the "doing" code (since the "doing" code knows nothing of the view-model or threading), and is particularly useful if you need to support arbitrary UI models.

Categories