•  
  •  
  •  
     
  •  
  •  
 
关闭
2011-11-18 21:00 5393人阅读 评论(1) 收藏 举报

此文章假设读者已经熟悉在.NET下通过HttpWebRequest/WebClient来操作网页,

但是由于学艺不精或经验不够丰富,仍有很多困惑和疑问?

那么下面就通过一系列演示来解决其中一些问题。

废话不多,先列举一些HTTP/HTTPS操作过程经常遇到的问题:

1、HTTP协议头参数?

示例:

上图是浏览google时通过IE9.0开发者工具抓到的HTTP数据包,如图中所示,HTTP协议头

存在一些固定的键值对;很多人经常搞不清楚这些协议头到底是否必须要?是否必须和浏览器

提交时抓去到的一模一样去提交?

要回答这2个问题,一是需要对HTTP协议有一个简单了解,二是要根据具体应用进行分析;

如:Accept参数,细心一点就会发现请求页面时可能为 text/html 请求图片时就为image/jpeg

当然根据系统环境,还有Application/xml一类等。 那么你需要根据需求………

又比如:user-Agent参数,很明显里面包含的是系统类型与浏览器类型,假设你需要伪造!!

还有如:Accept-Encoding参数,如果大家在使用HttpWebRequest请求网页时也添加了如上图的 Accept-Encoding参数,那自己会很杯具的发现,请求回来的内容需要先gzip解压;该怎么做你应该知道了!!

类似上面提交的三种情况,我们需要的就是经验和灵活应用,作为开发者,我们的优势是可以换位

以一个开发者的角度来思考问题:比如我是否会通过Http Header中的Referer参数头来判断访问者

来路,是否允许它请求;

我的做法就是简单、简单、简单:

 StringBuilder bulider = new StringBuilder();
                bulider.AppendLine("POST /user/pass_request HTTP/1.1");
                bulider.AppendLine("Host: www.*.com");
                bulider.AppendLine("User-Agent: Mozilla/5.0 (Windows NT 6.1; IE 9.0)");
                bulider.AppendLine("Accept: text");
                bulider.AppendLine("Content-Type: application/x-www-form-urlencoded");
                bulider.AppendLine(string.Format("Content-Length: {0}\r\n", Encoding.Default.GetBytes(strPost).Length));
                bulider.Append(strPost);
   需要补充的是Post操作时必须包含Content-Type: application/x-www-form-urlencoded参数;

2、HTTP与HTTPS的区别?

HTTPS相比HTTP是一种安全可靠的连接,打开一个HTTPS连接,我们会发现浏览器都会有相应的提示,   类似这样的,可以通过点击其图标查看安全状态和证书;

那么我们用HttpWebRequest操作HTTPS和HTTP时有什么区别或者不一定的地方呢?

其实大部分地方都是一样的,很多网站在服务器段并没有做非常严格的限制和配置,在做HTTPS操作时甚至不需要添加证书,但是如果遇到必须要使用证书的,那就需要指定HttpWebRequest的Credentials属性;

关于这一点就不详解了,有兴趣的可以关注下苏飞的文章http://www.cnblogs.com/sufei/archive/2011/10/22/2221289.html

另外关注HttpWebRequest操作HTTPS的文章网上也较多,大家可自行搜索!

3、Cookies问题?

HttpwebRequest好的一点是我们不需要去关注Cookies,.NET中提供了CookieContainer类来做 Cookies容器,很好的与HttpWebRequest结合,使得我们不必要自己去处理Cookies,当然一些涉及到修改Cookies内容的时候还是有必要的。

例如一年前我分析过拍拍网->财付通的跳转,它就在Cookies中存放了一个参数导致在跳转过程中不需要重新登录,而直接从HTTP页面访问至HTTPS;

下面在讲Socket操作时就需要特别关注下这个Cookies咯!

4、速度问题?

毫无疑问由于HTTP协议是基于TCP/IP的,而HttpWebRequest在封装过程中的一些处理或多或少的会影响到访问速度;至于影响多少,我在前段时间做一个国外网站操作的时候简单对比了下,HttpWebRequest和Socket原生操作的速度相差大概在5倍以上;

很多时候其实我们并不是很关注速度影响,但是实际应用过程中就会遇到有客户要求的飞速(当然不排除一些客户认为线程越多速度越快)。

简单谈了下以上4个问题,其实还很是很片面,鉴于个人表达能力有限,有些东西还需要大家在实践中去认识了解;下面就来着重看下通过Socket操作HTTP/HTTPS;

前面我们已经知道了简单的HTTP协议,也知道HTTP是基于TCP/IP协议的,对于有网络经验的同学,我们就可以直接写Socket提交HTTP协议,这一步相对比较简单,我们直接看一下代码就OK了:

static byte[] InternalSocketHttp(IPEndPoint endpoint,
           HttpArgs args,
           HttpMethod method)
        {
            using (Socket sK = new Socket(AddressFamily.InterNetwork,
                        SocketType.Stream,
                        ProtocolType.Tcp))
            {
                try
                {
                    sK.Connect(endpoint);
                    if (sK.Connected)
                    {
                        byte[] buff = ParseHttpArgs(method, args);
                        if (sK.Send(buff) > 0)
                        {
                            return ParseResponse(endpoint,sK,args);
                        }
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
            }
            return null;
        }

其中有2个比较重要的函数一个是ParseHttpArgs(),另外一个是ParseResponse();

先看第一个函数:

static byte[] ParseHttpArgs(HttpMethod method, HttpArgs args)
        {
            StringBuilder bulider = new StringBuilder();
            if (method.Equals(HttpMethod.POST))
            {
                bulider.AppendLine(string.Format("POST {0} HTTP/1.1",
                    args.Url));
                bulider.AppendLine("Content-Type: application/x-www-form-urlencoded");
            }
            else
            {
                bulider.AppendLine(string.Format("GET {0} HTTP/1.1",
                args.Url));
            }
            bulider.AppendLine(string.Format("Host: {0}",
                args.Host));
            bulider.AppendLine("User-Agent: Mozilla/5.0 (Windows NT 6.1; IE 9.0)");
            if (!string.IsNullOrEmpty(args.Referer))
                bulider.AppendLine(string.Format("Referer: {0}",
                    args.Referer));
            bulider.AppendLine("Connection: keep-alive");
            bulider.AppendLine(string.Format("Accept: {0}",
                args.Accept));
            bulider.AppendLine(string.Format("Cookie: {0}",
                args.Cookie));
            if (method.Equals(HttpMethod.POST))
            {
                bulider.AppendLine(string.Format("Content-Length: {0}\r\n",
                   Encoding.Default.GetBytes(args.Body).Length));
                bulider.Append(args.Body);
            }
            else
            {
                bulider.Append("\r\n");
            }
            string header = bulider.ToString();
            return Encoding.Default.GetBytes(header);
        }

通过上面的代码,很清晰的我们就能看到ParseHttpArgs其实就是将HttpArgs的一些属性填充为HTTP协议,并返回其二进制内容用于Socket提交,其中值得注意的一点就是在HTTP协议头完毕后实际上需要一个空行,这一点有疑惑的同学请看HTTP协议详解:http://www.cnblogs.com/li0803/archive/2008/11/03/1324746.html

至于ParseResponse函数我们暂时先不讲解,其大致过程是先读取Socket ,读取出Http返回包的协议头,

然后根据不同的协议头进行下一步处理;

-----------------------到此为止,简单的利用Socket进行HTTP操作的例子已经有了,无非就是填充协议,提交数据,解析返回数据;

下面我们来了解下HTTP返回协议头:

HTTP/1.1 200   HTTP/1.1 404 大家都比较熟悉,一个是成功 一个是404无法访问,我们需要关注的是 HTTP/1.1 302 ,对于302的解释大家可以google下.

而我们的任务是需要处理302,在浏览器操作时遇到301 302之类的协议时,一般浏览器会自动帮我们进行跳转,而我们使用HttpWebRequest操作时也可以通过指定AllowAutoRedirect属性来响应重定向;

那么在Socket提交时,可就没有那么智能了,这时候就需要我们自己处理302,否则你会发现你请求的结果和你的预期不一样;

 if (header.StartsWith("HTTP/1.1 302"))
                {
                    int start = header
                        .ToUpper().IndexOf("LOCATION");
                    if (start > 0)
                    {
                        string temp = header.Substring(start, header.Length - start);
                        string[] sArry = Regex.Split(temp, "\r\n");
                        args.Url = sArry[0].Remove(0, 10);
                        return Get(endpoint, args, certificates);  //注意:302协议需要重定向
                    }
                }

上面这段代码时前面提到的ParseResponse函数的一部分,根据302协议描述,我们知道它有一个Location参数,这个参数内容就是需要重定向的地址,当我们判断到302操作时就需要重新提交;

这里有一个问题插播下:HTTP协议是一种短连接,而我们一般做网络通信做Socket操作时,大多数是长连接操作,那么这里我们遇到302 或者 发起一个请求,这个过程是也应该按照HTTP协议的要求进行短连接,即 连接服务器-> 发起一个HTTP请求->收到一个HTTP请求->断开服务器连接。(所以细心的同学就会发现之前的示例中的using语句以及此处的 Get(*,*,*)方法)

OK,简单的了解了302如何处理,还有重要的一点就是Cookies,很多同学都知道Cookies也是HTTP协议的一个参数,在用Socket提交时也需要指定Cookies,这一点很好理解,服务端会根据Cookies来判断页面跳转之间的状态,那么假设你需要你的提交能被服务端正确判断到,那你必须提交Cookies让服务端知道就是你;

知道了这一点,那我们一些同学在添加Cookies的时候就犯难了,抓包的时候发现Cookies里面类似SessionID的一段随机字符串不知道哪里来的,oh my god,我用浏览器浏览的时候会有这个值,那用Socket的时候怎么办呢,其实在.NET中有一个类SessionIDManager (System.Web.SessionState下)可以帮助我们,

 sessionID = sessionIDManager.CreateSessionID(null);
bulider.AppendLine(string.Format("Cookie: Language=en-US;ASP.NET_SessionId={0}",
                    sessionID));

这下应该明白了吧;

说了这么多,我们还是来看下ParseResponse函数吧(注意:这只是一个简单示例,并不一定完全正确,不完善,请酌情使用)

private static byte[] ParseResponse(IPEndPoint endpoint,
             Socket sK,
             HttpArgs args)
        {
            //尝试10秒时间读取协议头
            CancellationTokenSource source = new CancellationTokenSource();
            Task<string> myTask = Task.Factory.StartNew<string>(
                new Func<object, string>(ReadHeaderProcess),
                sK,
                source.Token);
            if (myTask.Wait(10000))
            {
                string header = myTask.Result;
                if (header.StartsWith("HTTP/1.1 302"))
                {
                    int start = header
                        .ToUpper().IndexOf("LOCATION");
                    if (start > 0)
                    {
                        string temp = header.Substring(start, header.Length - start);
                        string[] sArry = Regex.Split(temp, "\r\n");
                        args.Url = sArry[0].Remove(0, 10);
                        return Get(endpoint, args);  //注意:302协议需要重定向
                    }
                }
                else if (header.StartsWith("HTTP/1.1 200"))  //继续读取内容
                {
                    int start = header
                           .ToUpper().IndexOf("CONTENT-LENGTH");
                    int content_length = 0;
                    if (start > 0)
                    {
                        string temp = header.Substring(start, header.Length - start);
                        string[] sArry = Regex.Split(temp, "\r\n");
                        content_length = Convert.ToInt32(sArry[0].Split(':')[1]);
                        if (content_length > 0)
                        {
                            byte[] bytes = new byte[content_length];
                            if (sK.Receive(bytes) > 0)
                            {
                                return bytes;
                            }
                        }
                    }
                    else
                    {
                        //不存在Content-Length协议头
                        return ParseResponse(sK);
                    }
                }
                else
                {
                    return Encoding.Default.GetBytes(header);
                }
            }
            else
            {
                source.Cancel();  //超时的话,别忘记取消任务哦
            }
            return null;
        }

解析下上面这段代码:

1)异步读取返回的协议头;设定超时时间!!!!!

2)解析协议头 ,200  / 302  /404 等!!!!

示例:

        /// <summary>
        ///  读取协议头
        /// </summary>
        /// <param name="args"></param>
        /// <returns></returns>
        static string ReadHeaderProcess(object args)
        {
            Socket sK = (Socket)args;
            StringBuilder bulider = new StringBuilder();
            while (true)
            {
                byte[] buff = new byte[1];
                int read = sK.Receive(buff, SocketFlags.None);
                if (read > 0)
                {
                    bulider.Append((char)buff[0]);
                }
                string temp = bulider.ToString();
                if (temp.Contains("\r\n\r\n"))
                {
                    break;
                }
            }
            return bulider.ToString();
        }

3)根据不同返回类型做不同操作!!!!

在返回协议中没有判断到Content-Length参数时通过ParseResponse(sK)方法去解析内容,这里需要说明的是这个函数并不完全正确,通过循环读取 判断直到读取到</html> 就认为结束,所以很有可能产生死循环,其代码如下:

 /// <summary>
        /// 注意:此函数可能产生死循环
        /// </summary>
        /// <param name="ssl"></param>
        /// <returns></returns>
        static byte[] ParseResponse(Socket sK)
        {
            ArrayList array = new ArrayList();
            StringBuilder bulider = new StringBuilder();
            int length = 0;
            while (true)
            {
                byte[] buff = new byte[1024];
                int len = sK.Receive(buff);
                if (len > 0)
                {
                    length += len;
                    byte[] reads = new byte[len];
                    Array.Copy(buff, 0, reads, 0, len);
                    array.Add(reads);
                    bulider.Append(Encoding.Default.GetString(reads));
                }
                string temp = bulider.ToString();
                if (temp.ToUpper().Contains("</HTML>"))
                {
                    break;
                }
            }
            byte[] bytes = new byte[length];
            int index = 0;
            for (int i = 0; i < array.Count; i++)
            {
                byte[] temp = (byte[])array[i];
                Array.Copy(temp, 0, bytes,
                    index, temp.Length);
                index += temp.Length;
            }
            return bytes;
        }

OK,OK,又说了一大堆关于Socket操作HTTP的东东,其中提到了Cookies 提到了302 提到了HTTP协议,也基本对应与文章开头提到的几个问题;下面我们还要继续关注下Socket如何操作HTTPS;

其实使用Socket操作HTTPS时与HTTP还是有一些不同的,首先证书加载无疑,还有一点就是连接,一般HTTP服务器端口80,而HTTPS服务端口是443,

如果有人妄图通过Socket提交 适用HTTPS协议的加密数据的话,那我没话说,您牛! 我这里需要讲解的是通过SslStream 加载证书来完成Socket下对HTTPS的操作;

来看下示例代码:

 static byte[] InternalSslSocketHttp(IPEndPoint endpoint,
            X509CertificateCollection certificates,
            HttpArgs args,
            HttpMethod method)
        {
            TcpClient tcp = new TcpClient();
            try
            {
                tcp.Connect(endpoint);
                if (tcp.Connected)
                {
                    using (SslStream ssl = new SslStream(tcp.GetStream(),
                        false,
                        new RemoteCertificateValidationCallback(ValidateServerCertificate),
                        null))
                    {
                        ssl.AuthenticateAsClient("ServerName",
                            certificates,
                            SslProtocols.Tls,
                            false);
                        if (ssl.IsAuthenticated)
                        {
                            byte[] buff = ParseHttpArgs(method, args);  //生成协议包
                            ssl.Write(buff);
                            ssl.Flush();
                            return ParseSslResponse(endpoint, ssl, args, certificates);
 
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            return null;
        }

怎么样,是否是似曾相识,跟Socket操作HTTP时结构很相似唉~~  ,不同的是这里使用TcpClient,

这里有个参数是X509CertificateCollection ,

其实这一切都只为了SslStream,有了SslStream 我想大家也都明白了,剩下的事情是差不多的了。

提一点关于ValidateServerCertificate这个函数,有过证书操作经验的同学应该不陌生了,大部分情况下,验证客户端证书也好,服务端证书也好,我们经常是直接返回一个 true~~  (我不知道为啥, 但是我做上一个WCF应用的时候也是这样干的) 所以在有遇到证书检验的时候,大家不妨也直接来个return true试试先;

至于剩下的代码我就不详细说咯:贴一下我自己用到的HttpHelper,其中一些代码是刚写的,有错误的地方还请大家海涵,有需要的就直接copy下去,自己用的时候自己调试吧!

using System;
using System.Collections;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
/************************************************************************/
/* Author:huliang
 * Email:huliang@yahoo.cn
 * QQ:12658501
 * 说明:转载请注明出处
/************************************************************************/
 
namespace iGame
{
    class HttpArgs
    {
        public string Url { get; set; }
        public string Host { get; set; }
        public string Accept { get; set; }
        public string Referer { get; set; }
        public string Cookie { get; set; }
        public string Body { get; set; }
    }
 
    static class HttpHelper
    {
        /// <summary>
        /// 提交方法
        /// </summary>
        enum HttpMethod
        {
            GET,
            POST
        }
 
        #region HttpWebRequest & HttpWebResponse
 
        /// <summary>
        /// Get方法
        /// </summary>
        /// <param name="geturl">请求地址</param>
        /// <param name="cookieser">Cookies存储器</param>
        /// <returns>请求返回的Stream</returns>
        public static string Get(string url,
            CookieContainer cookies,
            Encoding encoding)
        {
            return InternalHttp(HttpMethod.GET, url, null, cookies, encoding);
        }
 
        public static Stream Get(string url,
            CookieContainer cookies)
        {
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
            request.Method = "GET";
            request.UserAgent = "Mozilla/5.0 (Windows NT 6.1;MSIE 6.0;)";
            request.CookieContainer = cookies;
            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            return response.GetResponseStream();
        }
 
        /// <summary>
        /// Post方法
        /// </summary>
        /// <param name="posturl">请求地址</param>
        /// <param name="bytes">Post数据</param>
        /// <param name="cookieser">Cllkies存储器</param>
        /// <returns>请求返回的流</returns>
        public static string Post(string url,
            byte[] bytes,
            CookieContainer cookies,
            Encoding encoding)
        {
            return InternalHttp(HttpMethod.POST, url, bytes, cookies, encoding);
        }
 
        /// <summary>
        /// Http操作
        /// </summary>
        /// <param name="method">请求方式</param>
        /// <param name="url">请求地址</param>
        /// <param name="bytes">提交数据</param>
        /// <param name="cookieser">Cookies存储器</param>
        /// <returns>请求结果</returns>
        static string InternalHttp(HttpMethod method,
            string url,
            byte[] bytes,
            CookieContainer cookies,
            Encoding encoding)
        {
            if (string.IsNullOrEmpty(url))
                throw new ArgumentNullException("访问url不能为空");
            if (method == HttpMethod.POST)
            {
                if (bytes == null)
                    throw new ArgumentNullException("提交的post数据不能为空");
            }
            if (cookies == null)
                throw new ArgumentNullException("Cookies存储器不能为空");
            try
            {
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
                request.Method = method.ToString();
                request.UserAgent = "Mozilla/5.0 (Windows NT 6.1;MSIE 9.0;)";
                request.CookieContainer = cookies;
                if (method == HttpMethod.POST)
                {
                    request.ContentType = "application/x-www-form-urlencoded";
                    request.ContentLength = bytes.Length;
                    using (Stream stream = request.GetRequestStream())
                    {
                        stream.Write(bytes, 0, bytes.Length);
                        stream.Flush();
                        stream.Close();
                    }
                }
                HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                if (response.StatusCode == HttpStatusCode.OK)
                {
                    using (StreamReader reader = new StreamReader(response.GetResponseStream(), encoding))
                    {
                        return reader.ReadToEnd();
                    }
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.Message);
            }
            return null;
        }
 
        #endregion
 
        #region Ssl Socket
 
        static bool ValidateServerCertificate(
                 object sender,
                 X509Certificate certificate,
                 X509Chain chain,
                 SslPolicyErrors sslPolicyErrors)
        {
            /*
            if (sslPolicyErrors == SslPolicyErrors.None)
                return true;
            Console.WriteLine("Certificate error: {0}", sslPolicyErrors);
            return false;
            */
            return true;
        }
 
        public static byte[] Get(IPEndPoint endpoint, HttpArgs args, X509CertificateCollection certificates)
        {
            return InternalSslSocketHttp(endpoint, certificates, args, HttpMethod.GET);
        }
 
        public static byte[] Post(IPEndPoint endpoint,
            HttpArgs args,
            X509CertificateCollection certificates)
        {
            return InternalSslSocketHttp(endpoint, certificates, args, HttpMethod.POST);
        }
 
        static byte[] InternalSslSocketHttp(IPEndPoint endpoint,
            X509CertificateCollection certificates,
            HttpArgs args,
            HttpMethod method)
        {
            TcpClient tcp = new TcpClient();
            try
            {
                tcp.Connect(endpoint);
                if (tcp.Connected)
                {
                    using (SslStream ssl = new SslStream(tcp.GetStream(),
                        false,
                        new RemoteCertificateValidationCallback(ValidateServerCertificate),
                        null))
                    {
                        ssl.AuthenticateAsClient("ServerName",
                            certificates,
                            SslProtocols.Tls,
                            false);
                        if (ssl.IsAuthenticated)
                        {
                            byte[] buff = ParseHttpArgs(method, args);  //生成协议包
                            ssl.Write(buff);
                            ssl.Flush();
                            return ParseSslResponse(endpoint, ssl, args, certificates);
 
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            return null;
        }
 
        /// <summary>
        /// 解析 Ssl Response
        /// </summary>
        /// <param name="endpoint"></param>
        /// <param name="ssl"></param>
        /// <param name="args"></param>
        /// <param name="certificates"></param>
        /// <returns></returns>
        private static byte[] ParseSslResponse(IPEndPoint endpoint,
            SslStream ssl,
            HttpArgs args,
            X509CertificateCollection certificates)
        {
            //尝试10秒时间读取协议头
            CancellationTokenSource source = new CancellationTokenSource();
            Task<string> myTask = Task.Factory.StartNew<string>(
                new Func<object, string>(ReadSslHeaderProcess),
                ssl,
                source.Token);
            if (myTask.Wait(10000))
            {
                string header = myTask.Result;
                if (header.StartsWith("HTTP/1.1 302"))
                {
                    int start = header
                        .ToUpper().IndexOf("LOCATION");
                    if (start > 0)
                    {
                        string temp = header.Substring(start, header.Length - start);
                        string[] sArry = Regex.Split(temp, "\r\n");
                        args.Url = sArry[0].Remove(0, 10);
                        return Get(endpoint, args, certificates);  //注意:302协议需要重定向
                    }
                }
                else if (header.StartsWith("HTTP/1.1 200"))  //继续读取内容
                {
                    int start = header
                           .ToUpper().IndexOf("CONTENT-LENGTH");
                    int content_length = 0;
                    if (start > 0)
                    {
                        string temp = header.Substring(start, header.Length - start);
                        string[] sArry = Regex.Split(temp, "\r\n");
                        content_length = Convert.ToInt32(sArry[0].Split(':')[1]);
                        if (content_length > 0)
                        {
                            byte[] bytes = new byte[content_length];
                            if (ssl.Read(bytes, 0, bytes.Length) > 0)
                            {
                                return bytes;
                            }
                        }
                    }
                    else
                    {
                        //不存在Content-Length协议头
                        return ParseSslResponse(ssl);
                    }
                }
                else
                {
                    return Encoding.Default.GetBytes(header);
                }
            }
            else
            {
                source.Cancel();  //超时的话,别忘记取消任务哦
            }
            return null;
        }
 
        /// <summary>
        ///  读取协议头
        /// </summary>
        /// <param name="args"></param>
        /// <returns></returns>
        static string ReadSslHeaderProcess(object args)
        {
            SslStream ssl = (SslStream)args;
            StringBuilder bulider = new StringBuilder();
            while (true)
            {
                int read = ssl.ReadByte();
                if (read != -1)
                {
                    byte b = (byte)read;
                    bulider.Append((char)b);
                }
                string temp = bulider.ToString();
                if (temp.Contains("\r\n\r\n"))
                {
                    break;
                }
            }
            return bulider.ToString();
        }
 
        /// <summary>
        /// 注意:此函数可能产生死循环
        /// </summary>
        /// <param name="ssl"></param>
        /// <returns></returns>
        static byte[] ParseSslResponse(SslStream ssl)
        {
            //没有指定协议头,尝试读取至</html>
            ArrayList array = new ArrayList();
            StringBuilder bulider = new StringBuilder();
            int length = 0;
            while (true)
            {
                byte[] buff = new byte[1024];
                int len = ssl.Read(buff, 0, buff.Length);
                if (len > 0)
                {
                    length += len;
                    byte[] reads = new byte[len];
                    Array.Copy(buff, 0, reads, 0, len);
                    array.Add(reads);
                    bulider.Append(Encoding.Default.GetString(reads));
                }
                string temp = bulider.ToString();
                if (temp.ToUpper().Contains("</HTML>"))
                {
                    break;
                }
            }
            byte[] bytes = new byte[length];
            int index = 0;
            for (int i = 0; i < array.Count; i++)
            {
                byte[] temp = (byte[])array[i];
                Array.Copy(temp, 0, bytes,
                    index, temp.Length);
                index += temp.Length;
            }
            return bytes;
        }
 
        #endregion
 
        #region Socket
 
        public static byte[] Get(IPEndPoint endpoint,
            HttpArgs args)
        {
            return InternalSocketHttp(endpoint, args, HttpMethod.GET);
        }
 
        public static byte[] Post(IPEndPoint endpoint,
            HttpArgs args)
        {
            return InternalSocketHttp(endpoint, args, HttpMethod.POST);
        }
 
        static byte[] InternalSocketHttp(IPEndPoint endpoint,
           HttpArgs args,
           HttpMethod method)
        {
            using (Socket sK = new Socket(AddressFamily.InterNetwork,
                        SocketType.Stream,
                        ProtocolType.Tcp))
            {
                try
                {
                    sK.Connect(endpoint);
                    if (sK.Connected)
                    {
                        byte[] buff = ParseHttpArgs(method, args);
                        if (sK.Send(buff) > 0)
                        {
                            return ParseResponse(endpoint,sK,args);
                        }
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
            }
            return null;
        }
 
        private static byte[] ParseResponse(IPEndPoint endpoint,
             Socket sK,
             HttpArgs args)
        {
            //尝试10秒时间读取协议头
            CancellationTokenSource source = new CancellationTokenSource();
            Task<string> myTask = Task.Factory.StartNew<string>(
                new Func<object, string>(ReadHeaderProcess),
                sK,
                source.Token);
            if (myTask.Wait(10000))
            {
                string header = myTask.Result;
                if (header.StartsWith("HTTP/1.1 302"))
                {
                    int start = header
                        .ToUpper().IndexOf("LOCATION");
                    if (start > 0)
                    {
                        string temp = header.Substring(start, header.Length - start);
                        string[] sArry = Regex.Split(temp, "\r\n");
                        args.Url = sArry[0].Remove(0, 10);
                        return Get(endpoint, args);  //注意:302协议需要重定向
                    }
                }
                else if (header.StartsWith("HTTP/1.1 200"))  //继续读取内容
                {
                    int start = header
                           .ToUpper().IndexOf("CONTENT-LENGTH");
                    int content_length = 0;
                    if (start > 0)
                    {
                        string temp = header.Substring(start, header.Length - start);
                        string[] sArry = Regex.Split(temp, "\r\n");
                        content_length = Convert.ToInt32(sArry[0].Split(':')[1]);
                        if (content_length > 0)
                        {
                            byte[] bytes = new byte[content_length];
                            if (sK.Receive(bytes) > 0)
                            {
                                return bytes;
                            }
                        }
                    }
                    else
                    {
                        //不存在Content-Length协议头
                        return ParseResponse(sK);
                    }
                }
                else
                {
                    return Encoding.Default.GetBytes(header);
                }
            }
            else
            {
                source.Cancel();  //超时的话,别忘记取消任务哦
            }
            return null;
        }
 
        /// <summary>
        ///  读取协议头
        /// </summary>
        /// <param name="args"></param>
        /// <returns></returns>
        static string ReadHeaderProcess(object args)
        {
            Socket sK = (Socket)args;
            StringBuilder bulider = new StringBuilder();
            while (true)
            {
                byte[] buff = new byte[1];
                int read = sK.Receive(buff, SocketFlags.None);
                if (read > 0)
                {
                    bulider.Append((char)buff[0]);
                }
                string temp = bulider.ToString();
                if (temp.Contains("\r\n\r\n"))
                {
                    break;
                }
            }
            return bulider.ToString();
        }
 
        /// <summary>
        /// 注意:此函数可能产生死循环
        /// </summary>
        /// <param name="ssl"></param>
        /// <returns></returns>
        static byte[] ParseResponse(Socket sK)
        {
            ArrayList array = new ArrayList();
            StringBuilder bulider = new StringBuilder();
            int length = 0;
            while (true)
            {
                byte[] buff = new byte[1024];
                int len = sK.Receive(buff);
                if (len > 0)
                {
                    length += len;
                    byte[] reads = new byte[len];
                    Array.Copy(buff, 0, reads, 0, len);
                    array.Add(reads);
                    bulider.Append(Encoding.Default.GetString(reads));
                }
                string temp = bulider.ToString();
                if (temp.ToUpper().Contains("</HTML>"))
                {
                    break;
                }
            }
            byte[] bytes = new byte[length];
            int index = 0;
            for (int i = 0; i < array.Count; i++)
            {
                byte[] temp = (byte[])array[i];
                Array.Copy(temp, 0, bytes,
                    index, temp.Length);
                index += temp.Length;
            }
            return bytes;
        }
        #endregion
 
        #region  Helper
 
        static byte[] ParseHttpArgs(HttpMethod method, HttpArgs args)
        {
            StringBuilder bulider = new StringBuilder();
            if (method.Equals(HttpMethod.POST))
            {
                bulider.AppendLine(string.Format("POST {0} HTTP/1.1",
                    args.Url));
                bulider.AppendLine("Content-Type: application/x-www-form-urlencoded");
            }
            else
            {
                bulider.AppendLine(string.Format("GET {0} HTTP/1.1",
                args.Url));
            }
            bulider.AppendLine(string.Format("Host: {0}",
                args.Host));
            bulider.AppendLine("User-Agent: Mozilla/5.0 (Windows NT 6.1; IE 9.0)");
            if (!string.IsNullOrEmpty(args.Referer))
                bulider.AppendLine(string.Format("Referer: {0}",
                    args.Referer));
            bulider.AppendLine("Connection: keep-alive");
            bulider.AppendLine(string.Format("Accept: {0}",
                args.Accept));
            bulider.AppendLine(string.Format("Cookie: {0}",
                args.Cookie));
            if (method.Equals(HttpMethod.POST))
            {
                bulider.AppendLine(string.Format("Content-Length: {0}\r\n",
                   Encoding.Default.GetBytes(args.Body).Length));
                bulider.Append(args.Body);
            }
            else
            {
                bulider.Append("\r\n");
            }
            string header = bulider.ToString();
            return Encoding.Default.GetBytes(header);
        }
 
        #endregion
    }
}
 
0
0
 
 
猜你在找
查看评论
1楼 xuxubaby 2013-11-18 16:55发表 [回复]
文章很好,谢分享。
 
发表评论
  • 用 户 名:
  • 评论内容:
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
 
 
 
 
    个人资料
 
 
    • 访问:216888次
    • 积分:2714
    • 等级: 
    • 排名:第8611名
    • 原创:61篇
    • 转载:77篇
    • 译文:2篇
    • 评论:23条
    文章搜索
    文章分类
    文章存档
    最新评论
 
 
公司简介|招贤纳士|广告服务|银行汇款帐号|联系方式|版权声明|法律顾问|问题报告|合作伙伴|论坛反馈
网站客服杂志客服微博客服webmaster@csdn.net400-600-2320|北京创新乐知信息技术有限公司 版权所有|江苏乐知网络技术有限公司 提供商务支持
京 ICP 证 09002463 号|Copyright © 1999-2014, CSDN.NET, All Rights Reserved 

 

学习S5的更多相关文章

  1. CSS3与页面布局学习总结(四)——页面布局大全

    一.负边距与浮动布局 1.1.负边距 所谓的负边距就是margin取负值的情况,如margin:-100px,margin:-100%.当一个元素与另一个元素margin取负值时将拉近距离.常见的功能 ...

  2. java学习-关于字符串String

    有必要总结记录一下java的学习,否则,永远只是记忆碎片化和always google(费时) 刚好,小伙伴给了一份自己做的review,在学习的过程中,update一下自己的见解和学习内容: 关于S ...

  3. Android学习——windows下搭建Cygwin环境

    在上一篇博文<Android学习——windows下搭建NDK_r9环境>中,我们详细的讲解了在windows下进行Android NDK开发环境的配置,我们也讲到了在NDk r7以后,我 ...

  4. Android学习——windows下搭建NDK_r9环境

    1. NDK(Native Development Kit) 1.1 NDK简介 Android NDK是一套允许开发人员使用本地代码(如C/C++)进行Android APP功能开发的工具,通过这个 ...

  5. 小菜学习设计模式(三)—工厂方法(Factory Method)模式

    前言 设计模式目录: 小菜学习设计模式(一)—模板方法(Template)模式 小菜学习设计模式(二)—单例(Singleton)模式 小菜学习设计模式(三)—工厂方法(Factory Method) ...

  6. HTTPS学习总结

    p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 21.0px Verdana; color: #393939 } span.s1 { } HTTPS学习总结 ...

  7. JavaWeb学习总结(三)——Tomcat服务器学习和使用(二) 包含https 非对称秘钥 NB

    JavaWeb学习总结(三)--Tomcat服务器学习和使用(二) 一.打包JavaWeb应用 在Java中,使用"jar"命令来对将JavaWeb应用打包成一个War包,jar命 ...

  8. Android学习笔记(二)——探究一个活动

    //此系列博文是<第一行Android代码>的学习笔记,如有错漏,欢迎指正! 活动(Activity)是最容易吸引到用户的地方了,它是一种可以包含用户界面的组件,主要用于和用户进行交互.一 ...

  9. java基础学习总结——基础语法2

    一.语句

随机推荐

  1. java 实现视频转换通用工具类:视频相互转换-Ffmpeg(三)

    java 实现视频转换通用工具类:获取视频元数据信息(一) java 实现视频转换通用工具类:视频相互转换-总方法及Mencoder(二) 这节主要是ffmpeg的相关方法封装,在实际调用中主要使用f ...

  2. paxos 实现

    原文地址:http://rdc.taobao.com/blog/cs/?p=162 本文主要介绍zookeeper中zookeeper Server leader的选举,zookeeper在选举lea ...

  3. $_SERVER详细资料整理(转)

    PHP编程中经常需要用到一些服务器的一些资料,特把$_SERVER的详细参数整理下,方便以后使用. $_SERVER['PHP_SELF'] #当前正在执行脚本的文件名,与 document root ...

  4. metasploit(MSF)终端命令大全

    show exploits   列出metasploit框架中的所有渗透攻击模块. show payloads   列出metasploit框架中的所有攻击载荷. show auxiliary   列 ...

  5. PHP函数ip2long转换IP时数值太大产生负数的解决办法

    有两种办法: 1. bindec( decbin($long))  利用bindec和decbin两个函数转换一次就没有问题了 我一直在用上面的方法,但是在升级到PHP7以后就不起作用了(因为最近只进 ...

  6. 【转】int const A::func()和int A::func() const

    int const A::func() { return 0; }int A::func() const { return 0; } 上面的代码是合法的,其中A::func成员函数[只能在成员函数后面 ...

  7. Android View的绘制机制流程深入详解(三)

    本系列文章主要着重深入介绍Android View的绘制机制及流程,第三篇主要介绍并分析视图状态以及重绘流程,首先剖析了 视图的几种状态,然后在深入分析视图的重绘机制流程. 真题园网:http://w ...

  8. Android中的事件分发机制总结

    Android 的事件分发机制 一.View的事件分发总结: View的onTouchEvent和OnTouch区别  还是以自定义的TestButton为例. 我们可以通过重写onTouchEven ...

  9. 有效范围为request的bean

    Car.java类 package tom.jiafei; public class Car { String carnumber; String name; String date; public ...

  10. Java中double类型数据的精度问题

    今天在写段代码模拟计算器的时候,偶然发现,当我进行小数运算的时候,竟然出现了令我惊讶的结果,后来问了问度娘,才晓得,原来这里面还有点知识呢,下面是介绍: 你猜下面几句的结果是多少? public cl ...