热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

理解基于TCP的应用层通信协议

TCP协议示意 TCP协议关于七层网络通信的基本原理,特别推荐这篇图文并茂的长文《TCPIP笔记–综述》TCP通信基本特征 TCP数据流特征1.消息(结构化数据)被编码成字节流写入
TCP 协议示意

 

《理解基于 TCP 的应用层通信协议》

TCP协议

关于七层网络通信的基本原理,特别推荐这篇图文并茂的长文《TCP/IP笔记 – 综述》

TCP 通信基本特征

 

《理解基于 TCP 的应用层通信协议》

TCP数据流

特征

1. 消息(结构化数据)被编码成字节流写入 TCP 通道。

2. TCP 通道不能保证字节流一定到达目的地,但能保证到达的字节流是 正确有序 的。对于发送端而言,可以不停的写入数据,当网络出问题 ACK 超时会报错,但由于缓存的存在,发送端其实不知道有多少数据到达接收端。对于接收端而言,一直等待接收数据,一旦收到数据是能确定这些数据是连续、有序的,中间不可能有数据缺失,但接收端无法知道何时能收到下一个数据包。

3. TCP 通道像一个无形的管道,这个管道的流量由全链路复杂的网络环境决定,TCP 协议会自动调节(即拥塞控制)。

4. TCP 是全双工协议,读写互不干扰。注意,如图所示,读写是两个完全不同的通道,它们完全可能走不同的物理链路。

《理解基于 TCP 的应用层通信协议》

TCP控制流程

疑问

1. 对于接收端来说,虽然能接收到正确的有序的字节流,如何界定收到的字节流构成一个完整的消息体?这就是所谓 “”粘包” 问题。

2. 对于接收端来说,一直在等待读取数据,如何判断发送端是空闲还是失联?这就是 超时问题。

基于 TCP 构建数据通信协议,首先要解决的就是上面两个问题。另外,不管是客户端还是服务端,它们都同时是发送端和接收端。从应用层面来说,我们还需要构建 请求(Request)/响应(Response) 机制,比如浏览器调用后端 API 服务需要知道结果,或者消息服务器往客户端推送消息需要知道消息是否被客户端处理。当然也有一种消息从发送端发出后是不需要知道结果的,这种消息通常称为 通知(Notification)

基于 TCP 协议构建应用层的通信协议

两个模式和三个问题

TCP 协议本质是流模式,基于它可以构建各种应用层通信协议,但其基本模式只有两种:

1. Streaming 流模式,如 HTTP/1 协议,redis 协议。

2. Multiplexing 多路复用模式,如 MongoDB 协议、常见的 RPC 协议。

随着技术的发展,也出现了这两种基本模式的混合体:

1. Streaming + Multiplexing,如基于 HTTP/1 实现的 JSON-RPC 协议。

2. Multiplexing + Streaming,如 HTTP/2,基于 HTTP/2 的 gRPC 等。

根据 TCP 协议的特征,我们应用层协议一般要解决三个问题:

1. 粘包问题,从字节流中分解出一个个独立的消息体;

2. 请求/响应机制;

3. 消息指令或类型定义,解决超时问题和实际应用的通信需求。

Streaming

最原始的 Streaming 模式就是一应一答模式。

相信不少人基于 TCP 开发网络通信时干过这种事:把一个请求数据变成字节写入 TCP,再等待对方的应答数据,收到应答后开始下一个请求。HTTP/1.0 就是这个模式的最典型。

用上面的管道示意图来理解,每次往管道放入一个数据包,然后等对方回复一个数据包,从而实现应用层需要的 请求(Request)/响应(Response) 机制。

这种模式下通信效率显然特别低,为了提升效率得开多个 TCP 通道,然而打开 TCP 通道不但有三次握手开销,还给服务器带来一定资源开销压力,特别是 Apache 那种传统的 web 服务。

既然是管道,其实是可以像流体一样不断的写入数据包,只要定义 请求(Request)/响应(Response) 的逻辑关系,这就是 Pipelining 机制,HTTP/1.1 和 redis 协议属于这种模式。

《理解基于 TCP 的应用层通信协议》

流水线请求应答模式

比如 HTTP/1.1 协议,允许客户端依次写入多个请求而无需等待应答,服务端则应该按照客户端的请求顺序依次进行响应,从而确保 请求(Request)/响应(Response) 一一对应。

HTTP/1

HTTP/1 是基于文本的协议:

1. 通过 CRLF (也就是 \r\n) 标志解决粘包问题,如果内容中有 \r\n,必须进行转义,第一行命令行、Headers 头部和实体主体各有不同的转义处理。

2. 请求/响应机制就是一应一答模式或者 Pipelining。

3. 第一行定义了丰富的消息类型,超时机制则是在浏览器或者服务端逻辑实现,协议层没有定义。

《理解基于 TCP 的应用层通信协议》

HTTP 请求协议

 

《理解基于 TCP 的应用层通信协议》

HTTP 响应协议

Redis 的 RESP 协议

RESP 也是基于文本的协议:

1. 定义了五种消息类型,分别由 +–:$* 字符开头,CRLF 结尾,其中 Bulk Strings 类型允许包含 CRLF。

2. 请求/响应机制是复杂的 Pipelining,允许客户端不断的写入请求,服务端会按照顺序响应请求。但是,根据请求命令的不同,对应响应体会有 零到无数 个。

3. 协议层定义了五种消息类型,其中的 Arrays 是结构化消息类型。对比 HTTP 协议,RESP 协议不用依赖于更上一层的 JSON、XML 协议等,就能构造出复杂的消息体。超时问题依然由客户端或服务端的逻辑实现。

《理解基于 TCP 的应用层通信协议》

RESP 协议

HTTP/1 协议和 RESP 协议可以算是我们当前使用最广泛的协议,有很多服务都是基于 RESP 协议。然而,即便应用最广,也有 Pipelining 机制,基于 Streaming 的协议依然有一个痛点:头部阻塞,也就是如果某一个请求需要消耗很长处理时间才能响应,后续响应都得排队等候,即被阻塞。

Multiplexing

这是一种解决头部阻塞问题的更高效的模式,它不在依赖于 请求(Request)/响应(Response) 的顺序处理,允许请求并发发出,请求处理完成就立即响应,其核心就是 Request ID

 

《理解基于 TCP 的应用层通信协议》

Multiplexing

MongoDB 协议

MongoDB 协议 是基于二进制的协议,协议定义的内容很丰富:

1. 一个完整消息由 Header 和 Body 组成。Header 有 16 位,定义如下,Body 的长度则在 Header 中的 messageLength 定义,编码格式则是 bson。

2. Header 中的 requestID 是请求 ID,responseTo 是响应 ID,所以其请求/响应机制是Multiplexing。

3. Header 中的 opCode 定义了 11 中消息类型:

struct MsgHeader {

  int32  messageLength; // total message size, including this

  int32  requestID; // identifier for this message

  int32  responseTo; // requestID from the original request

  int32  opCode; // request type – see table below

}

Opcode 表:

| Opcode Name | Value | Comment |

|———-|——-|————–|

| OP_REPLY | 1 | Reply to a client request. responseTo is set. |

| OP_MSG | 1000 | Generic msg command followed by a string. |

| OP_UPDATE | 2001 | Update document. |

| OP_INSERT | 2002 | Insert new document. |

| RESERVED | 2003 | Formerly used for OP_GET_BY_OID. |

| OP_QUERY | 2004 | Query a collection. |

| OP_GET_MORE | 2005 | Get more data from a query. See Cursors. |

| OP_DELETE | 2006 | Delete documents. |

| OP_KILL_CURSORS | 2007 | Notify database that the client has finished with the cursor. |

| OP_COMMAND | 2010 | Cluster internal protocol representing a command request. |

| OP_COMMANDREPLY | 2011 | Cluster internal protocol representing a reply to an OP_COMMAND. |

注意,只有 OP_QUERY 和 OP_GET_MORE 两种类型有 requestID,其它类型都没有!

所以,在 MongoDB 2.6 之前,写入、更新、删除操作等是没有响应结果的!那么如何确定写入是否成功呢?通过 getLastError 命令,这个命令是基于 OP_QUERY 的。每一个写入操作追加一个 getLastError 请求,查询上一次命令是否报错(很笨的设计有没有?相当于回退到一应一答的 Streaming 模式了)。

MongoDB 2.6 之后使用了 maxWireVersion 3 协议,扩展了数据库的 commands,可以进行各种各样的操作,可以在客户端使用 db.$command.help() 查看所有命令。而 commands 的本质就是 OP_QUERY 类型的查询,所以第三代协议相当于只使用 OP_QUERY 、OP_GET_MORE和 OP_REPLY 类型的消息,淘汰了其它类型。

TiKV 协议

TiKV 协议 是基于二进制的协议:

1. 一个完整消息由 Header 和 Body 组成。Header 有 16 位,定义如下,Body 的长度则在 Header 中的 payload_len 定义,编码格式由 protobuf 定义。

2. Header 中的 msg_id 是请求 ID,所以其请求/响应机制是 Multiplexing

3. 没有定义消息类型,消息类型由 protobuf 精确定义

struct MsgHeader {

  uint16  MSG_MAGIC; // const MSG_MAGIC: u16 = 0xdaf4;

  uint16  MSG_VERSION_V1; // const MSG_VERSION_V1: u16 = 1;

  uint32  payload_len; // Body length

  uint64  msg_id; // request ID

}

Streaming + Multiplexing

这种模式是由 JSON-RPC 2.0 specifications 出现引发的,很多人基于 HTTP 来实现 JSON-RPC 服务。

JSON-RPC 2.0

JSON-RPC 2.0 定义两类四种类型的消息,分别是:

1. Request object:

定义了 jsonrpcmethodparamsid 四种属性,当 id 存在时,则为标准的 Request,如

{“jsonrpc”:”2.0″,”method”:”subtract”,”params”: [42,23],”id”:1}

Request 需要对方进行响应;不存在时则为 Notification,如

{“jsonrpc”:”2.0″,”method”:”update”,”params”: [1,2,3,4,5]}

Notification 不需要对方响应。

2. Response object:

包括 jsonrpcresulterrorid 四种属性,id 必须存在,resulterror 只能有一个存在,当 result 存在时,则为 Success Response,如

{“jsonrpc”:”2.0″,”result”:19,”id”:1}

当 error 存在时,则为 Error Response,如

{“jsonrpc”:”2.0″,”error”:{“code”:-32601,”message”:”Method not found”},”id”:”5″}

另外也有 RESP 协议配合 JSON-RPC 2.0 实现的 RPC 框架 toa-net,主要是利用 RESP协议解决粘包问题,JSON-RPC 2.0 协议解决 Multiplexing 模式的 请求(Request)/响应(Response)

Multiplexing + Streaming

HTTP/2

HTTP/2 协议在一个 TCP 通道建立了 N 个 Stream 流通道,每个 Stream 有唯一的 ID,从而实现 Multiplexing 模式,Stream 内则与原来的 HTTP/1 一样,是 Streaming 模式。

《理解基于 TCP 的应用层通信协议》

HTTP/2


推荐阅读
  • Servlet多用户登录时HttpSession会话信息覆盖问题的解决方案
    本文讨论了在Servlet多用户登录时可能出现的HttpSession会话信息覆盖问题,并提供了解决方案。通过分析JSESSIONID的作用机制和编码方式,我们可以得出每个HttpSession对象都是通过客户端发送的唯一JSESSIONID来识别的,因此无需担心会话信息被覆盖的问题。需要注意的是,本文讨论的是多个客户端级别上的多用户登录,而非同一个浏览器级别上的多用户登录。 ... [详细]
  • 如何实现织梦DedeCms全站伪静态
    本文介绍了如何通过修改织梦DedeCms源代码来实现全站伪静态,以提高管理和SEO效果。全站伪静态可以避免重复URL的问题,同时通过使用mod_rewrite伪静态模块和.htaccess正则表达式,可以更好地适应搜索引擎的需求。文章还提到了一些相关的技术和工具,如Ubuntu、qt编程、tomcat端口、爬虫、php request根目录等。 ... [详细]
  • 这是原文链接:sendingformdata许多情况下,我们使用表单发送数据到服务器。服务器处理数据并返回响应给用户。这看起来很简单,但是 ... [详细]
  • 本文介绍了使用AJAX的POST请求实现数据修改功能的方法。通过ajax-post技术,可以实现在输入某个id后,通过ajax技术调用post.jsp修改具有该id记录的姓名的值。文章还提到了AJAX的概念和作用,以及使用async参数和open()方法的注意事项。同时强调了不推荐使用async=false的情况,并解释了JavaScript等待服务器响应的机制。 ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • WebSocket与Socket.io的理解
    WebSocketprotocol是HTML5一种新的协议。它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送 ... [详细]
  • 解决nginx启动报错epoll_wait() reported that client prematurely closed connection的方法
    本文介绍了解决nginx启动报错epoll_wait() reported that client prematurely closed connection的方法,包括检查location配置是否正确、pass_proxy是否需要加“/”等。同时,还介绍了修改nginx的error.log日志级别为debug,以便查看详细日志信息。 ... [详细]
  • mac php错误日志配置方法及错误级别修改
    本文介绍了在mac环境下配置php错误日志的方法,包括修改php.ini文件和httpd.conf文件的操作步骤。同时还介绍了如何修改错误级别,以及相应的错误级别参考链接。 ... [详细]
  • 本文介绍了Java后台Jsonp处理方法及其应用场景。首先解释了Jsonp是一个非官方的协议,它允许在服务器端通过Script tags返回至客户端,并通过javascript callback的形式实现跨域访问。然后介绍了JSON系统开发方法,它是一种面向数据结构的分析和设计方法,以活动为中心,将一连串的活动顺序组合成一个完整的工作进程。接着给出了一个客户端示例代码,使用了jQuery的ajax方法请求一个Jsonp数据。 ... [详细]
  • 搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的详细步骤
    本文详细介绍了搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的步骤,包括环境说明、相关软件下载的地址以及所需的插件下载地址。 ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 本文介绍了在Mac上搭建php环境后无法使用localhost连接mysql的问题,并通过将localhost替换为127.0.0.1或本机IP解决了该问题。文章解释了localhost和127.0.0.1的区别,指出了使用socket方式连接导致连接失败的原因。此外,还提供了相关链接供读者深入了解。 ... [详细]
  • Imtryingtofigureoutawaytogeneratetorrentfilesfromabucket,usingtheAWSSDKforGo.我正 ... [详细]
  • 网络请求模块选择——axios框架的基本使用和封装
    本文介绍了选择网络请求模块axios的原因,以及axios框架的基本使用和封装方法。包括发送并发请求的演示,全局配置的设置,创建axios实例的方法,拦截器的使用,以及如何封装和请求响应劫持等内容。 ... [详细]
author-avatar
渊博的樱桃cherry
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有