Wednesday, March 26, 2008

An example win32 service in F#

I needed a small single purpose TCP socket server for work, and I thought it might be an opportune time to try a new programming language. My choices were Scala, F# or Erlang. The target platform was a win2k box and I wanted to be able to register it as a service so F# was the obvious choice. In the code below I modified the echo server example from the MIT F# site to run as a service.

// This sample code is provided "as is" without warranty of any kind. 
// We disclaim all warranties, either express or implied, including the 
// warranties of merchantability and fitness for a particular purpose. 

#light
#r "System.ServiceProcess.dll";;
#r "System.Configuration.Install.dll";;


// Echo Server: opens a service on the localhost which bounces data straight back  

open System.Net
open System.Net.Sockets
open System.Threading
open System.ServiceProcess
open System.ComponentModel
open System.Configuration.Install
open Microsoft.FSharp.Compatibility.CompatArray

let spawn f = (new Thread(new ThreadStart(f))).Start()

type EchoServer = class
    inherit ServiceBase as base
    
    new() as this = {} then base.ServiceName <- "SimpleEchoService"
    
    override this.OnStart(args:string[]) = 
        spawn(this.listen)
            
    member this.listen() =
        let sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP) 
        sock.Bind(new IPEndPoint(IPAddress.Any, 5120))

        let backlog = 2 // number of incoming connections that can be queued for acceptance
        sock.Listen(backlog)

        let port = (sock.LocalEndPoint :?> IPEndPoint).Port 
        Printf.printf "server accepting on port %d...\n" port

        while (true) do
            let csock = sock.Accept() 
            Printf.printf "the server has accepted a client on port %d...\n" port;
            spawn(fun () -> 
                let total = ref 0 
                try 
                    let buf = Bytearray.create 64
                    let stream = new NetworkStream(csock) 
                    while true do
                        Printf.printf "server reading...\n";
                        let nread = stream.Read(buf,0,64) 
                        Printf.printf "server read %d...\n" nread;
                        stream.Write(buf,0,nread);
                        total := !total + nread;
                    done;
                with 
                    | :? Sockets.SocketException 
                    | :? System.IO.IOException -> 
                        Printf.printf "client gone: server read %d bytes from that client\n" !total;
                        flush stdout
            )
        done

end

Print this post

2 comments:

Scott & Paula said...

Matthew... I'm trying to write a Win32 service in F# also, but I'm not sure how to programmatically add the installer. Can you help tell me how you did this?

Thanks,
Scott

The Eclectic Engineer said...

It's been a while since I've done anything in F# or .Net so this might not be 100% correct. From what I remember you just need to compile the F# code to an executable and then use the command line utility 'installutil' to register the service. You could also use an installer project or install the service programmatically. I've never personally installed a service programmatically. I've always create an MSI using WIX, but judging from this StackOverflow post it doesn't look to bad. I hope this helps, good luck.

http://stackoverflow.com/questions/255056/install-a-net-windows-service-without-installutil-exe