HTTP协议:socket处理ajax请求?

摘要: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, Safari
		xmlhttp=new XMLHttpRequest();
	}else{// code for IE6, IE5
		xmlhttp=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" οnclick="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

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 精致技术 设计师:CSDN官方博客 返回首页