跳至侧栏

[原创]前后端交互的方式整理

【原文链接】:https://blog.tecchen.xyz ,博文同步发布到博客园。
由于精力有限,对文章的更新可能不能及时同步,请点击上面的原文链接访问最新内容。
欢迎访问我的个人网站:https://www.tecchen.xyz

前言

本来我只是想整理下前后端如何传输数据这种交互过程,大概流程如下:

  • 前台使用ajax通过get/post等方式提交数据到后端
  • 后端如何获取参数
  • 经过业务处理后,返回前端对应的响应数据
  • 前端接受到响应后,进行数据展示

但整理过程中,发现更多的相关知识也需要一起学习整理下,就展开了下面这个博客。

出身网络工程专业,由于大学基本上没有上过课;
工作后基本上用Java进行开发,7年来一直是个弱弱的单身小程序员;
部分是摘自百度百科或者其他网站,再根据自己工作这么多年的理解,编写测试的相关代码;
各种协议标准及报文格式等也是经过理论及实践的验证,三两句的文字不能诠释其本质;
种种前提使得本文大体方向应该还是正确的,内容并不精准,仅供学习理解用。
具体章节如需详细展开,请留言,补充后,补充后持续更新。

TCP、HTTP协议

TCP(Transmission Control Protocol 传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。

  • 连接建立
    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客户端和服务器端成功地建立连接,可以开始传输数据了。
    三次握手

  • 连接终止
    建立一个连接需要三次握手,而终止一个连接要经过四次握手,这是由TCP的半关闭(half-close)造成的。具体过程如下图所示。
    TCP连接的终止
    TCP连接的终止
    (1) 某个应用进程首先调用close,称该端执行“主动关闭”(active close)。该端的TCP于是发送一个FIN分节,表示数据发送完毕。
    (2) 接收到这个FIN的对端执行 “被动关闭”(passive close),这个FIN由TCP确认。
    注意:FIN的接收也作为一个文件结束符(end-of-file)传递给接收端应用进程,放在已排队等候该应用进程接收的任何其他数据之后,因为,FIN的接收意味着接收端应用进程在相应连接上再无额外数据可接收。
    (3) 一段时间后,接收到这个文件结束符的应用进程将调用close关闭它的套接字。这导致它的TCP也发送一个FIN。
    (4) 接收这个最终FIN的原发送端TCP(即执行主动关闭的那一端)确认这个FIN。
    既然每个方向都需要一个FIN和一个ACK,因此通常需要4个分节。
    四次挥手

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 – 客户端 ↔ 服务端 (未来趋势,双工通信)
posted @ 2018-12-21 14:25  Candyメ奶糖  阅读(2023)  评论(0编辑  收藏  举报