Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

what's wrong on this f# echo server?

Tags:

f#

I've written this f# echo server:

open System.Net
open System.Net.Sockets
open System.IO
open System
open System.Text
open System.Collections.Generic

let addr = IPAddress.Parse("127.0.0.1")
let listener = new TcpListener(addr, 2000)
listener.Start()

let rec loop2(c:TcpClient,sr:StreamReader,sw:StreamWriter)=async {
        let line=sr.ReadLine()
        if not(line=null) then
            match line with
                |"quit"->
                    sr.Close()
                    sw.Close()
                    c.Close()  
                 |_ ->
                    if line.Equals("left") then
                        sw.WriteLine("right")
                        return! loop2(c,sr,sw)
                    sw.WriteLine(line)
                    return! loop2(c,sr,sw)

        else
            sr.Close()
            sw.Close()
            c.Close()   
}

let loop()=async {
while true do
    let c=listener.AcceptTcpClient()
    let d = c.GetStream()
    let sr = new StreamReader(d)
    let sw = new StreamWriter(d)
    sw.AutoFlush<-true
    Async.Start(loop2(c,sr,sw))
}

Async.RunSynchronously(loop())

This program can do:

  1. echo the client's message
  2. when client said 'left',return 'right'
  3. when client said 'quit',close the connection

but when I run the programming,when a client sent 'left',get 'right',and sent 'quit',i got this exception:

not handled exception: System.ObjectDisposedException: (con't write to closed) TextWriter。 in Microsoft.FSharp.Control.CancellationTokenOps.Start@1192-1.Invoke(Exception e) in .$Control.loop@419-40(Trampoline this, FSharpFunc2 action) in Microsoft.FSharp.Control.Trampoline.ExecuteAction(FSharpFunc2 firstAction) in Microsoft.FSharp.Control.TrampolineHolder.Protect(FSharpFunc`2 firstAction) in [email protected](Object state) in System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(Object state) in System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, BooleanpreserveSyncCtx) in System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) in System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem() in System.Threading.ThreadPoolWorkQueue.Dispatch() in System.Threading._ThreadPoolWaitCallback.PerformWaitCallback() . . .(press any key to continue)

Screenshot of program in action
Screenshot of exception

how can i fix this problem?

like image 927
wang kai Avatar asked Nov 23 '25 16:11

wang kai


1 Answers

The problem is that, unlike what you might expect from its namesake in imperative languages, return in a computation expression doesn't short-circuit. So once the return! in the if line.Equals("right") returns, ie. after the socket has been closed, the code after the if block is run and tries to write to the closed socket. The fix is to put those two lines in an else:

                if line.Equals("left") then
                    sw.WriteLine("right")
                    return! loop2(c,sr,sw)
                else
                    sw.WriteLine(line)
                    return! loop2(c,sr,sw)

As an additional style tip, this whole body can be implemented as a match:

let rec loop2(c:TcpClient,sr:StreamReader,sw:StreamWriter)=async {
    let line=sr.ReadLine()
    match line with
    | null | "quit" ->
        sr.Close()
        sw.Close()
        c.Close()
    | "left" ->
        sw.WriteLine("right")
        return! loop2(c,sr,sw)
    | _ ->
        sw.WriteLine(line)
        return! loop2(c,sr,sw)
}
like image 137
Tarmil Avatar answered Nov 26 '25 05:11

Tarmil



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!