Tcplistener случайным образом сбрасывает соединение - PullRequest
3 голосов
/ 16 ноября 2011

Я играю с простым веб-сервером, написанным на 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()

EDIT

Кажется, проблема не связана с тем, как я вызывал 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()

Ответы [ 3 ]

4 голосов
/ 16 ноября 2011

Вот рабочая версия.Я бы использовал 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 голос
/ 16 ноября 2011

Это на самом деле не ответ на ваш вопрос, а просто комментарий относительно цикла и Async.Start (я думаю, что версия 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) }
1 голос
/ 16 ноября 2011

I думаю вы хотите

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. , вам не нужно вызывать Async.Start после AsyncAcceptSocket возврат
...