yos*_*ssi 9 c# asynchronous httplistener
在我构建的应用程序中,需要能够同时为多个客户端提供服务的Web服务器.
为此,我使用该HttpListener对象.用它的Async方法\事件BeginGetContext和EndGetContext.
在委托方法中,有一个监听器再次开始监听的呼叫,它主要起作用.
提供的代码是我在这里和那里找到的代码混合,以及模拟数据处理瓶颈的延迟.
问题是,它只在最后一个连接服务之后才开始管理下一个连接..对我没用.
public class HtServer {
public void startServer(){
HttpListener HL = new HttpListener();
HL.Prefixes.Add("http://127.0.0.1:800/");
HL.Start();
IAsyncResult HLC = HL.BeginGetContext(new AsyncCallback(clientConnection),HL);
}
public void clientConnection(IAsyncResult res){
HttpListener listener = (HttpListener)res.AsyncState;
HttpListenerContext context = listener.EndGetContext(res);
HttpListenerRequest request = context.Request;
// Obtain a response object.
HttpListenerResponse response = context.Response;
// Construct a response.
// add a delay to simulate data process
String before_wait = String.Format("{0}", DateTime.Now);
Thread.Sleep(4000);
String after_wait = String.Format("{0}", DateTime.Now);
string responseString = "<HTML><BODY> BW: " + before_wait + "<br />AW:" + after_wait + "</BODY></HTML>";
byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString);
// Get a response stream and write the response to it.
response.ContentLength64 = buffer.Length;
System.IO.Stream output = response.OutputStream;
// You must close the output stream.
output.Write(buffer, 0, buffer.Length);
output.Close();
listener.BeginGetContext(new AsyncCallback(clientConnection), listener);
}
}
Run Code Online (Sandbox Code Playgroud)
private static void OnContext(IAsyncResult ar)
{
var ctx = _listener.EndGetContext(ar);
_listener.BeginGetContext(OnContext, null);
Console.WriteLine(DateTime.UtcNow.ToString("HH:mm:ss.fff") + " Handling request");
var buf = Encoding.ASCII.GetBytes("Hello world");
ctx.Response.ContentType = "text/plain";
// prevent thread from exiting.
Thread.Sleep(3000);
// moved these lines here.. to simulate process delay
ctx.Response.OutputStream.Write(buf, 0, buf.Length);
ctx.Response.OutputStream.Close();
Console.WriteLine(DateTime.UtcNow.ToString("HH:mm:ss.fff") + " completed");
}
Run Code Online (Sandbox Code Playgroud)
输出是

jga*_*fin 18
好.那是因为你在处理了第一个上下文后开始获取下一个上下文.不要那样做.直接获取下一个上下文:
public void clientConnection(IAsyncResult res){
HttpListener listener = (HttpListener)res.AsyncState;
HttpListenerContext context = listener.EndGetContext(res);
//tell listener to get the next context directly.
listener.BeginGetContext(clientConnection, listener);
HttpListenerRequest request = context.Request;
// Obtain a response object.
HttpListenerResponse response = context.Response;
// Construct a response.
// add a delay to simulate data process
String before_wait = String.Format("{0}", DateTime.Now);
Thread.Sleep(4000);
String after_wait = String.Format("{0}", DateTime.Now);
string responseString = "<HTML><BODY> BW: " + before_wait + "<br />AW:" + after_wait + "</BODY></HTML>";
byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString);
// Get a response stream and write the response to it.
response.ContentLength64 = buffer.Length;
System.IO.Stream output = response.OutputStream;
// You must close the output stream.
output.Write(buffer, 0, buffer.Length);
output.Close();
}
Run Code Online (Sandbox Code Playgroud)
这是我的示例代码,证明它可以工作(根据OP的请求更新):
class Program
{
private static HttpListener _listener;
static void Main(string[] args)
{
_listener = new HttpListener();
_listener.Prefixes.Add("http://localhost/asynctest/");
_listener.Start();
_listener.BeginGetContext(OnContext, null);
Console.ReadLine();
}
private static void OnContext(IAsyncResult ar)
{
var ctx = _listener.EndGetContext(ar);
_listener.BeginGetContext(OnContext, null);
Console.WriteLine(DateTime.UtcNow.ToString("HH:mm:ss.fff") + " Handling request");
var buf = Encoding.ASCII.GetBytes("Hello world");
ctx.Response.ContentType = "text/plain";
// simulate work
Thread.Sleep(10000);
ctx.Response.OutputStream.Write(buf, 0, buf.Length);
ctx.Response.OutputStream.Close();
Console.WriteLine(DateTime.UtcNow.ToString("HH:mm:ss.fff") + " completed");
}
}
Run Code Online (Sandbox Code Playgroud)
产生:

两个请求都开始直接处理.
为什么上面的代码有效
HTTP有一个叫做流水线的东西.这意味着通过同一连接接收的所有请求必须以相同的顺序获得响应.但是,内置的HttpListener似乎不支持流水线操作,而是在处理第二个请求之前完成第一个请求的响应.因此,确保通过新连接发送每个请求非常重要.
最简单的方法是在尝试代码时使用不同的浏览器.我做到了,当你看到我的请求同时处理时.
试试这个......
这将使用异步编码来确保没有阻塞.阻塞意味着线程休眠,这通常是程序倾向于"冻结"的方式.通过使用此代码,您运行非阻塞,这意味着它几乎不可能"冻结"应用程序.
public async Task handleClientConnection(HttpListener listener){
HttpListenerContext context = await listener.GetContextAsync();
var ret = handleClientConnection(listener);
HttpListenerRequest request = context.Request;
// Obtain a response object.
HttpListenerResponse response = context.Response;
// Construct a response.
// add a delay to simulate data process
String before_wait = String.Format("{0}", DateTime.Now);
await Task.Wait(4000);
String after_wait = String.Format("{0}", DateTime.Now);
string responseString = "<HTML><BODY> BW: " + before_wait + "<br />AW:" + after_wait + "</BODY></HTML>";
byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString);
// Get a response stream and write the response to it.
response.ContentLength64 = buffer.Length;
using(System.IO.Stream output = response.OutputStream)
output.Write(buffer, 0, buffer.Length);
await ret;
}
public void startServer(){
HttpListener HL = new HttpListener();
HL.Prefixes.Add("http://127.0.0.1:800/");
HL.Start();
await handleClientConnection(HL);
}
Run Code Online (Sandbox Code Playgroud)