• TCP、消息分包和协议设计


    TCP是一种流式协议

    TCP是一种面向连接的、可靠的、基于字节流的传输层通信协议。

    流式协议的特点是什么?就像流水连续不断那样,消息之间没有边界。例如send了3条消息(这里的“消息”是指应用层的一个完整的协议包),分别是100字节、50字节、80字节,recv时可能收到的是230字节,就是说一次recv收到了3条消息,需要应用逻辑自己对recv到的数据进行分析,得出完整的消息。能一次recv到多个消息,也可能一次recv到一个半消息或半个消息,都是有可能的,这就是流式协议的特点。有的文章讲的粘包也是这个概念。

    消息分包

    既然TCP是一种流式协议,需要应用层自己来分析出完整的消息,那有哪些方式来确定一个完整消息呢?这个就是应用层通讯协议设计的工作了。

    先看看最常见的HTTP协议是如何来分包的。HTTP协议是一种文本协议(非二进制协议),用 来分割消息头和消息体,HTTP请求的消息头中有Content-Length来告知消息体有多大,如果没有该字段就表示无消息体,GET请求大多是这样。HTTP响应的消息头中,或者有Content-Length,或者有Transfer-Encoding: chunked告知以chunk模式分析消息体。

    HTTP请求信息由3部分组成:

    1、请求方法(GET/POST)、URI、协议/版本

    2、请求头(Request Header)

    3、请求正文,请求正文和请求头要有空行。这个空行必须存在,说明结束请求头传输,开始传输正文请求。

    HTTP请求

    GET/HTTP/1.1 [请求头] Accept:image/gif.image/jpeg,*/* Accept-Language:zh-cn Connection:Keep-Alive Host:localhost User-Agent:Mozila/4.0(compatible;MSIE5.01;Window NT5.0) Accept-Encoding:gzip,deflate [请求正文] username=ring&password=1234

    http响应格式

    HTTP应答与HTTP请求相似,HTTP响应也由3个部分构成,分别是:

    1、状态行

    2、响应头(Response Header)

    3、响应正文

    HTTP响应1:Conetent-Length

    HTTP/1.1 200 OK
    SERVER: name
    DATE: Fri, 22 Dec 2017 10:50:38 GMT
    Content-Type: image/gif 
    Content-Length: 43
    Last-Modified: Mon, 1 Dec 2017 13:23:42 GMT
    Connection: keep-alive
    Expires: Fri, 22 Dec 2017 10:50:38 GMT
    Cache-Control: max-age=0
    Accept-Ranges: bytes

    GIF89a二进制数据




    HTTP响应2:Transfer-Encoding

    HTTP/1.1 200 OK
    DATE: Fri, 22 Dec 2017 10:55:50 GMT
    Content-Type: text/html
    Transfer-Encoding: chunked
    Connection: keep-alive
    Expires: Fri, 22 Dec 2017 10:55:50 GMT
    Cache-Control: max-age=7200
    Content-Encoding: gzip

    91a
    具体的数据
    0

    HTTP用 来分割消息头和消息体,这种用特定字符/字符串来分割或分包的方式,还有不少协议用到。例如FTP/SMTP/POP3都是用 来作为一个命令结束的标志。这种消息分包的方式,需要应用层去扫描已recv到的数据,性能上还不够高效,代码不严谨的还容易被攻击。在需要自定义协议的项目中,不少选择用二进制协议,解析高效,安全性更好些。

    最简单的二进制协议分包方式是消息的头4个字节表示消息的总长度。这种方式还需要对最大消息长度做个限制,例如64K或1024K大小,避免超大数据包对接收方缓冲区的破坏。更进一步的,可以加入简单校验方法。例如消息头1个字节固定式0x2,消息的最后1个字节固定式0x3,消息总长度放在第2~5字节。这样收到完整消息后,如果头尾不是0x2和0x3,就直接异常处理。

    协议设计

    消息分包是协议设计的一个工作,协议设计的话题还不少,这里以HTTP协议为例,简要的说说里面设计的点,自己设计的协议也可以对照着有选择的使用,原理是共通的。

    由消息头+消息体组成:空行分割HTTP head和body,HTTP头的每一行以 结尾,空行就是
    消息分包:如上所述,HTTP用Content-Length和Transfer-Encodeing来分包
    消息压缩:请求中有Accept-Encoding字段,响应中用Content-Encoding字段表明压缩方式,一般采用gzip压缩
    消息加密:https  (SSL: Secure Socket Layer)
    消息ID:URL就是消息ID
    响应的状态码:第一个数字定义了响应的类别。
        1xx:指示信息--表示请求已接收,继续处理
        2xx:成功--表示请求已被成功接收、理解、接受
        3xx:重定向--要完成请求必须进行更进一步的操作
        4xx:客户端错误--请求有语法错误或请求无法实现
        5xx:服务器端错误--服务器未能实现合法的请求
    协议版本号: HTTP/1.1中的1.1就是HTTP 1.1版本
    长连接:请求中Connection: keep-alive表示希望服务器保持连接,减少TCP连接的开销
    字符集: Content-Type字段表明了字符集,例如: Content-Type: text/html; charset=gb2312
    字符转义:URL中的参数需要做URL转义处理,例如http://xx.com/do?name=t%2F%3F%23%3Daa表示name为t/?#=aa

    在我们自己设计协议时,可以有选择的使用,如果消息比较大,可以采用支持压缩;如果要兼容多个版本的协议,那版本号必不可少。如果采用二进制协议,字符集和字符穿衣的用处不大。

  • 相关阅读:
    Ruby--Array
    SQL--查询相同字段的数据
    Ruby--CSV
    Ruby--String
    Git常用命令
    JS Date函数操作
    FTP命令
    Rails--%w用法[转]
    Rails--export csv
    Mysql远程访问
  • 原文地址:https://www.cnblogs.com/ring1992/p/8085157.html
Copyright © 2020-2023  润新知