I've recently started with .Net remoting and I have managed to get working with some simple tutorials such as building a library dll that works as a calculator which the client can access and use(https://www.youtube.com/watch?v=Ve4AQnZ-_H0).
What I'm looking for to understand is how I could access current information that is held on the server. For example if I have this simple part running on the server:
int x = 0;
while (!Console.KeyAvailable)
{
x++;
System.Threading.Thread.Sleep(1000);
Console.WriteLine(x);
}
What I found so far is that the dll built is only returning a static result, such as with the calculator. I'd want to be able to tell how much x is on the server at any given time, through the client.
I don't know if I'm being clear enough but I'll try to explain better if it's needed.
In the following Server implementation demonstrates how you can keep state between calls.
// this gets instantiated by clients over remoting
public class Server:MarshalByRefObject
{
// server wide state
public static int Value;
// state only for this instance (that can be shared with several clients
// depending on its activation model)
private StringBuilder buildup;
// an instance
public Server()
{
buildup = new StringBuilder();
Console.WriteLine("server started");
}
// do something useful
public int DoWork(char ch)
{
Console.WriteLine("server received {0}", ch);
buildup.Append(ch);
return Value;
}
// return all typed chars
public string GetMessage()
{
Console.WriteLine("server GetMessage called") ;
return buildup.ToString();
}
// how long should this instance live
public override object InitializeLifetimeService()
{
// run forever
return null;
}
}
Notice the override InitializeLifetimeService. If you don't control this, your instance will get torn down after 5 minutes.
To use the above class we use the following code to get a listener up and running, including some of your logic. Don't forget to add an reference to the assembly System.Runtime.Remoting.
static void Main(string[] args)
{
// which port
var chn = new HttpChannel(1234);
ChannelServices.RegisterChannel(chn, false);
// Create only ONE Server instance
RemotingConfiguration.RegisterWellKnownServiceType(
typeof(Server), "server", WellKnownObjectMode.Singleton);
Server.Value = 0;
while (!Console.KeyAvailable)
{
Server.Value++;
System.Threading.Thread.Sleep(1000);
Console.WriteLine(Server.Value);
}
}
When this code runs, it should listen on your local box on port 1234 for connections. On first run I had to disable the firewall, allow that port to pass the local firewall.
A client implementation that uses the Server might look like this. Don't forget to add an reference to the assembly System.Runtime.Remoting.
static void Main(string[] args)
{
var chn = new HttpChannel();
ChannelServices.RegisterChannel(chn, false);
RemotingConfiguration.RegisterWellKnownClientType(
typeof(Server),
"http://localhost:1234/server");
Console.WriteLine("Creating server...");
var s = new Server();
Console.WriteLine("type chars, press p to print, press x to stop");
var ch = Console.ReadKey();
while(ch.KeyChar != 'x')
{
switch(ch.KeyChar )
{
case 'p':
Console.WriteLine("msg: {0}", s.GetMessage());
break;
default:
Console.WriteLine("Got value {0} ", s.DoWork(ch.KeyChar));
break;
}
ch = Console.ReadKey();
}
Console.WriteLine("stopped");
}
If you compile and run this your result can look like this:
Related
I have an assignment in C# involving automation. My code is mainly based off things I have found online. My current problem is I am trying to read in IP addresses line by line from a CSV file into an array, pinging each object of the array, and appending the object so that each object will read "IP, status(if the ping was successful), response time"
I am trying to use a foreach loop but am running into errors. I am on a Mac so it is using mono. Any help would be appreciated.
(Full assignment below if it helps explain anything)
Starting with a premade C# console application, your application will upon running allow for three commands – start, stop, and exit. The application will, upon first running, ask the user for a CSV file (the user must provide a CSV file). This file will contain web addresses (1 per line) – the professor will supply the initial file.
Your application will be setup with a timer so that every time the timer runs, it pings the provided web addresses, gathering whether the server responds or not and the response time of each server. The timer should run continuously from the point the user types “start” until they type “stop” or “exit”. The data will be recorded back to another CSV file, containing the web address, response time, and whether or not the server responded at all.
using System;
using System.Timers;
using System.IO;
using System.Linq;
using System.Net.NetworkInformation;
using System.Collections.Generic;
using Microsoft.VisualBasic;
namespace MT_proj4
{
public class Program
{
public static String[] addressArray = File.ReadAllLines("Project04_URLs.csv");
public static string IP;
public bool status;
public string time;
public static void Main()
{
foreach (string s in addressArray)
{
IP = Console.ReadLine();
bool PingHost(string)
{
bool pingable = false;
#pragma warning disable XS0001 // Find APIs marked as TODO in Mono
Ping pinger = new Ping();
#pragma warning restore XS0001 // Find APIs marked as TODO in Mono
try
{
PingReply reply = pinger.Send(IP);
pingable = reply.Status == IPStatus.Success;
}
catch (PingException)
{
return false;
}
for (int i = 0; i < addressArray.Length; i++)
{
addressArray[i] = addressArray[i] + pingable;
}
}
}
}
static void TimerClass()
{
Timer timer = new Timer(5000);
timer.Elapsed += HandleTimer;
timer.Start();
Console.WriteLine("Type 'exit' to close...");
Console.WriteLine("Enter a command...");
while (true)
{
Console.Write("Command: ");
string command = Console.ReadLine();
Console.WriteLine("Command entered: " + command);
if (command.ToLower() == "stop")
timer.Stop();
else if (command.ToLower() == "exit")
break;
}
}
static void HandleTimer(Object source, ElapsedEventArgs e)
{
Console.WriteLine("\nHandler not implemented...");
}
}
I would restructure your solution a bit. The ping code should run its own thread. Remove the timer, and simply block on Console.ReadLine(). If the results are not expected, repeat until you get either stop or exit.
The reason is the ping work and the console processing are on the same thread. These will block.
Try this:
class Program
{
static void Main(string[] args)
{
Console.WriteLine("pinging....until stop command");
Thread pinger = new Thread(DoPinging);
pinger.Start();
while (true)
{
string command = Console.ReadLine();
if (command.ToLower() == "stop")
break;
else if (command.ToLower() == "exit")
break;
Console.WriteLine("Ignoring-> " + command);
}
pinger.Abort();
}
static void DoPinging()
{
// here goes the pinging code
}
}
In your example code, it looks like you want to read input for ping from Console as well, after the line read, if the value doesn't equal the stop commands (stop, exit), put the value into a queue and let the ping code read from the queue.
You mention reading a CSV file for the addresses to ping (but your example code doesn't reference a file). You can take this as a command line argument (string[] passed in main) and do the work for processing the file in the DoPinging method.
If you want me to add more detail to that, please comment below and I will work on that.
Thnx
Matt
I want to make a chat. The server is made in console app and the client is made in winforms.
In client I write a nickname and connect to server. The server receives name from client. I add all clients that connect to server in a Dictionary list with the (string)name and (TcpClient)Socket. After, I want to send to every client the client list.
When I debug on server, the Sockets appear with DualMode,EnableBroadcast error. In client when I have to receive the list it stops and doesn't do anything.
Server
namespace MyServer
{
class MyServer
{
public Dictionary<string, TcpClient> clientList = new Dictionary<string, TcpClient>();
TcpListener server = null;
NetworkStream stream = null;
StreamReader streamReader = null;
StreamWriter streamWriter = null;
TcpClient clientSocket;
String messageReceived;
int number_clients = 0;
public MyServer(TcpClient clientSocket_connect)
{
stream = clientSocket_connect.GetStream();
streamReader = new StreamReader(stream);
streamWriter = new StreamWriter(stream);
receiveMessage(clientSocket_connect); // receive messages
}
public MyServer()
{
Thread thread = new Thread(new ThreadStart(run));
thread.Start();
}
public void receiveMessage(TcpClient client_Socket)
{
messageReceived = streamReader.ReadLine();
if (messageReceived.Substring(messageReceived.Length - 4) == "user")
{
String name = messageReceived.Substring(0, messageReceived.Length - 4);
bool found = false;
foreach (var namefound in clientList.Keys)
{
if (namefound == name)
{
found = true;
streamWriter.WriteLine("The user already exists");
streamWriter.Flush();
}
}
if (!found)
{
//show who's connected
Console.WriteLine(name + " is online");
number_clients++;
clientList.Add(name, client_Socket);
//send to client clientlist
String send = null;
foreach (var key in clientList.Keys)
{
send += key + ".";
}
foreach (var value in clientList.Values)
{
TcpClient trimitereclientSocket = value;
if (trimitereclientSocket != null)
{
NetworkStream networkStream = trimitereclientSocket.GetStream();
StreamWriter networkWriter = new StreamWriter(networkStream);
networkWriter.WriteLine(send + "connected");
networkWriter.Flush();
}
}
}
}
}
void run()
{
IPAddress ipAddress = IPAddress.Parse("127.0.0.1");
server = new TcpListener(ipAddress, 8000);
server.Start();
Console.WriteLine("Server started!");
while (true)
{
clientSocket = server.AcceptTcpClient();
new MyServer(clientSocket);
}
}
}
static void Main(string[] args)
{
MyServer server = new MyServer();
}
}
Client
namespace MyClient
{
class MyClient
{
List<string> clientList = new List<string>();
TcpClient client = null;
NetworkStream stream = nul
l;
StreamReader streamReader = null;
StreamWriter streamWriter = null;
bool connected;
String received_message;
public MyClient()
{
client = new TcpClient("127.0.0.1", 8000);
stream = client.GetStream();
streamReader = new StreamReader(stream);
streamWriter = new StreamWriter(stream);
}
public void sendClientName(String name)
{
streamWriter.WriteLine(Convert.ToString(name));
streamWriter.Flush();
}
public List<ClientName> receiveClientList()
{
List<ClientName> val = new List<ClientName>();
string name = Convert.ToString(streamReader.ReadLine());
if (name.Substring(0, name.Length - 9) == "connected")
{
ClientName client = new ClientName();
client.Nume = name;
val.Add(client);
}
return val;
}
}
}
Client Form
public partial class Form1 : Form
{
MyClient client = new MyClient();
public Form1()
{
InitializeComponent();
Thread receiveClients = new Thread(new ThreadStart(getMessages));
}
private void btnConnect_Click(object sender, EventArgs e)
{
client.sendClientName(txtNickname.Text + "user");
}
public void getMessages()
{
while (true)
{
lbClientsConnected.Items.Add(client.receiveClientList());
}
}
}
I was unable to reproduce any error when running your code. I don't know what you mean by "the Sockets appear with DualMode,EnableBroadcast error". That said, there are a number of fixable problems with the code, including some that pertain directly to your concern that "when I have to receive the list it stops and doesn't do anything."
Probably the biggest issue with the code is that you simply never start the client's receiving thread. You need to call the Start() method on the Thread object after it's been created:
public Form1()
{
InitializeComponent();
Thread receiveClients = new Thread(new ThreadStart(getMessages));
// The receiving thread needs to be started
receiveClients.Start();
}
Now, even with that fixed, you have a few other problems. The next big issue is that you are parsing the received text incorrectly. In your code, where you should be looking for the text "connected" at the end of the string, you instead extract the other part of the text (with the list of client names).
Your receiveClientList() method should instead look like this:
private const string _kconnected = "connected";
public List<string> receiveClientList()
{
List<string> val = new List<string>();
string name = Convert.ToString(streamReader.ReadLine());
// Need to check the *end* of the string for "connected" text,
// not the beginning.
if (name.EndsWith(_kconnected))
{
name = name.Substring(0, name.Length - _kconnected.Length);
val.Add(name);
}
return val;
}
(You didn't share the ClientName class in your question, and really the example doesn't need it; a simple string value suffices for the purpose of this exercise. ALso, I've introduced the const string named _kconnected, to ensure that the string literal is used correctly in each place it's needed, as well as to simplify usage.)
But even with those two issues fixed, you've still got a couple in the Form code where you actually handle the return value of the receive method. First, you are passing the List<T> object that is returned from the receive method to the ListBox.Items.Add() method, which would just result in the ListBox displaying the type name for the object, rather than its elements.
Second, because the code is executing in a thread other than the UI thread that owns the ListBox object, you must wrap the call in a call to Control.Invoke(). Otherwise, you'll get a cross-thread operation exception.
Fixing those two issues, you get this:
public void getMessages()
{
while (true)
{
// Need to receive the data, and the call Invoke() to add the
// data to the ListBox. Also, if adding a List<T>, need to call
// AddRange(), not Add().
string[] receivedClientList = client.receiveClientList().ToArray();
Invoke((MethodInvoker)(() => listBox1.Items.AddRange(receivedClientList)));
}
With those changes, the code will process the message sent by the client, and return the list of clients. That should get you further along. That said, you still have a number of other problems, including some fairly fundamental ones:
The biggest issue is that when you accept a connection in the server, you create a whole new server object to handle that connection. There are a number of reasons this isn't a good idea, but the main one is that the rest of the code seems to conceptually assume that a single server object is tracking all of the clients, but each connection will result in its own collection of client objects, each collection having just one member (i.e. that client).
Note that once you've fixed this issue, you will have multiple threads all accessing a single dictionary data structure. You will need to learn how to use the lock statement to ensure safe shared use of the dictionary across multiple threads.
Another significant problem is that instead of using the streamWriter you created when you first accepted the connection, you create a whole new StreamWriter object (referenced in a local variable named networkWriter) to write to the socket. In this very simple example, it works fine, but between buffering and the lack of thread safety, this incorrectly-designed code could have serious data corruption problems.
Less problematic, but worth fixing, is that your server code completely fails to take advantage of the fact that you're storing the clients in a dictionary, as well as that .NET has useful helper functions for doing things like joining a bunch of strings together. I would write your server's receiveMessage() method something more like this:
private const string _kuser = "user";
public void receiveMessage(TcpClient client_Socket)
{
messageReceived = streamReader.ReadLine();
if (messageReceived.EndsWith(_kuser))
{
String name = messageReceived.Substring(0, messageReceived.Length - _kuser.Length);
if (clientList.ContainsKey(name))
{
streamWriter.WriteLine("The user already exists");
streamWriter.Flush();
return;
}
//show who's connected
Console.WriteLine(name + " is online");
number_clients++;
clientList.Add(name, client_Socket);
string send = string.Join(".", clientList.Keys);
foreach (var value in clientList.Values.Where(v => v != null))
{
// NOTE: I didn't change the problem noted in #2 above, instead just
// left the code the way you had it, mostly. Of course, in a fully
// corrected version of the code, your dictionary would contain not
// just `TcpClient` objects, but some client-specific object specific
// to your server implementation, in which the `TcpClient` object
// is found, along with the `StreamReader` and `StreamWriter` objects
// you've already created for that connection (and any other per-client
// data that you need to track). Then you would write to that already-
// existing `StreamWriter` object instead of creating a new one each
// time here.
NetworkStream networkStream = value.GetStream();
StreamWriter networkWriter = new StreamWriter(networkStream);
networkWriter.WriteLine(send + "connected");
networkWriter.Flush();
}
}
}
The above is not exhaustive by any means. Frankly, you probably should spend more time looking at existing examples of network-aware code, e.g. on MSDN and Stack Overflow, as well as on tutorials on web sites, blogs, or in books. Even when you write the server in a one-thread-per-connection way as you seem to be trying to do here, there are lots of little details you really need to get correct, and which you haven't so far.
But I do hope the above is enough to get you past your current hurdle, and on to the next big problem(s). :)
So I need to have my IP string to be used by 2 threads of my program. I don't know alot about classes and voids but at the time I have it kind of like this:
static void Main(string[] args)
{
string IP = "127.0.0.1"
}
and I want to use it in another thread:
static void th1T()
{
while (true)
{
var ping = new Ping();
ping.Send(IP);
}
}
How can I get that to work? I know I am doing something wrong, but don't know what i shall use instead.
The first issue is that IP is scoped to the method Main. You'll need to declare IP somewhere th1T can get to if you truly want to share it.
static string IP = "127.0.0.1"
static void Main(string[] args)
{
// do some work and start th1T()
}
static void th1T()
{
while (true)
{
var ping = new Ping();
ping.Send(IP);
}
}
Here you'll be able to reach IP even if th1T is running on a different thread. There are some other options as well though, like injecting the IP into th1T when the thread is started. To do that you'd have to change the signature of th1T to this:
static void th1T(object data)
and you'd have to change the code a little:
static void th1T(object data)
{
while (true)
{
var ping = new Ping();
ping.Send(data as string);
}
}
You could then start that on another thread like this:
Thread newThread = new Thread(th1T);
newThread.Start(IP);
Threading is very much a subjective subject. If the value you're referencing from another thread is being read only, like in your example, and it's only set once by the controller (the class starting the work), then it's absolutely valid to share the variable between threads. Many however will argue to their death on this.
We can only speak in generalities in respect to this issue. For example, in general it's more appropriate to inject the value to avoid race conditions and dead locks. But again, that would really depend on what you're doing with the value. You can't inject the value if you're reading a flag from one thread that's set by another. In that case you have to safely synchronize the value.
There's no need to continue because as you see the rabbit hole only gets deeper.
You should refactor your method that pings to accept a string as a parameter:
static void th1T(string IP)
{
while (true)
{
var ping = new Ping();
ping.Send(IP);
}
}
And when you call the method, pass in your IP.
What you're wanting to do amounts to a global variable, which is generally not a good idea.
Another approach you could use is a static class to store the value:
public static class Values
{
string IP { get; set; }
}
And in your method:
static void th1T()
{
while (true)
{
var ping = new Ping();
ping.Send(Values.IP);
}
}
I want to add that there are several issues that could arise from this approach. It is probably not a best practice to do it this way.
You haven't shown how you are starting the thread but in all cases you have the possibility to send the string as parameter to the thread. For example if you are manually spawning a new Thread you could pass it as parameter to the Start method:
static void Main(string[] args)
{
string IP = "127.0.0.1"
Thread t = new Thread(th1T);
t.Start(IP);
}
static void th1T(object value)
{
// The value parameter will contain the IP here
string ip = (string)value;
while (true)
{
var ping = new Ping();
ping.Send(ip);
}
}
Notice how the th1T method now takes an object parameter that you could cast back to the value being passed. In this example we have passed a simple string value but you could pass arbitrary complex objects.
If you are using Tasks you also have the possibility to pass parameters:
static void Main(string[] args)
{
string IP = "127.0.0.1"
Task.Factory.StartNew(th1T, IP);
}
static void th1T(object value)
{
// The value parameter will contain the IP here
string ip = (string)value;
while (true)
{
var ping = new Ping();
ping.Send(ip);
}
}
I am using Named Pipes to communicate with a process. I have been able to make it work with the following code. (Original code found here : via archive.org )
class ProgramPipeTest
{
public void ThreadSenderStartClient(object obj)
{
// Ensure that we only start the client after the server has created the pipe
ManualResetEvent SyncClientServer = (ManualResetEvent)obj;
using (NamedPipeClientStream pipeStream = new NamedPipeClientStream(".","ToSrvPipe",PipeDirection.Out,PipeOptions.None))
{
// The connect function will indefinately wait for the pipe to become available
// If that is not acceptable specify a maximum waiting time (in ms)
pipeStream.Connect();
Console.WriteLine("[Client] Pipe connection established");
using (StreamWriter sw = new StreamWriter(pipeStream))
{
sw.AutoFlush = true;
string temp;
Console.WriteLine("Please type a message and press [Enter], or type 'quit' to exit the program");
while ((temp = Console.ReadLine()) != null)
{
if (temp == "quit") break;
sw.WriteLine(temp);
}
}
}
}
public void ThreadStartReceiverClient(object obj)
{
// Ensure that we only start the client after the server has created the pipe
ManualResetEvent SyncClientServer = (ManualResetEvent)obj;
using (NamedPipeClientStream pipeStream = new NamedPipeClientStream(".", "FromSrvPipe", PipeDirection.In, PipeOptions.None))
{
// The connect function will indefinately wait for the pipe to become available
// If that is not acceptable specify a maximum waiting time (in ms)
pipeStream.Connect();
Console.WriteLine("[ClientReceiver] Pipe connection established");
using (StreamReader sr = new StreamReader(pipeStream))
{
// Display the read text to the console
string temp;
while ((temp = sr.ReadLine()) != null)
{
Console.WriteLine("Received from server: {0}", temp);
}
}
}
}
static void Main(string[] args)
{
// To simplify debugging we are going to create just one process, and have two tasks
// talk to each other. (Which is a bit like me sending an e-mail to my co-workers)
ProgramPipeTest Client = new ProgramPipeTest();
Thread ClientThread = new Thread(Client.ThreadSenderStartClient);
Thread ReceivedThread = new Thread(Client.ThreadStartReceiverClient);
ClientThread.Start();
ReceivedThread.Start();
}
}
Everything works as intended. I am able to issue commands to my target process (audacity).
My issue is, I basically want to wrap a C# GUI around this code, but am not sure how to modify it so that the communication is done without having to use the console, as commands would be issued via the GUI or from the code.
I have tried turning the streamWriter sw into a class variable, exposing it via property and calling sw.WriteLine() with a method, but that doesn't seem to work.
So I am unsure how to encapsulate the stream back and forth nicely within an object.
I found this article which seemed like it was spot on, Using Named Pipes to Connect a GUI to a Console App in Windows, but unfortunately it does not seem to come with any code and is kind of over my head without any to refer to.
So how can I use named pipes without having to use the console to issue the commands ?
What you want to do is take the main pieces of logic which are the sender, the receiver out of that code and rewrite it into a re-usable class that can be used like a purpose-specific wrapper class.
Perhaps the code below could serve as a guideline (I have NOT checked to see if this works, it might require minor changes)
public sealed class ResponseReceivedEventArgs : EventArgs
{
public ResponseReceivedEventArgs(string id, string response)
{
Id = id;
Response = response;
}
public string Id
{
private set;
get;
}
public string Response
{
private set;
get;
}
}
public delegate void ResponseReceived(object sender, ResponseReceivedEventArgs e);
public sealed class NamedPipeCommands
{
private readonly Queue<Tuple<string, string>> _queuedCommands = new Queue<Tuple<string,string>>();
private string _currentId;
private readonly Thread _sender;
private readonly Thread _receiver;
// Equivalent to receiving a "quit" on the console
private bool _cancelRequested;
// To wait till a response is received for a request and THEN proceed
private readonly AutoResetEvent _waitForResponse = new AutoResetEvent(false);
// Lock to modify the command queue safely
private readonly object _commandQueueLock = new object();
// Raise an event when a response is received
private void RaiseResponseReceived(string id, string message)
{
if (ResponseReceived != null)
ResponseReceived(this, new ResponseReceivedEventArgs(id, message));
}
// Add a command to queue of outgoing commands
// Returns the id of the enqueued command
// So the user can relate it with the corresponding response
public string EnqueueCommand(string command)
{
var resultId = Guid.NewGuid().ToString();
lock (_commandQueueLock)
{
_queuedCommands.Enqueue(Tuple.Create(resultId, command));
}
return resultId;
}
// Constructor. Please pass in whatever parameters the two pipes need
// The list below may be incomplete
public NamedPipeCommands(string servername, string pipeName)
{
_sender = new Thread(syncClientServer =>
{
// Body of thread
var waitForResponse = (AutoResetEvent)syncClientServer;
using (var pipeStream = new NamedPipeClientStream(servername, pipeName, PipeDirection.Out, PipeOptions.None))
{
pipeStream.Connect();
using (var sw = new StreamWriter(pipeStream) { AutoFlush = true })
// Do this till Cancel() is called
while (!_cancelRequested)
{
// No commands? Keep waiting
// This is a tight loop, perhaps a Thread.Yield or something?
if (_queuedCommands.Count == 0)
continue;
Tuple<string, string> _currentCommand = null;
// We're going to modify the command queue, lock it
lock (_commandQueueLock)
// Check to see if someone else stole our command
// before we got here
if (_queuedCommands.Count > 0)
_currentCommand = _queuedCommands.Dequeue();
// Was a command dequeued above?
if (_currentCommand != null)
{
_currentId = _currentCommand.Item1;
sw.WriteLine(_currentCommand.Item2);
// Wait for the response to this command
waitForResponse.WaitOne();
}
}
}
});
_receiver = new Thread(syncClientServer =>
{
var waitForResponse = (AutoResetEvent)syncClientServer;
using (var pipeStream = new NamedPipeClientStream(servername, pipeName, PipeDirection.In, PipeOptions.None))
{
pipeStream.Connect();
using (var sr = new StreamReader(pipeStream))
// Do this till Cancel() is called
// Again, this is a tight loop, perhaps a Thread.Yield or something?
while (!_cancelRequested)
// If there's anything in the stream
if (!sr.EndOfStream)
{
// Read it
var response = sr.ReadLine();
// Raise the event for processing
// Note that this event is being raised from the
// receiver thread and you can't access UI here
// You will need to Control.BeginInvoke or some such
RaiseResponseReceived(_currentId, response);
// Proceed with sending subsequent commands
waitForResponse.Set();
}
}
});
}
public void Start()
{
_sender.Start(_waitForResponse);
_receiver.Start(_waitForResponse);
}
public void Cancel()
{
_cancelRequested = true;
}
public event ResponseReceived ResponseReceived;
}
You can see that I have created abstractions for the Console.ReadLine (the command queue) and Console.WriteLine (the event). The "quit" is also a boolean variable that is set by the "Cancel()" method now. Obviously this isn't the most optimal/correct way of doing it - I am just showing you one way to relate the imperative code from above into a wrapper class that can be re-used.
I want to be able to check if a certain service is running (say it had a Display name - ServiceA). I want my program to check say every 5 mins that the service is still running. If it is fine, it will loop and wait another 5 mins and then check again. If it finds that ServiceA has stopped I want the program to email me and say...ServiceA has stopped running. Below I have the code which I have done so far which is able to pull all the current services running and there actual display name back to the console. Anyone any ideas on the code/logic needed for what I need above?
namespace ServicesChecker
{
class Program
{
static void Main(string[] args)
{
ServiceController[] scServices;
scServices = ServiceController.GetServices();
Console.WriteLine("Services running on the local computer:");
foreach (ServiceController scTemp in scServices)
{
if (scTemp.Status == ServiceControllerStatus.Running)
{
Console.WriteLine();
Console.WriteLine(" Service : {0}", scTemp.ServiceName);
Console.WriteLine(" Display name: {0}", scTemp.DisplayName);
}
}
//Create a Pause....
Console.ReadLine();
}
}
}
Put every service's name in an array and check if your wanted name is running
List<string> arr = new List<string>();
foreach (ServiceController scTemp in scServices)
{
if (scTemp.Status == ServiceControllerStatus.Running)
{
arr.add(scTemp.ServiceName);
}
}
if (arr.Contains("YourWantedName")
{
// loop again
}
else
{
// send mail
}
There's no need to iterate over all services, if you know which one you're looking for: you can instantiate ServiceController with the service name.
As for sending an email: take a look at the System.Net.Mail.MailMessage class.
NB: You know that you can also just configure the service to trigger an action if it fails?
You will need to track the state of the service which will require some sort of storage. The simplest is probably an XML file that tracks the status of the service, maybe a schema like this
<services>
<service name="service1" last-check="12/21/2011 13:00:05" last-status="running" />
...
</services>
Your monitoring app, will wake up find the status of the services it is interested in, and check to see what that service's previous status was. If the status was running, but is currently stopped, send the email. If the service wasn't found add it to the list of services.
Persisting the status of the services to disk protects you in the case when your monitoring app goes down.
Heres an example of a service which does quite something similar. Should be simple to adapt it to your needs..
public partial class CrowdCodeService : ServiceBase
{
private Timer stateTimer;
private TimerCallback timerDelegate;
AutoResetEvent autoEvent = new AutoResetEvent(false);
public CrowdCodeService()
{
InitializeComponent();
}
int secondsDefault = 30;
int secondsIncrementError = 30;
int secondesMaximum = 600;
int seconds;
protected override void OnStart(string[] args)
{
Loggy.Add("Starting CrowdCodeService.");
timerDelegate = new TimerCallback(DoSomething);
seconds = secondsDefault;
stateTimer = new Timer(timerDelegate, autoEvent, 0, seconds * 1000);
}
static bool isRunning = false;
// The state object is necessary for a TimerCallback.
public void DoSomething(object stateObject)
{
if (CrowdCodeService.isRunning)
{
return;
}
CrowdCodeService.isRunning = true;
AutoResetEvent autoEvent = (AutoResetEvent)stateObject;
try
{
////// Do your work here
string cs = "Application";
EventLog elog = new EventLog();
if (!EventLog.SourceExists(cs))
{
EventLog.CreateEventSource(cs, cs);
}
elog.Source = cs;
elog.EnableRaisingEvents = true;
elog.WriteEntry("CrowdCodes Service Error:" + cmd.Message.ToString(), EventLogEntryType.Error, 991);
}
}
finally
{
CrowdCodeService.isRunning = false;
}
}
protected override void OnStop()
{
Loggy.Add("Stopped CrowdCodeService.");
stateTimer.Dispose();
}
}