[原創]先後端交互的方式整理

【原文連接】:https://blog.tecchen.xyz ,博文同步發佈到博客園。
因爲精力有限,對文章的更新可能不能及時同步,請點擊上面的原文連接訪問最新內容。
歡迎訪問個人我的網站:https://www.tecchen.xyzphp

前言

原本我只是想整理下先後端如何傳輸數據這種交互過程,大概流程以下:css

  • 前臺使用ajax經過get/post等方式提交數據到後端
  • 後端如何獲取參數
  • 通過業務處理後,返回前端對應的響應數據
  • 前端接受到響應後,進行數據展現

但整理過程當中,發現更多的相關知識也須要一塊兒學習整理下,就展開了下面這個博客。html

出身網絡工程專業,因爲大學基本上沒有上過課;
工做後基本上用Java進行開發,7年來一直是個弱弱的單身小程序員;
部分是摘自百度百科或者其餘網站,再根據本身工做這麼多年的理解,編寫測試的相關代碼;
各類協議標準及報文格式等也是通過理論及實踐的驗證,三兩句的文字不能詮釋其本質;
種種前提使得本文大致方向應該仍是正確的,內容並不精準,僅供學習理解用。
具體章節如需詳細展開,請留言,補充後,補充後持續更新。前端

TCP、HTTP協議

TCP(Transmission Control Protocol 傳輸控制協議)是一種面向鏈接的、可靠的、基於字節流的傳輸層通訊協議。java

  • 鏈接創建
    TCP是因特網中的傳輸層協議,使用三次握手協議創建鏈接。當主動方發出SYN鏈接請求後,等待對方回答SYN+ACK,並最終對對方的 SYN 執行 ACK 確認。這種創建鏈接的方法能夠防止產生錯誤的鏈接,TCP使用的流量控制協議是可變大小的滑動窗口協議。
    TCP三次握手的過程以下:
    客戶端發送SYN(SEQ=x)報文給服務器端,進入SYN_SEND狀態。
    服務器端收到SYN報文,迴應一個SYN (SEQ=y)ACK(ACK=x+1)報文,進入SYN_RECV狀態。
    客戶端收到服務器端的SYN報文,迴應一個ACK(ACK=y+1)報文,進入Established狀態。
    三次握手完成,TCP客戶端和服務器端成功地創建鏈接,能夠開始傳輸數據了。
    三次握手jquery

  • 鏈接終止
    創建一個鏈接須要三次握手,而終止一個鏈接要通過四次握手,這是由TCP的半關閉(half-close)形成的。具體過程以下圖所示。
    TCP鏈接的終止
    TCP鏈接的終止

(1) 某個應用進程首先調用close,稱該端執行「主動關閉」(active close)。該端的TCP因而發送一個FIN分節,表示數據發送完畢。git

(2) 接收到這個FIN的對端執行 「被動關閉」(passive close),這個FIN由TCP確認。
注意:FIN的接收也做爲一個文件結束符(end-of-file)傳遞給接收端應用進程,放在已排隊等候該應用進程接收的任何其餘數據以後,由於,FIN的接收意味着接收端應用進程在相應鏈接上再無額外數據可接收。程序員

(3) 一段時間後,接收到這個文件結束符的應用進程將調用close關閉它的套接字。這致使它的TCP也發送一個FIN。web

(4) 接收這個最終FIN的原發送端TCP(即執行主動關閉的那一端)確認這個FIN。
既然每一個方向都須要一個FIN和一個ACK,所以一般須要4個分節。
四次揮手ajax

TCP狀態機
TCP狀態機

更多能夠學習:
五分鐘讀懂TCP 協議——TCP協議簡介
TCP協議
TCP、UDP 的區別,三次握手、四次揮手

HTTP(HyperText Transfer Protocol,超文本傳輸協議)是互聯網上應用最爲普遍的一種網絡協議。
一般,由HTTP客戶端發起一個請求,創建一個到服務器指定端口(默認是80端口)的TCP鏈接。HTTP服務器則在那個端口監聽客戶端發送過來的請求。一旦收到請求,服務器(向客戶端)發回一個狀態行,好比"HTTP/1.1 200 OK",和(響應的)消息,消息的消息體多是請求的文件、錯誤消息、或者其它一些信息。
經過HTTP或者HTTPS協議請求的資源由統一資源標示符(Uniform Resource Identifiers)(或者,更準確一些,URLs)來標識。
HTTP協議採用了請求/響應模型。客戶端向服務器發送一個請求,請求頭包含請求的方法、URL、協議版本、以及包含請求修飾符、客戶信息和內容的相似於MIME的消息結構。服務器以一個狀態行做爲響應,響應的內容包括消息協議的版本,成功或者錯誤編碼加上包含服務器信息、實體元信息以及可能的實體內容。

URL、RESTful

統一資源定位符(Uniform Resource Locator,URL)是對能夠從互聯網上獲得的資源的位置和訪問方法的一種簡潔的表示,是互聯網上標準資源的地址。互聯網上的每一個文件都有一個惟一的URL,它包含的信息指出文件的位置以及瀏覽器應該怎麼處理它。
基本URL包含模式(或稱協議)、服務器名稱(或IP地址)、路徑和文件名,如「協議://受權/路徑?查詢」。完整的、帶有受權部分的普通統一資源標誌符語法看上去以下:協議://用戶名:密碼@子域名.域名.頂級域名:端口號/目錄/文件名.文件後綴?參數=值#標誌

REST全稱是Representational State Transfer。點擊瞭解更多RESTful 架構詳解

HTTP詳解

HTTP目前分爲如下版本,其中目前最流行的是HTTP/1.1版本。

HTTP報文格式詳解

發現網友寫的更詳細,就不浪費你們的時間了。Http協議報文格式

數據交互格式

能夠經過GET/POST等方式將變量、對象、數組、JSON、XML、file/img、base64圖片、cookies等各類形式的數據提交到後端。
源碼下載:先後端交互的方式的演示代碼

GET

代碼示例:

/**
 * GET-變量
 */
@GetMapping("/testVar1")
public String testVar1(/*@RequestParam("param")*/String param) {
    return param;
}

/**
 * GET-path變量
 */
@GetMapping("/testVar2/{param}")
public String testVar2(@PathVariable/*("param")*/ String param) {
    return param;
}

/**
 * GET-對象
 */
@GetMapping("/testObject1")
public GetParam testObject1(GetParam obj) {
    return obj;
}

/**
 * GET-數組
 */
@GetMapping("/testArray1")
public Object testArray1(String[] obj) {
    return obj;
}

運行結果:



POST

POST方式可使用curl、postman、getman、eolinker等工具實現。

/**
 * POST-多個變量、對象(form提交)
 */
@PostMapping(value = "/testPostFormVar")
public String testPostVar1(@RequestParam(name = "username") String username,
                          @RequestParam(name = "password") String password) {
    return username + ":" + password;
}

/**
 * POST-JSON(ajax)
 */
@PostMapping(value = "/testPostJsonVar")
public PostParam testPostVar2(@RequestBody PostParam param) {
    return param;
}

/**
 * 通用的post接收方式
 */
@PostMapping(value = "/testPostVar")
public PostParam testPostVar(PostParam param) {
    return param;
}

運行結果:




POST方式總結:

content-type 數據格式 後臺接受方式 專用註解
application/x-www-form-urlencoded[默認] json對象 對象 @RequestParam
application/json json字符串 對象 @RequestBody
$.post() 默認application/x-www-form-urlencoded json對象 對象 @RequestParam

數據交互技術

  • 服務器渲染
    談起服務端渲染,對於動態服務而言,這個世界上跑的大多數頁面都通過服務端的數據渲染,接口->前端賦值->模板渲染。這些都是在服務器完成,在咱們查看源碼的時候,能夠看到完整的html代碼,包括每一個數據值。經常使用的php模板:Smarty,Blade,Mustache。若是使用Node.js做爲服務端的話: ejs,doT,jade等。
  • Form、Ajax、jsonp
    服務端渲染隨着單頁面應用以及Restful接口的興起,Ajax逐漸成爲目前先後端交流最爲頻繁的方式。Ajax的核心是XmlHttpRequest。咱們經過對該對象的操做來進行異步的數據請求。實際上咱們接觸到最多jQuey就有很好的封裝,好比\(.ajax,\).post等,若是用Angular的話咱們能夠用$http服務,除了這些以外,咱們可使用第三方的Ajax庫qwest等。

跨域代碼示例(使用"http://localhost:9090/jsonp.html"的頁面發起請求):
後端:

@RestController
@RequestMapping("/cdr")
public class CrossDomainRequestsController {
    /**
     * 通用的post接收方式
     */
    @RequestMapping(value = "/testCdr")
    public PostParam testPostVar(PostParam param) {
        return param;
    }
}

前端:

<!DOCTYPE html>
<html>
<head>
    <title>跨域</title>
    <meta charset="UTF-8">
    <script src="https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script>
    <script>

        $(function(){
             $("#get").click(function () {
                $.ajax({
                    type: 'get',
                    url: "http://localhost:8080/cdr/testCdr",
                    contentType: "application/x-www-form-urlencoded; charset=utf-8",
                    data:{
                        "username": "TEC-9",
                        "password": "wodemima"
                    },
                    success : function(data,status){
                        console.log("數據: \n" + JSON.stringify(data) + "\n狀態: " + status);
                    }
                });
            });

            $("#post").click(function () {
                $.ajax({
                    type: 'POST',
                    url: "http://localhost:8080/cdr/testCdr",
                    contentType: "application/x-www-form-urlencoded; charset=utf-8",
                    data:{
                        "username": "TEC-9",
                        "password": "wodemima"
                    },
                    success : function(data,status){
                        console.log("數據: \n" + JSON.stringify(data) + "\n狀態: " + status);
                    }
                });
            });
        });
    </script>
</head>
<body>
<h1></h1>
<div>
    <table>
        <thead>
        <tr>
            <td>請求地址</td>
            <td>方式</td>
        </tr>
        </thead>
        <tbody>
        <tr>
            <td><button id="get">標準的ajax-post提交驗證</button></td>
            <td>get</td>
        </tr>
        <tr>
            <td><button id="post">標準的ajax-post提交驗證</button></td>
            <td>post</td>
        </tr>
        </tbody>
    </table>
</div>
</body>
</html>

執行結果:

跨域方案一:
直接添加@CrossOrigin註解

@RestController
@RequestMapping("/cdr")
@CrossOrigin
public class CrossDomainRequestsController {

跨域方案二:
JOSNP方式

$("#testJsonp").click(function () {
    $.ajax({
        type: 'get',
        url: "http://localhost:8080/cdr/testJsonp",
        contentType: "application/json; charset=utf-8",
        dataType: "jsonp", //指定服務器返回的數據類型
        jsonpCallback: "printResult",  //指定回調函數名稱
        data:{
            "username": "TEC-9",
            "password": "wodemima"
        },
        success : function(data,status){
            // console.log("數據: \n" + JSON.stringify(data) + "\n狀態: " + status);
            console.log("success")
        }
    });
});

@RequestMapping(value = "/testJsonp")
public JSONPObject testJsonp(PostParam param, String callback) {
    return new JSONPObject(callback, param);
}
  • Comet(不研究)
    以即時通訊爲表明的web應用程序對數據的Low Latency要求,傳統的基於輪詢的方式已經沒法知足,並且也會帶來很差的用戶體驗。因而一種基於http長鏈接的「服務器推」技術便被hack出來。這種技術被命名爲Comet,這個術語由Dojo Toolkit 的項目主管Alex Russell在博文Comet: Low Latency Data for the Browser首次提出,並沿用下來。
  • SSE
    SSE(Server-Sent Event,服務端推送事件)是一種容許服務端向客戶端推送新數據的HTML5技術。與由客戶端每隔幾秒從服務端輪詢拉取新數據相比,這是一種更優的解決方案。
    示例代碼,也能夠參考如下連接:
    服務器推送消息SSE以及springmvc後臺實現例子
    SSE技術詳解:使用 HTTP 作服務端數據推送應用的技術

前端:

if (window.EventSource) {

var eventSource = new EventSource("http://localhost:9090/es/testEventSource");

//只要和服務器鏈接,就會觸發open事件
eventSource.addEventListener("open", function () {
    console.log("open和服務器創建鏈接");
});

//處理服務器響應報文中的load事件
eventSource.addEventListener("load", function (e) {
    console.log("load服務器發送給客戶端的數據爲:" + e.data);
    if(!e.data) {
        eventSource.close();
        console.log("關閉");
    }
});

//若是服務器響應報文中沒有指明事件,默認觸發message事件
eventSource.addEventListener("message", function (e) {
    console.log("message服務器發送給客戶端的數據爲:" + e.data);

});

//發生錯誤,則會觸發error事件
eventSource.addEventListener("error", function (e) {
    console.log("error服務器發送給客戶端的數據爲:" + e.data);
});

}
else {
console.log("服務器不支持EvenSource對象");
}

後端:

static Integer randomInt = 0;

@RequestMapping(value = "/testEventSource", produces = "text/event-stream")
public String testEventSource() {
    //響應報文格式爲:
    //data:Hello World
    //event:load
    //id:140312
    //換行符(/r/n)
    StringBuffer result = null;
    // open/load/message/error
    result = new StringBuffer();
    if (randomInt == 0) {
        result = new StringBuffer();
        result.append("event:open");
        result.append("\n");
        result.append("data:" + randomInt++);
        result.append("\n\n");
        return result.toString();
    }

    while (randomInt < 5) {
        result = new StringBuffer();
        result.append("event:load");
        result.append("\n");
        result.append("data:" + randomInt++);
        result.append("\n\n");
        return result.toString();
    }
    result = new StringBuffer();
    result.append("event:load");
    result.append("\n");
    result.append("data:");
    result.append("\n\n");
    return result.toString();
}
  • Websocket
    WebSocket協議是基於TCP的一種新的網絡協議。它實現了瀏覽器與服務器全雙工(full-duplex)通訊——容許服務器主動發送信息給客戶端。
    學習連接:使用 HTML5 WebSocket 構建實時 Web 應用 小結 說了那麼多簡單總結下,你們想明白幾點就好了,客戶端與服務端誰先主動,是否強調數據的實時性。 AJAX – 請求 → 響應 (頻繁使用) Comet – 請求 → 掛起 → 響應 (模擬服務端推送) Server-Sent Events – 客戶單 ← 服務端 (服務端推送) WebSockets – 客戶端 ↔ 服務端 (將來趨勢,雙工通訊)
相關文章
相關標籤/搜索