We use services on computers, whether we know it or not. Look at the
anti-virus icon, printer status monitor and Windows firewall landing in the
system tray-many of these are services. A 'service' is a program, which
works essentially in the background and may or may not interact with the user.
Some of the services monitor the system security; some monitor hardware devices;
others (called network services) wait for connections from other machines and
serve their requests.
Network services are set up on a particular IP address and port number. No
two services can be set up on the same IP address and port combination. The
combination of IP address and port number is termed a 'socket'. A service
can also use one of many protocols (namely TCP/IP, UDP, etc) to communicate. In
this article, we shall be talking about TCP sockets only.
|
How services work
Services do not require the user to explicitly start the service each time the
computer boots, nor do they wait for the user to kill them when the system shuts
down. They are managed by the OS. The list of services available on a system can
be viewed from the Microsoft Services Management Console Administrative
Tools>Services.
A program has to fulfill two requirements to qualify as a service. The first
job is the creation of a local registry entry for itself at the location HKLM\
SYSTEM\CurrentControlSet\Services. It has to create a sub-key with the name of
service, full path the executable ('ImagePath' sub-key), an optional brief
description and a start-up mode ('start' sub-key, values 2, 3 and 4 signify
Automatic, Manual and Disabled respectively).
In our example, once a client connects to our service, it is sent a random number before the connection is closed |
Coding a service
Note that .NET provides the System.ServiceProcess namespace, which has a base
class 'ServiceBase'. Any service built in .NET needs to extend this base
class and override a few methods that start, stop and manage the service
instance.
Above all, the 'Main' method of this extended class should invoke the
static Run method of ServiceBase with an instance of the service class (Similar
to the Application.Run method for Windows forms).
This article demonstrates the coding, installation and testing of a random
number server using C# language. The source code for the same can be accessed at
forums.pcquest.com under the Developer section.
RandomServer class
Our random server is implemented in a file named RandomServer.cs. It contains
RandomServer class, which extends ServiceBase. The constructor sets the AutoLog
property True, which permits the server to enjoy automatic logging of events
(without requiring us to code the EventLog control ourselves). The name of the
server service is set up using the ServiceName property. The CanPauseAndContinue
property tells Windows that the server can be temporarily requested to remain
idle (but not stopped), in order to relax the system from overload or permit
some maintenance work.
The RandomServer overrides the methods OnStart, OnStop, OnShutDown, OnPause
and OnContinue. These overridden methods provide the link between the code we
create and the Windows service management architecture. There is one more method
called OnCustomCommand, which permits handling of custom signals using command
numbers. Since we do not plan anything beyond the standard actions described
above, this method is simply ignored.
Our RandomNumberServer service is registered in the Windows registry and we can control it from the Services console |
Our server logic is implemented in a separate thread called serverThread,
which runs the method Serve(). Both serverThread and Serve are our own methods.
The Serve() method creates a TCP server socket using the TcpListener class on
port number 4444 of the loopback adapter (IP 127.0.0.1). The server starts
listening, enters a while loop, accepts connections as they arrive, sends out a
random number, logs the IP address and port number of the client and closes the
socket.
The server itself is started as a separate thread, since the OnStart method
has to return within 30 secs. Otherwise, Windows assumes that the service failed
to start. Pause operation is implemented using a Boolean variable serverPaused.
If the value in this variable is True, the server sleeps for 10 ms at a time
till this is set to False again by calling the OnContinue method.
//RandomServer.cs
using System.Threading;
using System.Net;
using System.Net.Sockets;
using System.ServiceProcess;
using System;
public class RandomServer : ServiceBase
{
TcpListener server = null;
Thread serverThread = null;
bool serverPaused = false;
public RandomServer() {
AutoLog=CanPauseAndContinue = CanShutdown = true;
this.ServiceName = "RandomNumberServer";
}
protected override void OnStart(string<> arg) {
serverThread = new Thread(new ThreadStart(Serve));
serverThread.Start();
}
protected override void OnStop() {server.Stop(); server = null;}
protected override void OnShutdown() {OnStop();}
protected override void OnPause() {serverPaused = true;}
protected override void OnContinue() {serverPaused = false;}
protected override void OnCustomCommand(int cmd) {}
private void Serve() {
server = new TcpListener(IPAddress.Loopback, 4444);
server.Start();
System.Random r = new System.Random();
while(server != null) {
try {
if(serverPaused) {Thread.Sleep(10); continue;}
Socket s = server.AcceptSocket();
EventLog.WriteEntry("Accepted "+((IPEndPoint)s.RemoteEndPoint));
string ran = ""+r.NextDouble();
byte<> b = System.Text.Encoding.ASCII.GetBytes(ran);
s.Send(b, b.Length, 0);
EventLog.WriteEntry("Served "+((IPEndPoint)s.RemoteEndPoint));
s.Close();
}
catch(System.Exception ex) {
System.Console.WriteLine(ex.Message);
}
}
}
public static void Main() {
erviceBase.Run(new RandomServer());
}
}
The service automatically logs events to the Windows Event log, captured here using an event listener |
Installer for RandomServer
RandomServer cannot run as a normal program. It needs to be installed with
proper registry entries so that Windows can detect and run the program. The .NET
framework provides ServiceProcessInstaller and ServiceInstaller classes within
System.ServiceProcess assembly to do this. The type of service account is set in
ServiceProcessInstaller.
The enumeration called Service-Account contains possible types (LocalService,
NetworkService, LocalSystem or User).
Then, the name of service, a brief description and startup type (defined in
the enumeration ServiceStartMode) are set.
The start mode options can be Automatic, Manual or Disabled. The properties
filled up here are reflected in the registry entries. The installation logic is
collected in a file called RandomServerInstaller.cs.
//RandomServerInstaller.cs
using System.ServiceProcess;
public class RandomServerInstaller : System.Configuration.Install.Installer
{
public RandomServerInstaller() {
ServiceProcessInstaller spi = new ServiceProcessInstaller();
ServiceInstaller si = new ServiceInstaller();
spi.Account = ServiceAccount.NetworkService;
si.ServiceName = "RandomNumberServer";
si.Description = "Random Number Generation Service on port No. 4444";
si.StartType = ServiceStartMode.Automatic;
Installers.AddRange( new System.Configuration.Install.Installer<> {spi, si});
}
}
Compile and run
We can manage the installation and running of this service using the Visual
Studio 2005 IDE itself. Otherwise, if you have just the .NET SDK installed, you
can use the 'csc' command to compile our C# service and manage installation
and removal using the 'installutil'
command.
To minimize the size of coding, creation of a separate client is avoided and
telnet client is used. Random number can be obtained from our server by issuing
the command telnet localhost 4444 either in the command prompt or through
Start->Run. For testing the service from other hosts on a network, localhost
in the command should be replaced by appropriate host name or IP address.
We can take a look at the logs generated by our service from the Windows
Event Viewer (you can open this by going to the Administrative Tools).
V Nagaradjane