我希望将httpListener构建到一个小型服务器应用程序中。在阅读它的同时,我在this question on stackoverflow
中遇到了以下代码段Public Class HTTPServer
Shared Listener As HttpListener = New HttpListener
Public Shared Sub Start()
ServicePointManager.DefaultConnectionLimit = 500
ServicePointManager.Expect100Continue = False
ServicePointManager.MaxServicePoints = 500
Listener.Prefixes.Add("http://localhost/")
Listener.Start()
For i As Integer = 1 To (System.Environment.ProcessorCount * 2)
Dim NewThread As New System.Threading.Thread(AddressOf ListenerThread)
NewThread.Priority = ThreadPriority.Normal
NewThread.IsBackground = True
NewThread.Start()
Next
End Sub
Private Shared Sub ListenerThread()
Dim SyncResult As IAsyncResult
While True
SyncResult = Listener.BeginGetContext(New AsyncCallback(AddressOf ListenerCallback), Listener)
SyncResult.AsyncWaitHandle.WaitOne()
End While
End Sub
Private Shared Sub ListenerCallback(ByVal StateObject As IAsyncResult)
Dim Listener As HttpListener = DirectCast(StateObject.AsyncState, HttpListener)
Dim Context As HttpListenerContext = Listener.EndGetContext(StateObject)
Dim Request As HttpListenerRequest = Context.Request
Dim Response As HttpListenerResponse = Context.Response
Dim ResponseString As String = "OK"
Dim Buffer As Byte() = System.Text.Encoding.UTF8.GetBytes(ResponseString)
Response.ContentLength64 = Buffer.Length
Dim OutputStream As System.IO.Stream = Response.OutputStream
OutputStream.Write(Buffer, 0, Buffer.Length)
OutputStream.Close()
OutputStream.Dispose()
End Sub
End Class
这似乎很简单,看起来很像msdn example。然而,当在虚拟项目中测试时,我发现了一些让我感到困惑的事情,例如UI对象可以直接从回调子中访问,我认为这应该会导致交叉线程异常。
为了澄清,我稍微修改了这段代码,以便在一个简单的winforms项目的主要形式中运行。
似乎我并不完全理解代码,例如AsyncWaitHandle.WaitOne()对我来说是全新的。
有人可以简要介绍一下这个片段吗?任何帮助表示赞赏。
答案 0 :(得分:2)
此代码段如下:
因为它调用了SyncResult.AsyncWaitHandle.WaitOne()来阻止该线程,它将通过同步调用得到相同的结果。
在我看来,它只返回一个简单的OK响应,所以我们甚至不需要任何其他线程。我很快就会告诉你代码(如果我们的操作繁重,我的代码将无法正常工作,请不要在表单应用程序中执行此操作。)
static void Main(string[] args)
{
ServicePointManager.DefaultConnectionLimit = 500;
ServicePointManager.Expect100Continue = false;
ServicePointManager.MaxServicePoints = 500;
HttpListener listener = new HttpListener();
listener.Prefixes.Add("http://localhost:999/");
listener.Start();
listener.BeginGetContext(ListenerCallBack, listener);
Console.ReadLine();
}
private static void ListenerCallBack(IAsyncResult result)
{
HttpListener httpListener = (HttpListener) result.AsyncState;
// Call EndGetContext to complete the asynchronous operation.
HttpListenerContext context = httpListener.EndGetContext(result);
HttpListenerRequest request = context.Request;
// Obtain a response object.
HttpListenerResponse response = context.Response;
// Construct a response.
string responseString = "<HTML><BODY> Hello world!</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;
output.Write(buffer,0,buffer.Length);
// You must close the output stream.
output.Close();
// we call BeginGetContext() to send async request again for next coming client
httpListener.BeginGetContext(ListenerCallBack, httpListener);
}
在C#5和Net 4.5中,我们有同步方法,它会更容易:
static void Main(string[] args)
{
ServicePointManager.DefaultConnectionLimit = 500;
ServicePointManager.Expect100Continue = false;
ServicePointManager.MaxServicePoints = 500;
HttpListener listener = new HttpListener();
listener.Prefixes.Add("http://localhost:999/");
listener.Start();
while (true)
{
var t = listener.GetContextAsync();
HttpListenerContext context = t.Result;
HttpListenerRequest request = context.Request;
// Obtain a response object.
HttpListenerResponse response = context.Response;
// Construct a response.
string responseString = "<HTML><BODY> Hello world!</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;
output.Write(buffer, 0, buffer.Length);
// You must close the output stream.
output.Close();
}
}
关于UI的另一个关于“交叉线程异常”的问题,你是对的,创建的新Thread将为SynchronizationContext.Current提供一个空值,因为它是一个线程池线程,我们需要在表单的SynchronizationContext中做一个post() 。以下是更多信息http://msdn.microsoft.com/en-us/magazine/gg598924.aspx和http://blogs.msdn.com/b/pfxteam/archive/2012/01/20/10259049.aspx。
答案 1 :(得分:0)
带有HttpListener的简单C#WebServer(名称:服务器),BackgroundWorker(名称:bw_server),2个按钮(名称:btn_start&amp; btn_stop)和TextBox(名称:tb_log):
public partial class Form1 : Form
{
HttpListener server;
private void btn_start_Click(object sender, EventArgs e)
{
bw_server.RunWorkerAsync(@"http://+:80/");
}
private void bw_server_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
string prefix = e.Argument.ToString();
bw_server.ReportProgress(0, "Starting server...");
if (!bw_server.CancellationPending)
{
try
{
start_server(prefix);
}
catch (Exception ex)
{
bw_server.ReportProgress(0, ex.Message);
bw_server.CancelAsync();
}
}
else
{
e.Cancel = true;
return;
}
}
private void bw_server_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
{
log(e.UserState.ToString());
}
private void bw_server_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
log("Server was stopped");
}
else
{
log("Server work was completed");
}
}
private void log(string msg)
{
if (String.IsNullOrEmpty(msg)) return;
tb_log.AppendText((tb_log.Text == String.Empty ? String.Empty : Environment.NewLine) + DateTime.Now.ToLongTimeString() + " " + msg);
}
private void btn_stop_Click(object sender, EventArgs e)
{
WebRequest.Create("http://localhost:" + port + "/?stop=1").GetResponse();
bw_server.CancelAsync();
}
private void start_server(string prefix)
{
server = new HttpListener();
if (!HttpListener.IsSupported) return;
if (String.IsNullOrEmpty(prefix))
{
throw new ArgumentException("prefix");
}
server.Prefixes.Add(prefix);
server.Start();
while (server.IsListening)
{
HttpListenerContext context = server.GetContext();
HttpListenerRequest request = context.Request;
if (request.HttpMethod == "GET")
{
HttpListenerResponse response = context.Response;
response.ContentType = "text/html; charset=UTF-8";
if (request.QueryString["stop"] == "1")
{
write_to_resp(response, "server was stopped");
server.Stop();
}
else
{
write_to_resp(response, "bad params");
}
}
}
}
private static void write_to_resp(HttpListenerResponse response, string str_resp)
{
byte[] buffer = Encoding.UTF8.GetBytes(str_resp);
response.ContentLength64 = buffer.Length;
response.OutputStream.Write(buffer, 0, buffer.Length);
}
}