【原文連接】:https://blog.tecchen.xyz ,博文同步發佈到博客園。
因爲精力有限,對文章的更新可能不能及時同步,請點擊上面的原文連接訪問最新內容。
歡迎訪問個人我的網站:https://www.tecchen.xyz 。php
原本我只是想整理下先後端如何傳輸數據這種交互過程,大概流程以下:css
但整理過程當中,發現更多的相關知識也須要一塊兒學習整理下,就展開了下面這個博客。html
出身網絡工程專業,因爲大學基本上沒有上過課;
工做後基本上用Java進行開發,7年來一直是個弱弱的單身小程序員;
部分是摘自百度百科或者其餘網站,再根據本身工做這麼多年的理解,編寫測試的相關代碼;
各類協議標準及報文格式等也是通過理論及實踐的驗證,三兩句的文字不能詮釋其本質;
種種前提使得本文大致方向應該仍是正確的,內容並不精準,僅供學習理解用。
具體章節如需詳細展開,請留言,補充後,補充後持續更新。前端
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
(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、UDP 的區別,三次握手、四次揮手
HTTP(HyperText Transfer Protocol,超文本傳輸協議)是互聯網上應用最爲普遍的一種網絡協議。
一般,由HTTP客戶端發起一個請求,創建一個到服務器指定端口(默認是80端口)的TCP鏈接。HTTP服務器則在那個端口監聽客戶端發送過來的請求。一旦收到請求,服務器(向客戶端)發回一個狀態行,好比"HTTP/1.1 200 OK",和(響應的)消息,消息的消息體多是請求的文件、錯誤消息、或者其它一些信息。
經過HTTP或者HTTPS協議請求的資源由統一資源標示符(Uniform Resource Identifiers)(或者,更準確一些,URLs)來標識。
HTTP協議採用了請求/響應模型。客戶端向服務器發送一個請求,請求頭包含請求的方法、URL、協議版本、以及包含請求修飾符、客戶信息和內容的相似於MIME的消息結構。服務器以一個狀態行做爲響應,響應的內容包括消息協議的版本,成功或者錯誤編碼加上包含服務器信息、實體元信息以及可能的實體內容。
統一資源定位符(Uniform Resource Locator,URL)是對能夠從互聯網上獲得的資源的位置和訪問方法的一種簡潔的表示,是互聯網上標準資源的地址。互聯網上的每一個文件都有一個惟一的URL,它包含的信息指出文件的位置以及瀏覽器應該怎麼處理它。
基本URL包含模式(或稱協議)、服務器名稱(或IP地址)、路徑和文件名,如「協議://受權/路徑?查詢」。完整的、帶有受權部分的普通統一資源標誌符語法看上去以下:協議://用戶名:密碼@子域名.域名.頂級域名:端口號/目錄/文件名.文件後綴?參數=值#標誌
REST全稱是Representational State Transfer。點擊瞭解更多RESTful 架構詳解。
HTTP目前分爲如下版本,其中目前最流行的是HTTP/1.1版本。
發現網友寫的更詳細,就不浪費你們的時間了。Http協議報文格式
能夠經過GET/POST等方式將變量、對象、數組、JSON、XML、file/img、base64圖片、cookies等各類形式的數據提交到後端。
源碼下載:先後端交互的方式的演示代碼
代碼示例:
/** * 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方式可使用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 |
跨域代碼示例(使用"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); }
前端:
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(); }