Skip to content

Instantly share code, notes, and snippets.

@panesofglass
Created January 4, 2011 17:43
Show Gist options
  • Star 13 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save panesofglass/765088 to your computer and use it in GitHub Desktop.
Save panesofglass/765088 to your computer and use it in GitHub Desktop.
Asynchronous TCP server in F#
// [snippet: Async socket server using F# async computations.]
open System
open System.IO
open System.Net
open System.Net.Sockets
open System.Threading
type Socket with
member socket.AsyncAccept() = Async.FromBeginEnd(socket.BeginAccept, socket.EndAccept)
type Server() =
static member Start(hostname:string, ?port) =
let ipAddress = Dns.GetHostEntry(hostname).AddressList.[0]
Server.Start(ipAddress, ?port = port)
static member Start(?ipAddress, ?port) =
let ipAddress = defaultArg ipAddress IPAddress.Any
let port = defaultArg port 80
let endpoint = IPEndPoint(ipAddress, port)
let cts = new CancellationTokenSource()
let listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
listener.Bind(endpoint)
listener.Listen(int SocketOptionName.MaxConnections)
printfn "Started listening on port %d" port
let rec loop() = async {
printfn "Waiting for request ..."
let! socket = listener.AsyncAccept()
// Setting ownsSocket to false allows us to later re-use a socket.
let stream = new NetworkStream(socket, false)
printfn "Received request"
let response = [|
"HTTP/1.1 200 OK\r\n"B
"Content-Type: text/plain\r\n"B
"\r\n"B
"Hello World!"B |] |> Array.concat
try
try
let! bytesSent = stream.AsyncWrite(response)
printfn "Sent response"
with e -> printfn "An error occurred: %s" e.Message
finally
stream.Close()
socket.Shutdown(SocketShutdown.Both)
socket.Close()
return! loop() }
Async.Start(loop(), cancellationToken = cts.Token)
{ new IDisposable with member x.Dispose() = cts.Cancel(); listener.Close() }
// [/snippet]
// [snippet: Demo server]
let disposable = Server.Start(port = 8090)
Thread.Sleep(60 * 1000)
printfn "bye!"
disposable.Dispose()
// [/snippet]
@panesofglass
Copy link
Author

The AsyncSend appears to be questionable. I can usually pass the socket into a NetworkStream to get it working, but I would like to bypass the extra, unnecessary step of creating the NetworkStream, if possible.

@panesofglass
Copy link
Author

Using NetworkStream as the F#-provided AsyncRead and AsyncWrite methods appropriately use I/O completion ports. This should be much more performant.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment