一、什麼是通訊協議
通訊協議是指雙方實體完成通訊或服務所必須遵循的規則和約定。
二、diydb的協議格式
1、協議格式是通訊協議中最重要的部分之一,diydb採用的是自定義格式和bson格式的混合格式
2、協議格式總覽
協議主要分三個大段
(1)協議頭:包括訊息長度(用於讀取乙個完整的協議包)和訊息型別
(2)協議主體:不同的訊息型別對應不同的協議主體,協議主體主要儲存的是每種訊息型別獨有的資訊
(3)附加資訊:乙個或多個bson格式的物件,表示實際操作和返回的資料庫中的資料物件
3、協議格式主體
返回訊息:一、返回值,用於表示返回成功或失敗;二、返回記錄數,用於表示返回的資料物件的個數,即附加訊息中bson物件的個 數。
插入訊息:插入記錄數,表示要插入的資料物件的個數。
刪除訊息:因為每次只能刪除乙個資料,所以這裡沒有特殊資訊。
查詢訊息:因為每次只能查詢乙個資料,所以這裡沒有特殊資訊。
注:1)diydb的資料刪除和查詢時,客戶端必須在附加資訊中以bson格式傳遞乙個資料物件的_id資訊。
2)另外,quit命令只需要協議頭就可以了。
4、附加資訊
每個附加記錄或插入記錄實際上就是乙個bson物件,刪除條件或查詢條件實際上就是格式的json物件對應的bson物件(因為diydb實際上沒有複雜的條件查詢功能,當然可以以後增加)。
注:1)、在直接傳遞乙個結構體物件時,要注意結構體裡面的屬性的位元組對齊。
2)、跨作業系統通訊時要注意位元組序的問題。
三、原始碼分析
通訊協議的主要實現(這裡主要是資料打包解包)在msg.hpp和msg.cpp兩個檔案中,比如客戶端用msgbuildinsert封裝表示插入資料的資料報並存入資料流中,然後伺服器端**執行緒收到這個資料流後用msgextractinsert解包這個資料流中的資料。下面是msg.hpp的所有**。
#ifndef msg_hpp__
#define msg_hpp__
#include "bson.h"
#define op_reply 1//返回訊息
#define op_insert 2//插入訊息
#define op_delete 3//刪除訊息
#define op_query 4//查詢訊息
#define op_disconnect 6//斷開連線訊息
#define op_connect 7//連線訊息
#define return_code_state_ok 1
struct msgheader
/*資料報的頭*/
;struct msgreply
/*返回資料報*/
;struct msginsert
/*插入資料報*/
;struct msgdelete
/*刪除資料報*/
;struct msgquery
/*查詢資料報*/
;/*返回訊息的封裝*/
int msgbuildreply ( char **ppbuffer, int *pbuffersize,
int returncode, bson::bsonobj *objreturn ) ;
/*解訊息*/
int msgextractreply ( char *pbuffer, int &returncode, int &numreturn,
const char **ppobjstart ) ;
/*做乙個插入obj的資料報*/
int msgbuildinsert ( char **ppbuffer, int *pbuffersize, bson::bsonobj &obj ) ;
/*做乙個插入多個obj的資料報*/
int msgbuildinsert ( char **ppbuffer, int *pbuffersize, vector&obj ) ;
/*解插入*/
int msgextractinsert ( char *pbuffer, int &numinsert, const char **ppobjstart ) ;
/*刪除*/
int msgbuilddelete ( char **ppbuffer, int *pbuffersize, bson::bsonobj &key ) ;
/*解刪除*/
int msgextractdelete ( char *pbuffer, bson::bsonobj &key ) ;
/*查詢*/
int msgbuildquery ( char **ppbuffer, int *pbuffersize, bson::bsonobj &key ) ;
/*解查詢*/
int msgextractquery ( char *pbuffer, bson::bsonobj &key ) ;
int msgmultiinsert ( char **ppbuffer, int *pbuffersize, bson::bsonobj &obj ) ;
/*做乙個插入多個obj的資料報*/
int msgbuildinsert ( char **ppbuffer, int *pbuffersize, vector&obj ) ;
#endif
對上面函式的實現,我們以插入資料報的封裝和解包為例來分析
int msgbuildinsert ( char **ppbuffer, int *pbuffersize, bsonobj &obj )
pinsert = (msginsert*)(*ppbuffer) ;//將位元組流劃分成包格式
// 構建協議頭
pinsert->header.messagelen = size ;
pinsert->header.opcode = op_insert ;
// 構建協議主體
pinsert->numinsert = 1 ;
// 構建附加訊息,即填bson物件
memcpy ( &pinsert->data[0], obj.objdata(), obj.objsize() ) ;
done :
return rc ;
error :
goto done ;
}
int msgextractinsert ( char *pbuffer, int &numinsert, const char **ppobjstart )
// 檢測資料報的型別對不對
if ( pinsert->header.opcode != op_insert )
// 解析附加資訊,即取出bson物件
numinsert = pinsert->numinsert ;
// object
if ( 0 == numinsert )
else
done :
return rc ;
error :
goto done ;
}
注:客戶端得到的bson物件的由來:使用者在客戶端輸入命令,這個命令如果包含json格式的文字,則通過json庫根據表示json格式的文字構建乙個json物件,然後通過bson庫將這個json物件轉換成乙個bson物件。
四、總結
1、因為tcp通訊是位元組流,並沒有邊界標識,所以應用層在傳資料時,必須通過增加資料報長度字段或者增加資料報邊界標識來區分乙個完整的包。diydb中用的是資料報長度的方式,這也是實際應用中常用的方式。
2、如果在通訊時直接傳結構體資料,則要注意位元組對齊機制,所以要合理安排結構體中屬性的長度的排列順序。
客戶端和伺服器資料互動
客戶端一次把資料取出,還是分多次來取?有的人說把資料一次從資料庫取到客戶端然後在客戶端任意處理效率會高。有的人說把一次性把所有資料取到客戶端很耗時且資料大多沒有用處最好分次處理。其實這兩個說法並不矛盾,只是他們實用的方面是不同的。如果資料最不大,但是可能會和伺服器發生多次互動,最好一次把資料取出到客...
Redis客戶端和伺服器
一 redis客戶端 redisserver結構中儲存了所有客戶端的狀態資訊。struct redisserver 命令client list可以列出目前所有連線到伺服器的客戶端。命令client setname name 可以給客戶端設定乙個名字。偽客戶端 由於伺服器必須接收來自客戶端的命令以執行...
Redis 客戶端和伺服器
客戶端的資料結構 typedef struct redisclient redisclient 名字 name 標誌值 flags 指向客戶端正在使用的資料庫的指標,以及該資料庫的號碼 當前要執行的命令 命令的引數 命令的個數,以及指向命令實現函式的指標 輸入緩衝區 querybuf 輸出緩衝區 複...