• MongoDB数据库协议解析及C/C++代码实现


    MongoDB 是由 MongoDB Inc 开发的 NoSQL 数据库,它是无模式的。它是使用 c++ 和 javascript 设计和创建的,允许更高的连接性。

    MongoDB 是一个 NoSQL Server,其中数据存储在 BSON(二进制 JSON)文档中,每个文档本质上都建立在键值对结构上。由于 MongoDB 很容易存储无模式数据,因此使其适合捕获结构未知的数据。

    MongoDB Wire Protocol

    MongoDB Wire Protocol 是一个简单的基于套接字的请求-响应式协议。 客户端通过常规 TCP/IP 套接字与数据库服务器通信。通常,Mongo 使用 TCP 作为其传输协议。 Mongo 流量的著名 TCP 端口是 27017。

    MongoDB 将数据记录存储为 BSON 文档。 BSON 是 JSON 文档的二进制表示,尽管它包含比 JSON 更多的数据类型。


    MongoDB 文档由字段-值对组成。

    Mongo 消息类型和格式

    MongoDB 对客户端请求和数据库回复都使用 OP_MSG 操作码。

    #define OP_REPLY           1
    #define OP_MESSAGE      1000
    #define OP_UPDATE       2001
    #define OP_INSERT       2002
    #define OP_RESERVED     2003
    #define OP_QUERY        2004
    #define OP_GET_MORE     2005
    #define OP_DELETE       2006
    #define OP_KILL_CURSORS 2007
    #define OP_COMMAND      2010
    #define OP_COMMANDREPLY 2011
    #define OP_COMPRESSED   2012
    #define OP_MSG          2013
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    { OP_REPLY,  "Reply" },
    { OP_MESSAGE, "Message" },
    { OP_UPDATE,  "Update document" },
    { OP_INSERT,  "Insert document" },
    { OP_RESERVED,"Reserved" },
    { OP_QUERY,  "Query" },
    { OP_GET_MORE,  "Get More" },
    { OP_DELETE,  "Delete document" },
    { OP_KILL_CURSORS,  "Kill Cursors" },
    { OP_COMMAND,  "Command Request" },
    { OP_COMMANDREPLY,  "Command Reply" },
    { OP_COMPRESSED,  "Compressed Data" },
    { OP_MSG,  "Extensible Message Format" },
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    Mongo 标准消息头

    通常,每条消息都由一个标准消息头和请求特定的数据组成。

    消息长度:消息的总大小(以字节为单位)。 这个总数包括保存消息长度的 4 个字节。
    请求ID:唯一标识消息的客户端或数据库生成的标识符。
    响应:从客户端的消息中获取的 requestID。
    操作码:消息类型。

    Query 消息

    OP_QUERY消息用于在数据库中查询集合中的文档。
    messageLength: 报文总长度,使用 4 个字节表示。
    requestID : 由客户端或服务器生成的报文唯一标识。 例如客户端请求 (OP_QUERY 和 OP_GET_MORE), 将返回 OP_REPLY 报文给 responseTo 字段表示的请求. 客户端可以使用 requestID 和 responseTo 字段关联查询响应到原始请求上。
    responseTo : 服务端响应使用此字段关联到 requestID 对应的查询请求上。
    opCode: 报文类型。
    fullCollectionName: 完整的集合名称;即命名空间。
    number To Skip:要跳过的文件数
    number To Return:表示第一个 OP_REPLY 响应中的文档返回数

    Reply 消息

    该OP_REPLY消息由数据库发送,以响应OP_QUERY或OP_GET_MORE消息。

    cursorID :会在 OP_REPLY 中设置
    number Return:返回的文档数量
    document: 一个或多个要插入到集合中的文档。如果有多个,它们会依次写入到插槽中。

    Insert document 消息

    OP_INSERT消息用于将一个或多个文档插入到集合中。

    document: 一个或多个要插入到集合中的文档。如果有多个,它们会依次写入到插槽中。

    fullCollectionName:完整的集合名称;即命名空间。完整的集合名称是数据库名称与集合名称.的拼接,使用一个for concatenation。例如,对于数据库foo和集合bar,完整的集合名称是foo.bar。

    Update document 消息

    OP_UPDATE消息用于更新集合中的文档。

    ZERO: 0 - 保留给以后使用
    selector:文档查询条件,BSON文档,指定要更新的文档的选择查询。
    update: 指定要执行的更新

    Delete document 消息

    OP_DELETE消息用于从集合中删除一个或多个文档。

    ZERO:整数值为0.保留以备将来使用。
    selector:代表用于选择要删除的文档的查询的BSON文档。选择器将包含一个或多个元素,所有这些元素都必须与要从集合中移除的文档匹配。

    Get More 消息

    OP_GET_MORE消息用于在数据库中查询集合中的文档。

    ZERO: 0 - 保留给以后使用
    number To Return: 返回的文档数量,如果numberToReturn是0,数据库将使用默认的返回大小。
    curso rID :会在 OP_REPLY 中设置

    数据库将用OP_REPLY消息响应OP_GET_MORE消息。

    Kill Cursors 消息

    OP_KILL_CURSORS消息用于关闭数据库中的活动光标。这是确保在查询结束时回收数据库资源所必需的。

    ZERO:整数值为0.保留以备将来使用。
    numberOfCursorIDs:消息中的光标ID的数量。
    cursorIDs:游标ID的“数组”被关闭。如果有多个,它们会依次写入到插槽中。

    如果游标被读取直到耗尽(读取直到OP_QUERY或OP_GET_MORE为游标ID返回0),则不需要终止游标。

    MongoDB数据库协议解析及C/C++代码实现

    ...
    static int dissect_opcode_types(u_char *mongo_data, unsigned int offset, unsigned int opcode, int message_length)
    {
        if(opcode == 1)
        {
    		printf("Response :");
        }
        else
        {
    		printf("Request :");
        }
    	
    	printf("%s\n", val_to_str_const(opcode, opcode_vals, "Unknown"));	
        switch(opcode)
    	{
    		case OP_REPLY:
    		    offset = dissect_mongo_reply(mongo_data, offset, message_length);
    			break;
    		case OP_MESSAGE:
    		  offset = dissect_mongo_msg(mongo_data, offset, message_length);
    			break;
    		case OP_UPDATE:
    		    offset = dissect_mongo_update(mongo_data, offset, message_length);
    			break;
    		case OP_INSERT:
    		    offset = dissect_mongo_insert(mongo_data, offset, message_length);
    			break;
    		case OP_QUERY:
    			offset = dissect_mongo_query(mongo_data, offset, message_length);
    			break;
    		case OP_GET_MORE:
    		    offset = dissect_mongo_getmore(mongo_data, offset, message_length);
    			break;
    		case OP_DELETE:
    		    offset = dissect_mongo_delete(mongo_data, offset, message_length);
    			break;
    		case OP_KILL_CURSORS:
    		    offset = dissect_mongo_kill_cursors(mongo_data, offset, message_length);
    			break;
    		case OP_COMMAND:
    		    offset = dissect_mongo_op_command(mongo_data, offset, message_length);
    			break;
    		case OP_COMMANDREPLY:
    		    offset = dissect_mongo_op_commandreply(mongo_data, offset, message_length);
    			break;
    		case OP_COMPRESSED:
    		  offset = dissect_mongo_op_compressed(mongo_data, offset, message_length);
    			break;
    		case OP_MSG:
    		  offset = dissect_mongo_op_msg(mongo_data, offset, message_length);
    			break;
    		default:
    		  /* No default Action */
    		  break;
        }
    
        return offset;
    }
    ....
    int main(int argc, char* argv[])
    {
        char errbuf[1024];
        pcap_t *desc = 0;
    
        char *filename = argv[1];
        if (argc != 2)
        {
            printf("usage: ./dissect_mongo [pcap file]\n");
            return -1;
        }
    
        printf("ProcessFile: process file: %s\n", filename);
        if ((desc = pcap_open_offline(filename, errbuf)) == NULL)
        {   
            printf("pcap_open_offline: %s error!\n", filename);
            return -1; 
        }   
    
        pcap_loop(desc, pkt_number, (pcap_handler)ace_pcap_hand, NULL);
        pcap_close(desc);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82

    运行结果:



    总结

    MongoDB Wire Protocol是一个简单的基于socket,请求/响应方式的协议,客户端使用常规的TCP/IP套接字(socket)进行通信,服务端默认监听端口是 27017。

    在这里插入图片描述

    参考:https://www.mongodb.com/docs/manual/reference/mongodb-wire-protocol/

  • 相关阅读:
    Python Opencv实践 - 视频文件操作
    有求必应 | 听说这个管线排布,横竖都行?
    结合重心反向变异的飞蛾扑火优化算法-附代码
    gitlab服务器迁移(亲测有效)
    2024采用JSP的酒店客房管理系统源代码+毕业设计论文+开题报告+答辩PPT
    尚医通 (二十六) --------- 科室接口开发
    一款.NET开源、功能强大、跨平台的绘图库 - OxyPlot
    出现 CUDA out of memory 的解决方法
    TI C2000系列TMS320F2837xD开发板(DSP+FPGA)硬件规格参数说明书
    前端培训丁鹿学堂:vue基础之v-model使用详情
  • 原文地址:https://blog.csdn.net/chen1415886044/article/details/126772868