1. 程式人生 > >淺談jsoncpp及實現伺服器客戶端之間json包傳輸

淺談jsoncpp及實現伺服器客戶端之間json包傳輸

JSON是什麼?

JSON 是一種用於資料交換的文字格式(本質是一種檔案組織方式,比如你熟悉的txt, csv, doc,docx檔案等等),目的是取代繁瑣笨重的XML格式。這種格式不僅人很容易進行閱讀和編寫,同時機器也很容易解析和生成,是當前十分流行的資料格式,尤其是在前端領域。JSON是一種傳遞物件的語法,物件可以是name/value對、陣列其他物件,現在瀏覽器都自帶json解析。

簡單來說json就是一種客戶端與伺服器都可以識別及解析的資料格式,避免了難以定義資料格式的麻煩。比如:如果你每個客戶端想給伺服器一次傳送名稱,年齡,密碼等資訊,那麼伺服器用什麼資料結構來接收呢?有人就說用結構體,但是結構體中各個變數我們定義多大呢?是吧,多大都不合適,所以有了json這種統一的資料格式,雙方都根據json的規則進行解析就可以獲取到雙方傳送的位元組流(網路中用位元組流傳輸),而不必為定義每個資料的大小而苦惱(相當於傳送接收都是一個json物件不存在大小不一致)。可以將json理解為一個容器,我們按照容器的開啟方式就可以開啟容器獲取到資料。

舉個栗子(下面提供一小段json程式碼)? 

{
    "firstname":"John",
    "lastname":"smith",
    "isAlive":true,
    "age":25,
    "address":{
     "streetaddress":"21 street",   //街道地址
     "city":"XI'AN",       //城市地址
    },
    "phonenum":{
    {
        "type":"home",  //家庭電話
        "number": "212555666" 
    },
    {
        "type":"office",  //公司電話
        "number":"645111999"
    }      
    },
    "children":{
    [{"name":"mary","age",2},{"name":"bob","age":4}]  //陣列
    },
    "spouse":null
}

這是個json,儲存的資料描述了一個人John Smith的一些個人資訊,比如姓名,是否活著,年齡,地址以及電話號碼等資訊。其中,地址address和電話號碼phonenum呢,下面又包含了街道、城市,家庭電話、辦公電話資訊。

如果之前只接觸過儲存在Excel或者sql server等結構化資料庫中的結構化資料,那麼第一次看到json格式的資料,一定覺得很新穎。這種資料組織方式,條理清晰,能更好的表示自然界中人或物等實體的屬性關係。可以理解為一種樹狀結構,一棵有枝幹,枝幹上有葉子的樹。

你看懂了吧?那麼當你再看到它時就知道它是JSON了。主要部分:

花括弧,方括號,冒號和逗號

  • 花括弧表示一個“容器”
  • 方括號裝載陣列
  • 名稱和值用冒號隔開
  • 陣列元素通過逗號隔開

區分json和XML?

<1>什麼是XML?

XML也是一種格式規範,是一種包含了資料以及資料說明的文字格式規範,它是可擴充套件標記語言。擴充套件標記語言不是超文字標記語言(eg:HTML)的替代而是對超文字標記語言的補充。XML不像HTML這種超文字標記語言用於網頁的編輯(被設計用來顯示資料,其焦點是資料的外觀),它主要用於資料格式化儲存(被設計用來傳輸和儲存資料,其焦點是資料的內容),現在用的比較多的是作為配置檔案(表述底層資料)或者資料結構定義(資料載體),在網路中傳輸,是網路傳輸中間語言。

舉個栗子說明一下:

比如,我們要給對方傳輸一段資料,資料內容是“too young,too simple,sometimes naive”,要將這段話按照屬性拆分為三個資料的話,就是,年齡too young,閱歷too simple,結果sometimes naive。

我們都知道程式不像人,可以體會字面意思,並自動拆分出資料,因此,我們需要幫助程式做拆分,因此出現了各種各樣的資料格式以及拆分方式。

比如,可以是這樣的
資料為“too young,too simple,sometimes naive”
然後按照逗號拆分,第一部分為年齡,第二部分為閱歷,第三部分為結果。

這種方式可以用來容納資料並能夠被解析,但是不直觀,通用性也不好。基於這種情況,出現了xml這種資料格式, 上面的資料用XML表示的話
可以是這樣

<person age="too young" experience="too simple" result="sometimes naive" />

也可以是這樣

<person>
    <age value="too young" />
    <experience value="too simple" />
    <result value="sometimes naive" />
</person>

兩種方式都是xml,都很直觀,附帶了對資料的說明,並且具備通用的格式規範可以讓程式做解析。

與普通的純文字儲存資料不同的是XML與json做了資料的拆分和歸類,以便讓計算程式區分各部分的內容

拆分方式有多種,可以這樣來表述:

  • 用XML格式拆分資料
  • 用JSON格式拆分資料

xml可用於儲存,傳輸,交換資料,因為只描述/關心資料的結構,可以做到平臺無關,實現跨平臺,所以Windows平臺喜歡,Linux平臺喜歡,各種系統(包括非作業系統)都喜歡用它,它可拓展性好,對人類友好,結構清晰易讀。

小知識:

記得好像據說,最早是在全球資訊網剛出現的時候,為了在全球資訊網上傳輸資料,接收方能造理解,人們創了這種自帶格式說明的檔案格式。當時人們期待著xml能一統天下,結果後來html和瀏覽器出現了,html成了標準的網路協議,就沒xml什麼事了。現在xml就是個工具,幹啥都行。可以儲存並檢索資料,可以做傳輸約定格式,可以做配置檔案。

<2>json與xml的相同點及json比xml好在哪裡?

首先XML和JSON都使用結構化方法來標記資料,而且它們的值都是可列舉的,是“人類可讀”的,兩者都可以巢狀(有層級的,可以在值裡再存放值)),而且都能被多種的程式語言解析和使用,都能使用AJAX方法來傳遞(例如httpWebRequest) 。

但是JSON 簡單的語法格式和清晰的層次結構明顯要比 XML 容易閱讀,並且在資料交換方面,由於 JSON 所使用的字元要比 XML 少得多(相比 XML 檔案更小,相比XML確實更加輕量級,可以大大得節約傳輸資料所佔用得頻寬。XML有時候描述東西太費勁,驗證複雜,而且有大量的冗餘,尤其是結束標記(</html>)。如果只是幾個元素沒什麼,如果非常多,就不利於網路傳輸了,所以現在網路上用的非常多的是JSON。但是XML作為半結構化文件的代表性標準,早期代表的是一種資料思想。另外JSON的結構更容易對映至一般語言的資料結構,而且json具有長時間的穩定性(JSON 格式的創始人聲稱此格式永遠不升級),並且javaScript天生支援Json,解析一點都不費勁

XML相比JSON最大的區別是充滿了冗餘資訊。多數時候我們不需要冗餘資訊,但是一旦需要的時候沒有就是不行。這就是XML與JSON最大的區別。為什麼很多人都說json比較好,因為多數時候不需要冗餘資訊。

<3>為啥有了json還需要使用xml?

我們在選擇用哪種方式儲存資料一般會考慮以下兩點

  • 哪種更容易閱讀
  • 哪種更容易被程式解析(正反序列化)

根據不同的需求我們才會選擇儲存方式,只是json比較簡單,容易閱讀而已,但是在某些場合,在人機都需要識別資料的時候,比如配置檔案,選擇xml正是因為有大量的冗餘(提示資訊)使得xml的可讀性比json好。所以說我們選擇那種方式取決於我們的需求,這兩者不是互相替代的

看個XML和json的栗子

XML:

<?xml version="1.0" encoding="utf-8">
<country>
    <name>中國</name>
    <province>
        <name>廣東</name>
        <cities>
            <city>廣州</city>
            <city>佛山</city>
            <city>深圳</city>
        </cities>
    </province>
    <province>
        <name>陝西</name>
        <cities>
            <city>西安</city>
            <city>渭南</city>
            <city>咸陽</city>
        </cities>
    </province>
</country>

json:

{
    "name":"中國",
    "province":[
     {
        "name":"廣東",
        "cities":
         {
            "city":["廣州","佛山","深圳"]
         }
     }, 
     {    
        "name":"陝西",
        "cities":
          {
             "city":["西安","渭南","咸陽"]
          }
     }]   
}

看出來沒,其實資料都是一樣的,不同的只是資料的格式而已,同樣的資料,我用xml格式傳給你,你用xml格式解析出資料,用json格式傳給你,你就用json格式解析出資料,還可以我本地儲存的是xml格式的資料,我自己先解析出資料,然後構造成json格式傳給你,你解析json格式,獲得資料,再自己構造成xml格式儲存起來,說白了,不管是xml還是json,都只是包裝資料的不同格式而已,重要的是其中含有的資料,而不是包裝的格式。

json用法:

關於json用法大家可以參考這兩個部落格:

客戶端與伺服器之間json包傳輸簡單示例程式碼

伺服器:

#include<iostream>
#include<string.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<errno.h>
#include<json/json.h>
#include<sys/types.h>
#include<sys/socket.h>
//json連結的時候要加上 -ljson庫
using namespace std;
int main()
{
	int sockfd=socket(AF_INET,SOCK_STREAM,0);
	if(sockfd==-1)
	{
		cout<<"socket fail;errno:"<<errno<<endl;
		return 0;
	}
	struct sockaddr_in ser,cli;
	ser.sin_family=AF_INET;
	ser.sin_port=htons(5500);
	ser.sin_addr.s_addr=inet_addr("127.0.0.1");
	if(-1==bind(sockfd,(struct sockaddr*)&ser,sizeof(ser)))
	{
		cerr<<"bind fail;errno:"<<errno<<endl; //cerr
		return 0;
	}
	if(-1==listen(sockfd,20))
	{
		cout<<"listen fail;errno:"<<errno<<endl;
		return 0;
	}
	socklen_t len=sizeof(cli);
	int clifd=accept(sockfd,(struct sockaddr*)&cli,&len);
	if(-1==clifd)
	{
		cout<<"accept fail;errno:"<<errno<<endl;
		return 0;
	}
	char buff[128]={0};
	int n=recv(clifd,buff,127,0);//從網路中讀取到字元流
	if(n>0)
	{
		//解析客戶端傳送的json包,json包含三種類型的類:Value  Reader Writer
		Json::Value val; //Value是jsoncpp中最基本及重要的類,表示各種型別的物件,val是定義了臨時物件,供下面程式碼用
		Json::Reader read;//用於讀取,將字串轉化為Json::Value物件
		if(-1==read.parse(buff,val)) //將buff轉化為Json::Value物件
		{
			cout<<"change json fail;errno:"<<errno<<endl;
			return 0;
		}
		cout<<val.toStyledString()<<endl;
		//cout<<"name:"<<val["name"].asString()<<endl; //asString將遠端的結果轉化為字串
		//cout<<"pw:"<<val["pw"].asString()<<endl;

		//給客戶端傳送訊息
		Json::Value root;  //root表示整個json物件
		root["reason"]="ok";  //新建一個key(名為reason)賦予字串值ok
		if(-1==send(clifd,root.toStyledString().c_str(),strlen(root.toStyledString().c_str()),0)) //將json物件轉化為字串在網路中傳輸
		{
			cout<<"send reason fail;errno:"<<errno<<endl;
			return 0;
		}
	}
	else
	{
		cout<<"recv fail;errno:"<<errno<<endl;
		return 0;
	}
	return 0;
}

客戶端: 

#include<iostream>
#include<string.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<errno.h>
#include<json/json.h>
#include<sys/types.h>
#include<sys/socket.h>
using namespace std;
int main()
{
	int sockfd=socket(AF_INET,SOCK_STREAM,0);
	if(sockfd==-1)
	{
		cout<<"socket fail;errno:"<<errno<<endl;
		return 0;
	}
	struct sockaddr_in ser,cli;
	ser.sin_family=AF_INET;
	ser.sin_port=htons(5500);
	ser.sin_addr.s_addr=inet_addr("127.0.0.1");
	if(-1==connect(sockfd,(struct sockaddr*)&ser,sizeof(ser)))
	{
		cout<<"clifd connect fail;errno:"<<errno<<endl;
		return 0;
	}

	//建立json包
	Json::Value val;
	val["name"]="zhangsan";//新建key為name 值為zhangsan的val物件中的一個對映
	val["pw"]="123456";
	//傳送資料
	if(-1==send(sockfd,val.toStyledString().c_str(),strlen(val.toStyledString().c_str()),0))
	{
		cout<<"clifd send fail;errno:"<<errno<<endl;
		return 0;
	}
	//接受資料
	char buff[128]={0};
	if(recv(sockfd,buff,127,0)<=0)
	{
		cout<<"clifd recv fail;errno:"<<errno<<endl;
		return 0;
	}
	Json::Value root;
	Json::Reader read;
	if(-1==read.parse(buff,root))  //字串轉化為json物件
	{
		cout<<"json parse fail;errno:"<<errno<<endl;
		return 0;
	}
	//輸出json
	//cout<<root.asString()<<endl; 
	cout<<"reason:"<<root["reason"].asString()<<endl; //按字串列印
	return 0;
}

編譯連結時需要加上json庫

執行結果:

伺服器輸出時還有一種將資料分開的方式,我上面引起來了,大家可以自己放開測試一下。