應用層自定義協議與序列化
應用層
我們程序員寫的一個個解決我們實際問題,滿足我們日常需求的網絡程序,都是在應用層.
協議是一種"約定".Socket的接口,在讀寫數據時,都是按"字符串"的方式來發送接收的.如果我們要傳輸一些"結構化的數據"怎么辦呢?
其實,協議就是雙方約定好的結構化的數據
網絡版計算器
例如,我們需要實現一個服務器版的加法器.我們需要客戶端把要計算的兩個加數發過去, 然后由服務器進行計算,最后再把結果返回給客戶端.
約定方案一:
- 客戶端發送一個形如"1+1"的字符串;
- 這個字符串中有兩個操作數,都是整形;
- 兩個數字之間會有一個字符是運算符,運算符只能是+;
- 數字和運算符之間沒有空格;
- …
約定方案二:
- 定義結構體來表示我們需要交互的信息;
- 發送數據時將這個結構體按照一個規則轉換成字符串,接收到數據的時候再按照相同的規則把字符串轉化回結構體;
- 這個過程叫做"序列化"和"反序列化"
序列化和反序列化
無論我們采用方案一,還是方案二,還是其他的方案,只要保證,一端發送時構造的數據,在另一端能夠正確的進行解析,就是ok的.這種約定,就是應用層協議
但是,為了讓我們深刻理解協議,打算自定義實現一下協議的過程。
- 我們采用方案2,我們也要體現協議定制的細節
- 我們要引入序列化和反序列化,只不過我們課堂直接采用現成的方案–jsoncpp庫
- 我們要對socket進行字節流的讀取處理
理解read、write、recv、send和tcp支持全雙工
所以:
-
在任何一臺主機上,TCP連接既有發送緩沖區,又有接受緩沖區,所以,在內核中,可以在發消息的同時,也可以收消息,即全雙工
-
這就是為什么一個tcpsockfd讀寫都是它的原因
-
實際數據什么時候發,發多少,出錯了怎么辦,由TCP控制,所以TCP叫做傳輸控制協議
Json
Jsoncpp
Jsoncpp是一個用于處理JSON數據的C++庫。它提供了將JSON數據序列化為字符串以及從字符串反序列化為C++數據結構的功能。Jsoncpp是開源的,廣泛用于各種需要處理JSON數據的C++項目中。
特性
- 簡單易用:Jsoncpp提供了直觀的API,使得處理JSON數據變得簡單。
- 高性能:Jsoncpp的性能經過優化,能夠高效地處理大量JSON數據。
- 全面支持:支持JSON標準中的所有數據類型,包括對象、數組、字符串、數字、布爾值和null。
- 錯誤處理:在解析JSON數據時,Jsoncpp提供了詳細的錯誤信息和位置,方便開發者調試。
當使用Jsoncpp庫進行JSON的序列化和反序列化時,確實存在不同的做法和工具類可供選擇。以下是對Jsoncpp中序列化和反序列化操作的詳細介紹:
安裝
C++
- ubuntu: sudo apt - get install libjsoncpp - dev
- Centos: sudo yum install jsoncpp - devel
序列化
序列化指的是將數據結構或對象轉換為一種格式,以便在網絡上傳輸或存儲到文件中。Jsoncpp提供了多種方式進行序列化:
- 使用Json::Value的toStyledString方法
- 優點:將Json::Value對象直接轉換為格式化的JSON字符串。
- 示例:
#include <iostream>
#include <jsoncpp/json/json.h>int main()
{Json::Value root;root["name"] = "joe";root["sex"] = "男";std::string s = root.toStyledString();std::cout << s << std::endl;return 0;
}
執行結果:
{"name" : "joe","sex" : "男"
}
- 使用Json::StreamWriter
- 優點:提供了更多的定制選項,如縮進、換行符等。
- 示例:
#include <iostream>
#include <string>
#include <memory>
#include <jsoncpp/json/json.h>int main()
{Json::Value root;root["name"] = "joe";root["sex"] = "男";Json::StreamWriterBuilder wbuilder; // StreamWriter的工廠std::unique_ptr<Json::StreamWriter> writer(wbuilder.newStreamWriter());std::stringstream ss;writer->write(root, &ss);std::cout << ss.str() << std::endl;return 0;
}
執行結果:
{"name" : "joe","sex" : "男"
}
- 使用Json::FastWriter
- 優點:比StyledWriter更快,因為它不添加額外的空格和換行符。
- 示例:
#include <iostream>
#include <string>
#include <memory>
#include <jsoncpp/json/json.h>int main()
{Json::Value root;root["name"] = "joe";root["sex"] = "男";Json::FastWriter writer;std::string s = writer.write(root);std::cout << s << std::endl;return 0;
}
執行結果:
{"name":"joe","sex":"男"}
另一種寫法示例:
#include <iostream>
#include <string>
#include <memory>
#include <jsoncpp/json/json.h>int main()
{Json::Value root;root["name"] = "joe";root["sex"] = "男";Json::StyledWriter writer;std::string s = writer.write(root);std::cout << s << std::endl;return 0;
}
執行結果:
{"name" : "joe","sex" : "男"
}
反序列化
反序列化指的是將序列化后的數據重新轉換為原來的數據結構或對象。Jsoncpp提供了以下方法進行反序列化:
- 使用Json::Reader
- 優點:提供詳細的錯誤信息和位置,方便調試。
- 示例:
#include <iostream>
#include <jsoncpp/json/json.h>int main() {std::string json_string = "{\"name\":\"張三\",\"age\":30,\"city\":\"北京\"}";// 解析JSON字符串Json::Value root;Json::Reader reader;bool parsingSuccessful = reader.parse(json_string, root);if (!parsingSuccessful) {// 解析失敗,輸出錯誤信息std::cout << "Failed to parse JSON: " << reader.getFormattedErrorMessages() << std::endl;return 1;}// 訪問JSON數據std::string name = root["name"].asString();int age = root["age"].asInt();std::string city = root["city"].asString();// 輸出結果std::cout << "Name: " << name << std::endl;std::cout << "Age: " << age << std::endl;std::cout << "City: " << city << std::endl;return 0;
}
執行結果:
Name: 張三
Age: 30
City: 北京
- 使用Json::CharReader的派生類(不推薦,上面的足夠了)
在某些情況下,你可能需要更精細地控制解析過程,可以直接使用Json::CharReader的派生類。
但通常情況下,使用Json::parseFromString或Json::Reader的parse方法就足夠了。
總結
你可以用toStyledString、StreamWriter和FastWriter提供了不同的序列化選項,它們提供強大的錯誤處理機制。
Json::Reader和parseFromString函數是Jsoncpp中主要的反序列化工具,在進行序列化和反序列化時,請確保處理所有可能的錯誤情況,并驗證輸入和輸出的有效性。
Json::Value
Json::Value是Jsoncpp庫中的一個重要類,用于表示和操作JSON數據結構。以下是一些常用的Json::Value操作列表:
- 構造函數
- Json::Value():默認構造函數,創建一個空的Json::Value對象。
- Json::Value(Json::ValueType type, bool aValue = false):根據給定的類型創建一個Json::Value對象。
- 訪問元素
- Json::Value& operator[](const char key)*:通過鍵(字符串)訪問對象中的元素。如果鍵不存在,則創建一個新的元素。
- Json::Value& operator[](const std::string& key):同上,但使用std::string類型的鍵。
- Json::Value& operator[](ArrayIndex index):通過索引訪問數組中的元素。如果索引超出范圍,則創建一個新的元素。
- Json::Value at(const char key)*:通過鍵訪問對象中的元素,如果鍵不存在則拋出異常。
- Json::Value at(const std::string& key):同上,但使用std::string類型的鍵。
- 類型檢查
- bool isNull():檢查值是否為null類型。
- bool isBool():檢查值是否為布爾類型。
- bool isInt():檢查值是否為32位整數類型。
- bool isInt64():檢查值是否為64位整數類型。
- bool isUInt():檢查值是否為無符號整數類型。
- bool isUInt64():檢查值是否為64位無符號整數類型。
- bool isIntegral():檢查值是否為整數或可轉換為整數的浮點數。
- bool isDouble():檢查值是否為雙精度浮點數。
- bool isNumeric():檢查值是否為數字(整數或浮點數)。
- bool isString():檢查值是否為字符串。
- bool isArray():檢查值是否為數組。
- bool isObject():檢查值是否為對象(即鍵值對的集合)。
- 賦值和類型轉換
- Json::Value& operator=(bool value):將布爾值賦給Json::Value對象。
- Json::Value& operator=(int value):將32位整數賦給Json::Value對象。
- Json::Value& operator=(int64_t value):將64位整數賦給Json::Value對象。
- Json::Value& operator=(uint value):將無符號整數賦給Json::Value對象。
- Json::Value& operator=(uint64_t value):將64位無符號整數賦給Json::Value對象。
- Json::Value& operator=(double value):將雙精度浮點數賦給Json::Value對象。
- Json::Value& operator=(const char value)*:將C字符串賦給Json::Value對象。
- Json::Value& operator=(const std::string& value):將std::string賦給Json::Value對象。
- bool asBool():將值轉換為布爾類型(如果可能)。
- int asInt():將值轉換為32位整數類型(如果可能)。
- Int64 asInt64():將值轉換為64位整數類型(如果可能)。
- UInt64 asUInt64():將值轉換為64位無符號整數類型(如果可能)。
- std::string asString():將值轉換為字符串類型(如果可能)。
- 數組和對象操作
- size_t size():返回數組或對象中的元素數量。
- bool empty():檢查數組或對象是否為空。
- void resize(ArrayIndex newSize):調整數組的大小。
- void clear():刪除數組或對象中的所有元素。
- void append(const Json::Value& value):在數組末尾添加一個新元素。
- Json::Value& operator[](const char key, const Json::Value& defaultValue = Json::nullValue)*:在對象中插入或訪問一個元素,如果鍵不存在則使用默認值。
- Json::Value& operator[](const std::string& key, const Json::Value& defaultValue = Json::nullValue):在對象中插入或訪問一個元素,如果鍵不存在則使用默認值,但使用std::string類型的鍵。