通过抓包分析gPRC协议
前言
gRPC 是一个高性能、开源和通用的 RPC 框架,面向移动和 HTTP/2 设计。目前提供 C、Java 和 Go 语言版本,分别是:grpc, grpc-java, grpc-go. 其中 C 版本支持 C, C++, Node.js, Python, Ruby, Objective-C, PHP 和 C# 支持。
gRPC 基于 HTTP/2 标准设计,带来诸如双向流、流控、头部压缩、单 TCP 连接上的多复用请求等特。这些特性使得其在移动设备上表现更好,更省电和节省空间占用。
正文
gRPC底层使用protobuf作为传输协议,wireshark支持对protobuf的解析,不过我们要提前做一些配置。
路径那里选择一个路径,可以把所有的proto文件都放在这里,wireshark可以自动扫描。
另外还有一点,我用来演示的demo,是gRPC的官方示例helloworld程序,其中server使用的端口是50051,为了避免数据太多,可以把端口过滤也加下。
设置好我们就可以开始抓包了,分别启动helloword的server和client,代码的地址是:
https://github.com/grpc/grpc-go/tree/master/examples
我们看下抓到的包
50051是服务端代码指定的接口,63281客户端随机生成的端口。我们可以看到一次gRPC调用会有多次TCP的通讯,不过这个TCP级别的包没那么容易看懂,前面介绍过gRPC是通过HTTP/2 协议通讯的,我们可以把这个抓的包转为HTTP/2再分析。如下图所示。
现在可以看到HTTP2和GRPC了,我们直接用http2关键字再过滤下,这样可以直接看http2相关的协议。
从上面这个图,我们基本上对一次gRPC通讯有个大概的轮廓了:
Magic->Settings(四个)->Headers->Data->Window-update,Ping->Ping->Headers,Data->Window_update,ping->Ping
上面这些东东,在http2里是不同的帧,比如magic帧,settings帧等。
先来看看magic帧,
magic帧的内容是固定的,如下:
HTTP/2.0\r\n\r\nSM\r\n\r\n
Magic 帧是http2用来建立连接的,之后紧跟着 SETTINGS 帧。settings帧如下:
settings帧也算是http2连接的一部分,四次交互,用来传递通讯参数。第一次settings是客户端发给服务端的,服务端回一个settings,带上参数mmax frame size:16384,这是告诉客户端,服务端愿意接受的包体大小为 16364 个字节。然后客户端和服务端各回一个settings进行确认。
接下来是headers帧,如下图:
可以看到,headers帧里面包含的数据很多,包括
- method(POST)
- scheme(http)
- path(/helloword.Greeter/SayHello)
- content-type(application/grpc)
等。
这个帧是客户端请求的请求头部分。
然后是data帧,如下图:
data帧是客户端向服务端发送请求携带的数据,我们可以看到它具体发送的数据是world
这个字符串。另外可以看到gRPC 数据由 gRPC 包头(5 字节)+gRPC 包体(7 字节)组成,gRPC 包头的压缩标志为 Not Compressed(未压缩)。
然后是一个window_update帧和ping帧,
window_update 帧主要用于流量控制,你看上图有个字段叫Window Size Increment,表示流量窗口增量,这个是跟客户端约定的发送窗口大小。
ping帧,客户端向服务端发送ping帧,服务端回复pong,目的是探活。
接下来是服务端回复 HEADERS 帧+DATA 帧(gRPC)+HEADERS 帧(终止流)
headers帧里包含http响应码(200),data包含发送的数据信息,最后的headers是终止通讯的。
最后还有一个window_update帧和两个ping帧,前面已经讲过了。
最后用一个流图来总结下gRPC中http2的通讯流程。
总结
本文通过抓包分析了gRPC通讯的过程,gRPC是基于HTTP2进行网络传输的,所以主要是基于http2的帧进行分析的。希望对你有所帮助。