HTTP协议:socket处理ajax请求?

来源:互联网 发布:手机评分软件 编辑:程序博客网 时间:2024/06/15 03:22

摘要:HTTP协议又叫超文本传输协议,所有的www文件都必须遵循这个协议。当互联网诞生时,HTTP协议被设计的是从网络上获取数据(客户主动获取数据),一旦服务器对其请求处理之后,浏览器就会断开这条连接。如此,可以想象HTTP是不支持服务端主动推送消息给浏览器的。如果想了解更多推送相关的知识可以阅读《Websocket轻量级消息推送 & 浏览器socket通信》一文,主要阐述了服务端要如何解析Websocket协议,另外可以阅读《古老浏览器与Flash socket通信》一文,它阐述了Flash(也就是swf)如何与js通信,Flash如何通过socket与远程跨域通信。而本文的主要内容是:使用socket(TCP连接)实现HTTP协议并充当服务端,对ajax的HTTP请求进行处理。


目录:

-------------------------------------------

- No Web Server

- ajax GET请求

- socket recv数据包

- ajax 跨域及TCP解析HTTP

- POST的缺陷


1.No Web Server

        在记忆当中,浏览器的HTTP请求总是伴随着tomcat、nginx、jboss、weblogic、websphere之类的特别配置过的电脑作为服务器,以快速高效的处理HTTP请求。那么,如果电脑上(如客户机)无法安装类似的服务是不是就无法处理HTTP请求了?其实这样从HTTP协议讲起:


        HTTP协议是应用层协议,在传输层是通过TCP来保持数据的可靠性、同时保证数据到达的顺序性。如此,可以知道HTTP请求实质上是一条TCP连接,再加上在其之上约定的传输协议(也就是HTTP)。这套传输协议,具体来说就是HTTP headers。而headers一般又分为两个部分:request和response,



2. ajax GET请求

        上述截图,是基于ajax采用GET提交数据(同步ajax): http://127.0.0.1:8086/index.html?name=qingdujun&age=18

<html><head><script type="text/javascript">function loadXMLDoc(){var xmlhttp;if (window.XMLHttpRequest){// code for IE7+, Firefox, Chrome, Opera, Safarixmlhttp=new XMLHttpRequest();}else{// code for IE6, IE5xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");}xmlhttp.open("GET","http://127.0.0.1:8086/index.html?name=qingdujun&age=18",false);xmlhttp.send();document.getElementById("myDiv").innerHTML=xmlhttp.responseText;}</script></head><body><h2>AJAX</h2><button type="button" onclick="loadXMLDoc()">请求数据</button><div id="myDiv"></div></body></html>

3. socket recv数据包

        服务端其实就是一个socket,recv的数据是这样的,直接打印了出来。下图中,大部分都是HTTP headers中的协议,实际需要处理的内容就是第一行。根据这些数据,采用socket实现HTTP就有思路了,只需要将第一行的GET内容进行切割,识别出需要的内容,即实现了数据的接受。那么如何发送数据呢?更简单了,只需要将HTTP headers附加到数据包中,然后通过TCP发送给浏览器即可。



4.ajax 跨域及TCP解析HTTP

        这里贪图方便,直接使用了网上现成的C#书写的服务端,具体可以阅读《C#中使用Socket实现简单Web服务器》一文。当然,进行了些许的改动,主要是针对ajax跨域请求的:Access-Control-Allow-Origin:*


附上修改后的socket实现HTTP Code,

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Net;using System.Net.Sockets;namespace socket2http{    class Program    {        static Socket m_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);              static void OnAccept(IAsyncResult ar)        {            try            {                Socket socket = ar.AsyncState as Socket;                Socket new_client = socket.EndAccept(ar);                socket.BeginAccept(new AsyncCallback(OnAccept), socket);                byte[] recv_buffer = new byte[1024 * 640];                int real_recv = new_client.Receive(recv_buffer);                                string recv_request = Encoding.UTF8.GetString(recv_buffer, 0, real_recv);                Console.WriteLine(recv_request);                Resolve(recv_request, new_client);             }            catch            {            }        }        static void Resolve(string request, Socket response)        {            string[] strs = request.Split(new string[] { "\r\n" }, StringSplitOptions.None);            if (strs.Length > 0)             {                string[] items = strs[0].Split(' ');                 Dictionary<string, string> param = new Dictionary<string, string>();                if (strs.Contains(""))                {                    string post_data = strs[strs.Length - 1];                     if (post_data != "")                    {                        string[] post_datas = post_data.Split('&');                        foreach (string s in post_datas)                        {                            param.Add(s.Split('=')[0], s.Split('=')[1]);                        }                    }                }                Route(items[1], param, response);            }        }        public static void HomePage(Socket response)        {            string statusline = "HTTP/1.1 200 OK\r\n";            byte[] statusline_to_bytes = Encoding.UTF8.GetBytes(statusline);            string content ="abcdef";            byte[] content_to_bytes = Encoding.UTF8.GetBytes(content);            string header = string.Format("Access-Control-Allow-Origin:*\r\nContent-Type:text/html;charset=UTF-8\r\nContent-Length:{0}\r\n", content_to_bytes.Length);            byte[] header_to_bytes = Encoding.UTF8.GetBytes(header);            response.Send(statusline_to_bytes);            response.Send(header_to_bytes);             response.Send(new byte[] { (byte)'\r', (byte)'\n' });             response.Send(content_to_bytes);              response.Close();        }        static void Route(string path, Dictionary<string, string> param, Socket response)        {            HomePage(response);            return;            if (path.EndsWith("index.html") || path.EndsWith("/"))             {            }        }        static void Main(string[] args)        {            m_socket.Bind(new IPEndPoint(IPAddress.Any, 8086));            m_socket.Listen(100);            m_socket.BeginAccept(new AsyncCallback(OnAccept), m_socket);            Console.Read();        }    }}

5.POST的缺陷

        另外,《关于http post两阶段提交的一些问题》一文中提到:“有不少浏览器厂商对于POST的提交采用两阶段发送数据,特别是IE系统列的XMLHttpRequest对象,所以在IE浏览器上用AJAX提交POST的数据,是按两个阶段,第一步先发送header数据,第二步再发送body部分。如果我们用winshark抓包会看到两连次结过程。而对于firefox浏览器,则采用一次连接,这也是原来HTTP协议的“本意”,http协议本身不保存任何状态信息,一次请求一次应答。对于TCP事务而言,通讯次数越多可靠性越低,在一次连结中传输完需要的消息是最可靠的,但是却有很多浏览器厂商不愿意遵守这个原则,它们的理由也很搞笑:假如网络环境不好,网络延迟、丢包的时候,服务端会等待(延迟时),客户端重发POST的DATA数据到服务单,来确保本次请求的完整性。


所以,鉴于上述基础上,ajax请求能使用GET方式的坚决不用POST。


@qingdujun

2017-7-23 in Xi'An

原创粉丝点击