2011-11-15 128 views
3

我正在玩一個用F#編寫的簡單網絡服務器。正如我已經注意到了,如果我打刷新在Firefox中,一些嘗試後,我得到一個錯誤頁面,指出:Tcplistener隨機重置連接

連接被重置

,而該頁面被加載到服務器連接被重置。

爲什麼看起來會隨機發生這種情況(將積壓量增加到1000並沒有幫助)?另外,爲什麼Firefox是顯示我的網絡服務器響應的唯一瀏覽器?我猜我的回答是無效的,對嗎?

Web服務器的代碼:

module Program 

open System 
open System.Net.Sockets 
open System.IO 

type TcpListener with 
    member this.AsyncAcceptSocket = 
    Async.FromBeginEnd(this.BeginAcceptSocket, this.EndAcceptSocket) 

type Main = 
    static member Init() = 
     let tcpListener = new TcpListener(80)   
     tcpListener.Start() 

     let rec loop = 
      async{         
       let! socket = tcpListener.AsyncAcceptSocket 
       Async.Start(loop) 
       let stream = new NetworkStream(socket) 
       let streamWriter = new StreamWriter(stream)     

       streamWriter.WriteLine("HTTP/1.0 200 OK"); 
       streamWriter.WriteLine("Date: Fri, 15 Nov 2011 23:59:59 GMT"); 
       streamWriter.WriteLine("Content-Type: text/html"); 
       streamWriter.WriteLine("Content-Length: 13540"); 
       streamWriter.WriteLine("") 
       streamWriter.WriteLine("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">"); 
       streamWriter.WriteLine("<html>"); 
       streamWriter.WriteLine("<body>"); 
       streamWriter.WriteLine("<h1>This is the title</h1>"); 
       streamWriter.WriteLine("</body>"); 
       streamWriter.Write("</html>");     
       streamWriter.Flush()       
       stream.Close() 
       socket.Close() 
      } 

     Async.Start(loop) 

     Console.ReadLine() 

Main.Init() 

編輯

看來問題不涉及我在之前的解決方案調用loop的方式。我已經計劃減少到這一點(我的問題依然存在):

module Program 

open System 
open System.Net.Sockets 
open System.IO 

let tcpListener = new TcpListener(80)   
tcpListener.Start() 

while true do        
    let socket = tcpListener.AcceptSocket() 
    let stream = new NetworkStream(socket) 
    let streamWriter = new StreamWriter(stream)     

    streamWriter.WriteLine("response");    
    streamWriter.Flush()       
    stream.Close() 
    socket.Close() 

Console.ReadLine() 
+4

調用到'Async.Start(循環)'看起來確實錯了.. – ildjarn

+2

是否有可能是因爲你的內容長度是不正確的? –

+0

調用'Async.Start' _looks_錯誤,但我認爲沒有什麼技術上的錯誤(它看起來有點混亂)。 –

回答

4

這裏有一個工作版本Async.StartAsyncAcceptSocket。我會使用TcpClient而不是套接字,因此您不必管理其下劃線流。

module Program 

open System 
open System.Net 
open System.Net.Sockets 
open System.IO 

type TcpListener with 
    member this.AsyncAcceptTcpClient() = 
    Async.FromBeginEnd(this.BeginAcceptTcpClient, this.EndAcceptTcpClient) 

type Main = 
    static member Init() = 
     let tcpListener = new TcpListener(IPAddress.Loopback, 80) 
     tcpListener.Start() 

     let writeContent() = 
      let stream = new MemoryStream() 
      let streamWriter = new StreamWriter(stream) 
      streamWriter.WriteLine("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">") 
      streamWriter.WriteLine("<html>") 
      streamWriter.WriteLine("<body>") 
      streamWriter.WriteLine("<h1>This is the title</h1>") 
      streamWriter.WriteLine("</body>") 
      streamWriter.Write("</html>") 
      streamWriter.Flush() 
      stream 

     let rec loop() = 
      async { 
       use! tcp = tcpListener.AsyncAcceptTcpClient() 
       let stream = tcp.GetStream() 
       use streamWriter = new StreamWriter(stream) 
       use content = writeContent() 

       streamWriter.WriteLine("HTTP/1.0 200 OK") 
       streamWriter.WriteLine("Date: Fri, 15 Nov 2011 23:59:59 GMT") 
       streamWriter.WriteLine("Content-Type: text/html") 
       streamWriter.WriteLine("Content-Length: {0}", content.Length) 
       streamWriter.WriteLine("") 
       streamWriter.Flush() 

       content.WriteTo stream 
       stream.Flush() 
       return! loop() 
      } 

     Async.Start(loop()) 

     Console.ReadLine() |> ignore 

Main.Init() 
1

認爲你想

type TcpListener with 
    member this.AsyncAcceptSocket() = 
    Async.FromBeginEnd(this.BeginAcceptSocket, this.EndAcceptSocket) 

type Main = 
    static member Init() = 
     let tcpListener = new TcpListener(80)   
     tcpListener.Start() 

     let rec loop() = 
      async{         
       let! socket = tcpListener.AsyncAcceptSocket() 
       let stream = new NetworkStream(socket) 
       let streamWriter = new StreamWriter(stream)     

       streamWriter.WriteLine("HTTP/1.0 200 OK"); 
       streamWriter.WriteLine("Date: Fri, 15 Nov 2011 23:59:59 GMT"); 
       streamWriter.WriteLine("Content-Type: text/html"); 
       streamWriter.WriteLine("Content-Length: 13540"); 
       streamWriter.WriteLine("") 
       streamWriter.WriteLine("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">"); 
       streamWriter.WriteLine("<html>"); 
       streamWriter.WriteLine("<body>"); 
       streamWriter.WriteLine("<h1>This is the title</h1>"); 
       streamWriter.WriteLine("</body>"); 
       streamWriter.Write("</html>");     
       streamWriter.Flush()       
       stream.Close() 
       socket.Close() 
       return! loop() 
      } 

     Async.Start(loop()) 

     Console.ReadLine() 

Main.Init() 

一些改動:

  1. loop是一個函數,因此需要在定義和調用時接受()單位(與AsyncAcceptSocket相同)
  2. 您的異步循環需要與return! loop()
  3. 結束,你不需要調用返回
+0

雖然這些更改很好,但如果不設置正確的內容長度,它仍然無法正常工作。 – gradbot

+0

我從來沒有手動生成HTML響應;那些只是突出給我的東西。 – Daniel

+0

我不認爲這些變化是一個問題。 1)你可以多次啓動異步工作流,只要它們不捕獲任何狀態3)通過在接受套接字後調用'Async.Start',你立即開始監聽一個新的套接字,所以你可以並行處理2),因爲他開始等待下一個套接字(之前使用'Async.Start'),'loop'函數不需要在最後循環。 不過,我同意OP使用的風格有點混亂。 –

1

這是沒有真正回答你的問題,而只是一個關於循環和Async.Start評論(我認爲gradbot的版本或許應該工作,回答您的主要問題)。

總之,違背了丹尼爾和gradbot,我認爲你的Async.Start使用不,但也許只是混淆(和混淆命名)。

  • 你實現它,你等待插座的方式,然後開始一個新的異步(準備好接受新立即插座),然後你處理工作的其餘部分。這意味着,您可以並行處理請求。

  • gradbot和Daniel實現它的方式是,它們接受套接字,將回復發送給調用者,然後等待另一個套接字。這意味着處理是連續的!

我覺得混亂來自你寫它的方式 - 我可能會啓動電流插座的處理作爲一個新的異步工作流程,然後等待下一個套接字(我相信這是比較容易理解,但我認爲你的版本是正確的太):

// Asynchronous function that handles communication with a single client 
let handleClient (tcp:TcpClient) = 
    async { 
    try 
     let stream = tcp.GetStream() 
     use streamWriter = new StreamWriter(stream) 
     use content = writeContent() 

     streamWriter.WriteLine("HTTP/1.0 200 OK") 
     // Some stuff omitted 
     streamWriter.Flush() 

     content.WriteTo stream 
     stream.Flush() 
    finally 
     tcp.Dispose() 
    } 

let mainLoop = 
    async {  
    while true do 
     // Wait for a client and start async workflow to process it 
     let! tcp = tcpListener.AsyncAcceptTcpClient() 
     Async.Start(handleClient tcp) } 
+0

謝謝。事實上,我的初衷是並行處理請求,但你的解決方案比我的更清晰。 – kahoon