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

  相關資料參考:

  1. 司徒正美 《JavaScript 框架設計》第13章 數據交互模塊

      2. xingoo 【前端開發系列】—— 別說你不會Ajax


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM