HttpWebRequest对象池 HTTP协议 HttpWebRequest和 Socket的一点总结

相信接触过网络开发的人对HTTP、HttpWebRequest、Socket这些东西都不陌生吧。它们之间的一些介绍和关系我这里都忽略了。开我们平时开发过程中也是很少有机会接触大什么大并发这个东东,一般大并发我们都需要考虑异步和多线程以及对象池,这个我们以一个简单demo来讲解吧。

主要的调用关系图如下:

类的结构图如下:

一:这里我们依次对这些类做一个简单的说明

HttpRequestInfo:

public string Url:http请求的url字符串,如http://www.baidu.com/
public byte[] PostData:Post请求中的数据
public WebHeaderCollection Headers:请求的头部数据
public bool AllowAutoRedirect :是否允许301、302自动跳转,如果你想获取请求返回的头部信息,建议一般设置为false
public Dictionary<string, string> ExternalData :请求过程中附加的数据(如数据记录的ID),便于在成功或失败回调函数中调用
public Action<HttpContextInfo> ActionCompleted :请求成功后所调用的函数
public Action<HttpRequestException> ActionException:请求失败所调用函数
public HttpRequestInfo Clone():返回当前对象的一个副本。

HttpResponseInfo

public Stream ResponseContent :Http请求返回内容(除头部信息)的对象流
public HttpStatusCode StatusCode:Http返回的状态
public string StatusDescription :Http状态描述
public WebHeaderCollection Headers:Http返回的头部信息
public string GetString(Encoding coding):把http返回体中数据流转换为字符串,转换编码就是我们所传参数。

   public interface IHttpRequest
    {
        void GetResponseAsync(HttpRequestInfo request);
        bool IsBusy { set; get; }
    }

在IHttpRequest接口中,IsBusy属性主要是判断当前对象是否正在使用中,GetResponseAsync方法是真正完成Http请求的方法。

这里我们主要看看HttpRequestFactory的封装吧,管理对象实例的个数,相当于一个对象池,这里的代码主要是基于。net framework2.0的,

首先我们需要2个集合分别管理HttpRequestInfo请求实例和IHttpRequest处理请求实例,

  static Queue<HttpRequestInfo> requestTask = new Queue<HttpRequestInfo>();
   static List<IHttpRequest> Handlers = new List<IHttpRequest>();

而我们暴露给外部的一个主要方法就是AddRequestTask,

  public static void AddRequestTask(HttpRequestInfo info)
        {
            if (!string.IsNullOrEmpty(info.Url))
            {
                lock (Lockobj)
                {
                    Interlocked.Increment(ref requestCount);
                    requestTask.Enqueue(info);
                }
            }
        }
那么这些请求在什么时候被处理了,在一个叫Process方法中处理,

private static void Process(object obj)
        {
            while (true)
            {
                IHttpRequest handler = GetAvailableHttpRequest();
                while (handler == null)
                {
                    Thread.Sleep(100);
                    handler = GetAvailableHttpRequest();
                }
                HttpRequestInfo task = GetTask();
                while (task == null)
                {
                    Thread.Sleep(100);
                    task = GetTask();
                }
                if (task != null && handler != null)
                {
                    Interlocked.Decrement(ref requestCount);
                    handler.GetResponseAsync(task);
                }
                // Thread.Sleep(10);
            }
        }

在这个方法中我们需要调用GetAvailableHttpRequest来获取IHttpRequest处理对象实例,调用GetTask来获取HttpRequestInfo请求实例。如果这2个实例都存在我们调用   IHttpRequest.GetResponseAsync(HttpRequestInfo);方法开始处理http请求。

GetAvailableHttpRequest如下:

  private static IHttpRequest GetAvailableHttpRequest()
        {
            lock (Lockobj)
            {
                for (int i = 0; i < Handlers.Count; i++)
                {
                    if (!Handlers[i].IsBusy)
                    {
                        return Handlers[i];
                    }
                }
                if (Handlers.Count <= MaxRequestCount)
                {
                    IHttpRequest handler = (IHttpRequest)Activator.CreateInstance(_httpRequestType);
                    Handlers.Add(handler);
                    return handler;
                }
            }
            return null;
            //return GetAvailableHttpRequest();
        }

GetAvailableHttpRequest方法中,我们首先在处理对象集合中查找是否有空闲对象,如果有就返回,否则检查当前对象实例个数个数是否达到最大个数,如果没有我们则创建新实例,且加入到集合中,再返回,否者返回null。所以在Process方法中有一个检查,看啊看你返回的IHttpRequest是否为null,请注意这里一定不要用递归来返回有效的IHttpRequest,建议用一个死循环来处理,如果用递归一般不会出现什么问题,但是递归层次嵌套太深就会出现栈溢出错误,我在测试的时候曾经出现过这个问题。GetTask和GetAvailableHttpRequest处理一样。

那么这里的Process方法有在什么地方调用了,在HttpRequestFactory的静态构造函数中调用。

    static HttpRequestFactory()
        {
            MaxRequestCount = 10;
            ThreadPool.QueueUserWorkItem(new WaitCallback(Process));
        }
到这里我们的一个对象池就构造玩了。

二 现在我们来看看RequestHttpWebRequest是如何处理HTTP请求的。它主要使用HttpWebRequest来处理请求。

这里我们主要使用HttpWebRequest的异步方法,因此我们需要构造一个状态对象StateObjectInfo

   class StateObjectInfo : HttpContextInfo
        {
            internal byte[] Buffer { set; get; } //把返回的流写到HttpResponseInfo.ResponseContent 时用到的暂存数组
            internal Stream ReadStream { set; get; }//把返回的流写到HttpResponseInfo.ResponseContent
            internal HttpWebRequest HttpWebRequest { set; get; }
            internal RequestHttpWebRequest RequestHandler { set; get; }//主要便于后面改IsBusy属性。
        }

其GetResponseAsync实现如下:

public void GetResponseAsync(HttpRequestInfo info)
        {
            HttpWebRequest webRequest;
            StateObjectInfo state;
            InitWebRequest(info, out webRequest, out state);
            try
            {
                if (IsHttpPost)
                {
                    webRequest.Method = "POST";
                    webRequest.ContentType = "application/x-www-form-urlencoded";
                    webRequest.BeginGetRequestStream(EndRequest, state);
                }
                else
                {
                    webRequest.BeginGetResponse(EndResponse, state);
                }

            }
            catch (Exception ex)
            {
                HandException(ex, state);
            }
        }

其中InitWebRequest的实现如下:

  private void InitWebRequest(HttpRequestInfo info, out HttpWebRequest webRequest, out StateObjectInfo state)
        {
            IsBusy = true;
            if (info.PostData != null && info.PostData.Length > 0)
            {
                IsHttpPost = true;
            }
            else
            {
                IsHttpPost = false;
            }
            if (info.Url.ToLower().Trim().StartsWith("https"))
            {
                IsHttps = true;
                ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(CheckValidationResult);
                ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;
            }
            else
            {
                IsHttps = false;
            }
            webRequest = HttpWebRequest.CreateDefault(new Uri(info.Url)) as HttpWebRequest;
            if (IsHttps)
            {
                /*基础连接已经关闭: 发送时发生错误 */
                /*无法从传输连接中读取数据: 远程主机强迫关闭了一个现有的连接*/
                webRequest.KeepAlive = false;
                webRequest.ProtocolVersion = HttpVersion.Version10;
                webRequest.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)";
            }
            webRequest.AllowAutoRedirect = info.AllowAutoRedirect;
            if (info.Headers != null && info.Headers.Count > 0)
            {
                foreach (string key in info.Headers.Keys)
                {
                    webRequest.Headers.Add(key, info.Headers[key]);
                }
            }
            //webRequest.Proxy = WebProxy.GetDefaultProxy();   
            //webRequest.Proxy.Credentials = CredentialCache.DefaultCredentials;     
            //webResponse.Headers.Get("Set-Cookie");   
            state = new StateObjectInfo
            {
                Buffer = new byte[1024 * 1024],
                HttpWebRequest = webRequest,
                RequestHandler = this,
                RequestInfo = info,
                ResponseInfo = new HttpResponseInfo()

            };
        }

关于该类的EndRequest、EndResponse我想就没什么说的了,其中ReadCallBack的实现如下:

   void ReadCallBack(IAsyncResult ar)
        {
            StateObjectInfo state = ar.AsyncState as StateObjectInfo;
            try
            {
                int read = state.ReadStream.EndRead(ar);
                if (read > 0)
                {
                    state.ResponseInfo.ResponseContent.Write(state.Buffer, 0, read);
                    state.ReadStream.BeginRead(state.Buffer, 0, state.Buffer.Length, ReadCallBack, state);
                }
                else
                {
                    state.ReadStream.Close();
                    state.HttpWebRequest.Abort();
                    if (state.RequestInfo.ActionCompleted != null)
                    {
                        state.ResponseInfo.ResponseContent.Seek(0, SeekOrigin.Begin);
                        state.RequestInfo.ActionCompleted(state);
                    }
                    state.Buffer = null;
                    state.RequestHandler.IsBusy = false;
                }
            }
            catch (Exception ex)
            {
                HandException(ex, state);
            }
        }

这里还有一个HandException方法需要我们注意:

   private void HandException(Exception ex, StateObjectInfo state)
        {
            if (state.ReadStream != null)
                state.ReadStream.Close();
            if (state.HttpWebRequest != null)
                state.HttpWebRequest.Abort();
            state.Buffer = null;
            if (state.RequestInfo.ActionException != null)
            {
                state.RequestInfo.ActionException(new HttpRequestException(state, ex));
            }
            state.RequestHandler.IsBusy = false;
        }

这里我们在使用HttpWebRequest的时候,在完成使用后一定要关闭请求流

在我们来看看一个简单的调用把:

  public static void DownLoadFile(string remoteurl, string destinationFilePath, string id)
        {
            try
            {
                if (HasIllegalCharacters(destinationFilePath, false))
                {
                    SetFileCopyed(id, "400", "HasIllegalCharacters");
                    return;
                }
                DirectoryInfo dir = new DirectoryInfo(destinationFilePath);
                FileInfo destinationFile = new FileInfo(destinationFilePath);
                if (!destinationFile.Directory.Exists)
                {
                    destinationFile.Directory.Create();
                }
                HttpRequestInfo request = new HttpRequestInfo(remoteurl);
                request.ActionCompleted = new Action<HttpContextInfo>(x =>
                {
                    if (x.ResponseInfo.StatusCode == HttpStatusCode.OK)
                    {
                        using (Stream wr = File.Open(destinationFilePath, FileMode.OpenOrCreate, FileAccess.Write), sr = x.ResponseInfo.ResponseContent)
                        {                   
                            byte[] data = new byte[1024 * 1024];
                            int readcount = sr.Read(data, 0, data.Length);
                            while (readcount > 0)
                            {
                                wr.Write(data, 0, readcount);
                                readcount = 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值