【问题标题】:Tcplistener randomly resetting the connectionTcplistener 随机重置连接
【发布时间】:2011-12-29 21:12:32
【问题描述】:

我正在玩一个用 F# 编写的简单网络服务器。正如我所注意到的,如果我在 Firefox 中点击刷新,经过一些尝试后,我会收到一个错误页面:

连接已重置

在加载页面时重置了与服务器的连接。

为什么这似乎是随机发生的(将积压增加到 1000 没有帮助)?另外,为什么 Firefox 是唯一显示我的网络服务器响应的浏览器?我想我的回答是无效的,对吗?

网络服务器的代码:

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()

【问题讨论】:

  • Async.Start(loop) 的调用肯定看起来不对..
  • 会不会是你的内容长度不对?
  • Async.Start的调用看起来错误,但我实际上认为它在技术上没有任何问题(只是看起来有点混乱)。
  • @Tomas :我的意思是第一个,而不是第二个。将Async.Start 2 行调用到loop 肯定不能按预期工作——这不是试图产生无限数量的套接字吗?或者我可能遗漏了一些关于TcpListener 行为的细节......
  • @ildjarn 我认为这很好,因为它会在接受套接字后生成工作流(意味着连接了客户端)。然后工作流的其余部分处理当前客户端,生成的工作流准备好接受新客户端...

标签: .net f# network-programming tcplistener


【解决方案1】:

这是一个工作版本。我会使用 TcpClient 而不是 socket,这样你就不必管理它的下划线流。

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()

【讨论】:

    【解决方案2】:

    认为你想要

    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. AsyncAcceptSocket 返回后,您无需调用Async.Start

    【讨论】:

    • 虽然这些更改很好,但如果不设置正确的内容长度,它仍然无法正常工作。
    • 我从来没有手动生成过 HTML 响应;这些只是让我印象深刻的事情。
    • 我认为这些变化都不是问题。 1)您可以多次启动异步工作流,前提是它们不捕获任何状态 3)通过在接受套接字后调用Async.Start,您会立即开始侦听新套接字,因此您可以获得并行处理 2)因为他开始等待对于下一个套接字(之前使用Async.Start),loop 函数不需要在最后循环。不过,我同意 OP 使用的风格有点混乱。
    • 对。我无法发现他的代码有明确的问题,但我认为这更好地表达了他的意图。因此我的介绍:我认为这就是你想要的
    【解决方案3】:

    这并不是真正回答你的问题,而只是关于循环和Async.Start 的评论(我认为 gradbot 的版本应该可以工作,回答你的主要问题)。

    无论如何,与 Daniel 和 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) }
    

    【讨论】:

    • 谢谢。的确,我的初衷是并行处理请求,但你的解决方案比我的要清晰得多。
    猜你喜欢
    • 2013-09-28
    • 1970-01-01
    • 2012-01-16
    • 1970-01-01
    • 2018-07-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-01
    相关资源
    最近更新 更多