我正在玩一个用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()
答案 0 :(得分:4)
这是一个有效的版本。我使用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()
答案 1 :(得分: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()
一些变化:
loop
是一个函数,因此它需要在定义和调用时接受单位()
(与AsyncAcceptSocket
相同)return! loop()
Async.Start
返回AsyncAcceptSocket
醇>
答案 2 :(得分:1)
这不是你的问题的真正答案,而只是关于循环和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) }