APM(异步编程模型)在.net编程中随处可见,HttpWebRequest类也实现了APM,可供用户使用。今天做了一个小实验,总结一下。还是先看代码吧。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Web; using System.Net; using System.IO; using System.Windows.Forms; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { Console.WriteLine( System.Threading.Thread.CurrentThread.ManagedThreadId.ToString()); XqHttpClient xc = new XqHttpClient(); XqFormData xf = new XqFormData(); xf.Add("username", "用户名字"); xf.Add("password", "用户密码"); xf.Add("loginsubmit", "登 陆"); int j=0; for (j = 0; j < 200; j++) { xc.SendPostRequest("http://blog.runsky.com/batch.login.php?action=login", xf.formData); xc.SendGetRequest("http://www.baidu.com"); } Console.WriteLine("main thread........................."); System.Threading.Thread.Sleep(1000); } } //构建窗体数据,并UrlEncoded class XqFormData { public XqFormData(Encoding e) { this.formEncoding = e; } public XqFormData() { this.formEncoding = Encoding.GetEncoding("gb2312"); } public string formData { get { return str.Substring(0, str.Length - 1); } set { } } private Encoding formEncoding; private string str; public void Add(string name, string value) { str += HttpUtility.UrlEncode(name, this.formEncoding); str += "="; str += HttpUtility.UrlEncode(value, Encoding.GetEncoding("gb2312")); str += "&"; } } /// <summary> /// HTTP通用客户端 /// </summary> class XqHttpClient { //服务器端的编码方式UTF8/GB2312 private Encoding encodingServer = Encoding.GetEncoding("gb2312"); private string strURI; //请求行,URLEncoded的UNICODE表示 private string strFormData; //窗体数据,URLEncoed的UNICODE表示 CookieContainer currentCookieContainer = new CookieContainer(); //当前的Cookie容器 private string strResponseBody = ""; //服务器返回的消息主体 //重载 public void SendPostRequest(string uri, string formData) { this.strURI = uri; this.strFormData = formData; SendPostRequest(); } //重载 public void SendGetRequest(string uri) { this.strURI = uri; SendGetRequest(); } //BeginGetResponse完成之后,此处有CLR其他线程执行 public void GetResponseDone(object state) //state自动被赋值为iResult { Console.WriteLine("GetResponseDone: " + System.Threading.Thread.CurrentThread.ManagedThreadId.ToString()); try { IAsyncResult ir = (IAsyncResult)state; HttpWebRequest req = (HttpWebRequest)ir.AsyncState; HttpWebResponse resp = (HttpWebResponse)(req.EndGetResponse(ir)); //读取消息标头 //读取消息主体 Stream ResponseStream = resp.GetResponseStream(); StreamReader sr = new StreamReader(ResponseStream, this.encodingServer); while (sr.Peek() >= 0) { char[] recv = new char[1024]; sr.Read(recv, 0, recv.Length); strResponseBody += new string(recv); } } catch (Exception e) { Console.WriteLine(e.ToString()); } //Console.WriteLine(this.strResponseBody); } //发送GET请求 public void SendGetRequest() { try { HttpWebRequest req = (HttpWebRequest)WebRequest.Create(strURI); req.Method = "GET"; req.CookieContainer = this.currentCookieContainer; //发送并取得服务器回应 //APM版本 req.BeginGetResponse( new AsyncCallback(this.GetResponseDone), req); //此时,系统会自动把 //ir作为GetResponseDone的参数传送,并把req赋值给ir的AsynState成员。 } catch (Exception e) { Console.WriteLine(e.ToString()); } } //异步调用 public void GetRequestStreamDone(object state) { Console.WriteLine("GetRequestStreamDone: " + System.Threading.Thread.CurrentThread.ManagedThreadId.ToString()); IAsyncResult ir = (IAsyncResult)state; HttpWebRequest req = (HttpWebRequest)ir.AsyncState; Stream requestStream = req.EndGetRequestStream(ir); StreamWriter sw = new StreamWriter(requestStream, Encoding.ASCII); sw.Write(this.strFormData); //写入窗体数据(urlencoded) sw.Close(); requestStream.Close(); //发送并取得服务器回应 req.BeginGetResponse(new AsyncCallback(this.GetResponseDone), req); } //发送POST请求 public void SendPostRequest() { try { HttpWebRequest req = (HttpWebRequest)WebRequest.Create(strURI); //构建标头 req.ContentType = "application/x-www-form-urlencoded"; req.ContentLength = this.strFormData.Length; req.Method = "POST"; req.CookieContainer = this.currentCookieContainer; //构建窗体数据 req.BeginGetRequestStream(this.GetRequestStreamDone, req); } catch (Exception e) { Console.WriteLine(e.ToString()); } } } }
其实APM使用起来相当简单直观。大概步骤如下:
(1)调用HttpWebRequest.BeginXXX(AsyncCallback callback, object state)
此函数发起异步调用,并把这个代表这个异步操作的IAsyncResult对象引用自动作为callback回调函数的实参传递,state则赋值给IAsyncResult对象的AsyncState成员。
(2)在回调函数里调用HttpWebRequest.EndXXX(ir),完成响应读取。
在APM模型里,其实回调函数是在等待的操作完成之后由CLR自动开启新线程执行的。
另外需要注意的是,对于同一个HttpWebRequest对象,不能同时进行混合使用同步和异步方法,BeginGetRequestStream()和BeginGetResponse()必须同时使用,不能把GetRequestStream()和BeginGetResponse()混合使用。也就是说一个特定的HttpWebRequest对象,要么执行异步操作,要么执行同步操作,不能混合同步和异步函数。实际上以首先调用的函数为准,如果先调用同步函数,即使以后调用异步函数,还是会按照同步方式执行。