TCP/IP协议学习(五) 基于C# Socket的Web服务器---动态通讯实现

目录  

(1).基于Ajax的前端实现

(2).Web服务器后端处理

  一个完整的web服务器,不仅需要满足用户端对于图片、文档等资源的需求;还能够对于用户端的动态请求,返回指定程序生成的数据。支持动态请求处理是web服务器的必要组成部分,现有比较成熟的前端动态技术有CGI,ASP/ASP.net, PHP,原生javascript实现的Ajax技术以及基于HTML5的webSocket通讯,它们每一项都涉及很多相关知识,不过归结到核心都是前后端的数据交互,特别是对于后端来说并没有太大区别。作为动态通讯,实现就不仅仅涉及后端的数据处理,前端也要负责相当的数据操作,因此本篇将分前端web网页和后端服务器两部分来实现整个过程,其中前端通过Ajax技术,后端基于C#来实现。

(1).基于Ajax的前端实现

Ajax全称异步JavaScript和XML,是一种创建交互式网页应用的网页开发技术,通过在后台与服务器进行少量数据交换,Ajax 可以使网页实现异步更新。Ajax的实现包含三个步骤:

1.创建XmlHttpRequest对象

2.绑定事件回调,判定事件回调,设置首部,发送请求(确定URL, 参数,异步方式等)

3.等待数据返回,回调函数执行。

其中XmlHttpRequest对象是Ajax实现的基础,几乎所有的现代浏览器都内建XMLHttpRequest对象,不过为了满足老版本浏览器如IE5,IE6的兼容性需求,对象创建的代码具体如下:

    //创建XMLhttpRequest对象并返回
    GetAjaxReq: function(){
        var s = ["XMLHttpRequest", "ActiveXObject(‘Microsoft.XMLHTTP‘)"], req;

        if(!"1"[0]){
                s[0] = location.protocol === "file:"? "!":s[0];
            }

        for(var i=0, axo; axo = s[i++]; ){
           try{           if(req = eval("new " + axo))    //eval 执行内部程序,并返回
                   return req;
              }catch(e){}
     }}            

  上述过程实现了XmlHttpRequest对象的创建,下一步就是Ajax通讯的实现了,不过在进行对象操作之前,要先熟悉XmlHttpReques对象的方法和属性,知己知彼,方能百战不殆,这里提供从相关参考资料上查找的信息,希望对大家有帮助。

  XmlHttpRequest属性:

  

     XmlHttpRequest方法:
     

 了解了上述属性方法,就可以比较清晰的了解ajax实现过程,包含http请求的创建,http头的添加,异步模式设置,回调函数确定以及最后的前端数据发送,当然为了方便整个Ajax流程代码的复用,这里编写一个简单的框架$,将ajax的实现等都放在IIFE中,如下: 

   //前端ajax实现主体,其中obj为用户提交的相关参数  ajax:function(obj){                                    
            var HttpReq = $.GetAjaxReq();                      //获得XmlHttpRequest对象
            $.mix(obj, $.ajaxSettings, false);                 //非覆写模式下多对象合并,用于发送对象初始化
            var ele = $.GetElement(obj.object);        

            HttpReq.open(obj.type, obj.url, obj.async);        //创建一个新的HTTP请求
            HttpReq.setRequestHeader(‘If-Modified-Since‘, ‘0‘);//添加http头 If-Modified-Since:0
            if(!obj.async)                                     //判断是否为异步模式,同步模式是timeout赋值会出错
                HttpReq.timeout = obj.timeout;
            HttpReq.onreadystatechange =  function(){          //Ajax执行后回调函数
                $.ajaxCallback(ele, obj, HttpReq);
            };

            //因为IE中将数组直接发送会不识别(火狐会将数组直接转成字符串发送),考虑到兼容性, 如果是数组,就转成字符串发送
            if({}.toString.apply(obj.data) === ‘[object Array]‘){
                HttpReq.send(obj.data.join(‘ ‘));
            }else{
                HttpReq.send(obj.data);
              }
           }, 

当请求被发送到服务器后,需要执行一些基于响应的任务,每当 readyState 改变时,就会触发 onreadystatechange 事件。其中readyState 属性存有 XMLHttpRequest 的状态信息,同时responseText将响应信息作为字符串返回。

因此回调函数的实现就比较简单,主要包含响应状态的判断,http连接状态的判断以及相关数据的获取处理。

ajaxCallback:function(ele, obj, req){
     if(req.readyState == 4){           //XmlHttpRequest状态变化判断
        if(req.status == 200){           //Http状态判断     //判断是否为函数,进行后续处理
        if(Object.prototype.toString.call(obj.success) ===  ‘[object Function]‘){
           obj.success(ele, req.responseText);
          }
        }
        else{
              alert("AJAX Update Data Failed!");
             }
         }
 },

实现了上述Ajax框架后,在html文档里添加通过调用Ajax接口,就可以完成一次Ajax通讯。

    function UploadText(){
        var t_ele = document.getElementById("TEXT");
        $.ajax({
            object: ‘#trans_f‘,    
            type:‘POST‘,           //ajax提交的方法
            url :‘html/AspApply.asp‘,       
            async: true,           //异步提交
            timeout: 5000,       
            data: t_ele.value,
            success:function(ele, msg){
                ele.innerHTML = msg;
            }
        });
    }

    function RefreshText(){
            $.ajax({
                object: ‘#frame‘,
                type:‘GET‘,
                url :‘html/AspGet.asp‘,
                async: true,
                timeout: 5000,
                success:function(ele, msg){
                    ele.innerHTML = msg;
                }
        });
    }

    RefreshText();

到此为止,Ajax通讯的前端部分实现已经全部完成,详细实现请参考博文后的链接文档。下面就进入本篇的正题,Web服务器对前端动态数据的处理及数据回复的实现。

(2).Web服务器后端处理

  Web服务器动态通讯的设计主要包括以下三个方面:

  1. 前端提交数据的获得(一个或多个数据包的读取)。

2. 数据分析,处理和保存。

3. 后端动作及相关数据回复反馈。

  对于一个完整的Web服务器并不在意数据来自AJAX、CGI、ASP还是Websocket,它只会对后端已经实现的处理架构进行正确反馈,如果没有实现,需要返回指定的错误代码,用于告知客户端。在进行服务器实现之前,我们要明白一点:前端的提交的数据虽然在传输过程上可能会分片或者加密等,但提交到后端服务器处理时,一定是完全透明且确定的,这就决定了我们可以通过分析后端服务器接收到的数据,来进行解析处理。这也是后端处理的基础,如果看过之前关于嵌入式在线升级实现的文章,那么本篇的解析部分也类似,我们需要在原有的静态服务器处理的基础上添加动态处理部分,完成整个服务器的实现。下面我就以上面编写的前端通讯数据包为例,阐述如何数据的分析和处理。

  Ajax-GET方法提交的数据如下:

  

  通过分析发现,Get的实现与静态资源的获取过程类似,因此我们的处理也与静态资源的获取差不多,检索方法匹配,返回指定的数据。不过为了和静态处理区分,我们定义专门的函数dynamic_process来进行动态数据处理。

     private static void dynamic_process(HTTPServer.HttpProcess HttpProcess, string str)
     {
            string send_str = "";
            if (HttpProcess.updata_status == false)
            {
                //解析ajax_methon处理方法 example: /html/AspGet.asp -> /html/aspget.asp -> aspget
                int PEnd = HttpProcess.hpr.url.IndexOf(".");
                int pStart = HttpProcess.hpr.url.LastIndexOf("/");
                HttpProcess.hpr.dynamic_method = HttpProcess.hpr.url.Substring(pStart + 1, PEnd - pStart - 1);          //......
            }       //......
               //get模式为false,直接执行
            if (HttpProcess.updata_status == false)
            {
                switch (HttpProcess.hpr.dynamic_method)
                {
                    case "aspapply":
                        send_str = create_correct_head(HttpProcess, System.Text.Encoding.UTF8.GetBytes(str).Length);
                        send_str += str;
                        break;
                    case "aspget":
                        send_str = create_correct_head(HttpProcess, HTTPServer.setup.Length);
                        send_str += HTTPServer.setup;
                        break;
                    default:
                        break;
                }                   //处理后数据返回
                byte[] send_byte = new byte[send_str.Length];
                send_byte = Encoding.UTF8.GetBytes(send_str);
                HttpProcess.bs = send_byte;
            }
        }

  对于get方法,我们将指定请求的数据处理完后以UTF-8的格式回复,前端异步等待后就可以接收到正确的数据。对于POST方法的处理,就是服务器动态通讯实现的核心,与在线升级时的实现类似,重点检索的有

  1.首字段

   包含url,http方法,以及对应的dynamic方法

2.Content-Length

   提交的数据长度

3.Data

   提交的数据

  

不过考虑到TCP包分片的情况,还要在http层添加循环,实现多数据包的接收处理,才能满足动态通讯的需求。   1. 多tcp分片的接收.

  //考虑到Post提交时长度较长时tcp层分片发送,需要等待所有包发送完在处理
  do
  {
     //获得当前Socket连接传输的数据,并转换为utf-8格式
     length = CurrentSocket.Receive(recvBytes, recvBytes.Length, 0);
     recvStr = Encoding.UTF8.GetString(recvBytes, 0, length);

     Console.WriteLine(recvStr);

     //http引擎处理,返回获得数据
     http_engine(recvStr, length, hpc);
  }while(hpc.updata_status);

    2.tcp首个数据包处理, 解析Ajax方法,解析获得接收数据长度,接收数据长度统计,接收状态判断,发送数据生成等    

       //ajax动态数据处理
        private static void dynamic_process(HTTPServer.HttpProcess HttpProcess, string str)
        {
            string send_str = "";
            if (HttpProcess.updata_status == false)
            {
                //解析ajax_methon处理方法
                int PEnd = HttpProcess.hpr.url.IndexOf(".");
                int pStart = HttpProcess.hpr.url.LastIndexOf("/");
                HttpProcess.hpr.dynamic_method = HttpProcess.hpr.url.Substring(pStart + 1, PEnd - pStart - 1);

                if (HttpProcess.hpr.http_method == "post")
                {
                    //解析Content-Length 获得接收数据正文总长度
                    PEnd = str.IndexOf("Content-Length:");
                    str = str.Substring(PEnd);
                    PEnd = str.IndexOf("\r\n");
                    string str_len = str.Substring(16, PEnd - 16);  //获得接收数据总长度!
                    HttpProcess.total_len = int.Parse(str_len);
                    PEnd = str.IndexOf("\r\n\r\n");
                    HttpProcess.updata_status = true;

                    //右移4位,获得正文首字符位置 \r\n\r\n
                    str = str.Substring(PEnd + 4);
                    //统计接收数据长度
                    //对于可能的中文输入,直接用str.length获得的长度与Content-Length不符合
                    //因此取长度用System.Text.Encoding.UTF8.GetBytes(str).Length;中英文获得的长度都正确
                    HttpProcess.recv_len += System.Text.Encoding.UTF8.GetBytes(str).Length;
                }
            }
            else
            {
                HttpProcess.recv_len += System.Text.Encoding.UTF8.GetBytes(str).Length;
            }
                  //接收数据长度和实际长度相同时,表示接收完毕,可以处理
            if (HttpProcess.recv_len == HttpProcess.total_len)
            {
                HttpProcess.updata_status = false;
            }

            if (HttpProcess.updata_status == false)
            {
                switch (HttpProcess.hpr.dynamic_method)
                {
                    case "aspapply":

              //Post方法,将获得数据返回给客户端
                        send_str = create_correct_head(HttpProcess, System.Text.Encoding.UTF8.GetBytes(str).Length);  
                        send_str += str;
                        break;
                    case "aspget":
                        send_str = create_correct_head(HttpProcess, HTTPServer.setup.Length);  //get方法,将服务器内部数据提交返回
                        send_str += HTTPServer.setup;
                        break;
                    default:
                        break;
                }
                byte[] send_byte = new byte[send_str.Length];
                send_byte = Encoding.UTF8.GetBytes(send_str);

                Console.WriteLine(send_str);
                HttpProcess.bs = send_byte;
            }
        }

实验效果,直接刷新网页:

Apply提交数据后:

  到此为止,一个简单的基于C#的web服务器全部功能就实现了,它包含静态网页,图片以及js,css文件的读取和基于Ajax的动态通讯实现,如果有一定的socket通讯知识基础以及对前后端知识的了解,整个实现过程并不复杂,不过相对于正常工作的服务器,本例中的实现只能说是简单的框架,需要完善很多细节和功能。

  具体代码参考:http://files.cnblogs.com/files/zc110747/webserver-2.zip

时间: 2024-08-25 07:28:31

TCP/IP协议学习(五) 基于C# Socket的Web服务器---动态通讯实现的相关文章

TCP/IP协议学习(四) 基于C# Socket的Web服务器---静态资源处理

目录 1. C# Socket通讯 2. HTTP 解析引擎 3. 资源读取和返回 4. 服务器测试和代码下载 Web服务器是Web资源的宿主,它需要处理用户端浏览器的请求,并指定对应的Web资源返回给用户,这些资源不仅包括HTML文件,JS脚本,JPG图片等,还包括由软件生成的动态内容.为了满足上述需求,一个完整的Web服务器工作流程: 1) 服务器获得浏览器通过TCP/IP连接向服务器发送的http请求数据包. 2) HTTP请求经过Web服务器的HTTP解析引擎分析得出请求方法.资源地址等

tcp/ip协议学习 第四章 ARP:地址解析协议

派猴子来的救兵 关于ARP的RFC文档在此! ARP干嘛的 曾经有段时间, 六七年前了吧. 本科的时候, 流行了一阵子ARP病毒攻击, 导致整个局域网都不能上网了. 当时只听说这个东西防不住, 只要有一个人中毒, 就导致所有人上不了网. 现在也终于知道这是怎么回事了, 也能手工让某个同学上不了网了, 咳咳. 大家应该也都知道ARP是干嘛的, 我再啰嗦一下.. 比如我访问了百度, 百度回了包给我, 百度只知道我的IP是什么,不知道我的MAC地址. 这个包到网关的时候, IP这一层再把数据交给下一层

tcp/ip协议学习笔记一

一. 简述 以前在学校学习计算机网络的时候学习多是网络7层模型OSI,了解了一些基本的计算机网络概念和协议通信格式,但是一直没弄明白其中的原理,包括各层之间的关系,应用,还有一些常见的令牌环网到底是什么东西,这个OSI它和 TCP/IP协议簇到底是什么关系,为什么有很多协议一样?ip指令格式是什么样的?为什么老是忘记,记不住?3次握手,4次握手协议是什么?ack,syn等等说不清道不明字段是指什么?为什么以太网网卡通信速率有100mbps,两台主机之间的通信却只有不到120kpbs?...所有的

tcp/ip 协议学习笔记   章一

一.tcp/ip包含: tcp/ip协议集包含四层:链路层.网络层.传输层.应用层 二.协议举例: 应用层: http.telnet.ssh ....传输层:TCP.UDP 网络层 :IP,ICMP,IGMP 链路层:ARP ,RARP 注:不是所有的程序都有这四层.有些基于IP,但是非tcp and 非UDP  eg:ping,某一些动态路由协议 三:IP地址分类 略 四:DNS 略 五:封装 数据封装是由tcp/ip协议栈完成的.不同的系统和主机,要遵行tcp/ip协议的标准 ,才能相互通信

tcp/ip协议学习 第四章 ICMP:Internet控制报文协议

派猴子来的救兵 关于ICMP的RFC文档在此! 干嘛的 在我看来, ICMP协议主要就是为了互相传递/查询一些基本信息, 大部分是传递一些错误信息. 比如A发送UDP信息到B的10000端口, 但B的10000端口并没有开放, 就会回一个ICMP包给A, 告诉A10000端口未开放. 基本的查询信息, 比如最常用的ping命令, 就是发送ICMP包到目的主机, 然后等待目的主机的响应(响应也是ICMP包). 协议 协议定义的非常简单. ICMP在IP层上面一层. 前面是20个字节的IP头, 然后

TCP/IP协议学习

计算机网路学得不好,首先先放个OSI七层网络模型吧 在协议的控制下,上层对下层进行调用,下层对上层进行服务, 上下层间用交换原语交换信息.这样可以提高传输速率,并且保证数据安全,所以说其实每一层都有存在的必要 但是现在互联网上大家都有TCP/IP协议,可以说是某种黑话,他封装了前三层 每一层也有不同的协议 在网络通信的过程中,将发出数据的主机称为源主机,接收数据的主机称为目的主机.当源主机发出数据时,数据在源主机中从上层向下层传送.源主机中的应用进程先将数据交给应用层,应用层加上必要的控制信息就

基于C# Socket的Web服务器---静态资源处理

Web服务器是Web资源的宿主,它需要处理用户端浏览器的请求,并指定对应的Web资源返回给用户,这些资源不仅包括HTML文件,JS脚本,JPG图片等,还包括由软件生成的动态内容.为了满足上述需求,一个完整的Web服务器工作流程: 1)   服务器获得浏览器通过TCP/IP连接向服务器发送的http请求数据包. 2)   HTTP请求经过Web服务器的HTTP解析引擎分析得出请求方法.资源地址等信息,然后开始处理. 3)   对于静态请求,则在服务器上查询请求url路径下文件,并返回(如果未找到则

tcp/ip协议学习 第三章 IP协议

派猴子来的救兵 关于IP的RFC文档在此! IP的头文件还是先贴一下, 总是记不住. 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |Version| IHL |Type of Service| Total Length | +-+-+-+-+-+-+-+-+-+-+-

TCP/IP协议简介(五) 之 应用层

应用层协议 在传输层之上,便是应用层.传输层的 UDP 报文和 TCP 报文段的数据部分就是应用层交付的数据. 不同类型的网络应用有不同的通信规则,因此应用层协议是多种多样的,比如 DNS.FTP.Telnet.SMTP.HTTP.RIP.NFS 等协议都是用于解决其各自的一类问题. 本节实验,介绍 DNS.FTP.HTTP 三个常用的应用层协议. 一.DNS DNS (Domain Name Service 域名服务) 协议基于 UDP,使用端口号 53. 由数字组成的 IP 地址很难记忆,所