网络传输中,牵扯到数据序列化,反序列化。其实,通信双方约定好序列化方式(大端/小端)即可。例如发送方按照大端序列化,接收端在接收到数据后,接收端判断自己的大小端模式,如果自己的CPU是大端模式,则不需要做大小端转换,直接进行数据解析即可。如果是小端,则解析完后还需要将数据转为小端模式。
当两个进程在进行远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。发送方需要把这个对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为对象。
把对象转换为字节序列的过程称为对象的序列化。
把字节序列恢复为对象的过程称为对象的反序列化。
说的再直接点,序列化的目的就是为了跨进程传递格式化数据
大端:数据高字节内容保存在内存的低地址中,数据的低字节内容保存在内存的高地址中。
小端:数据高字节内容保存在内存的高地址中,数据的低字节内容保存在内存的低地址中。
网络字节顺序是TCP/IP中规定好的一种数据表示格式,它与具体的CPU类型、操作系统等无关,从而可以保证数据在不同主机之间传输时能够被正确解释。网络字节顺序采用big endian排序方式。
序列化是用来通信的,服务端把数据序列化,发送到客户端,客户端把接收到的数据反序列化后对数据进行操作,完成后再序列化发送到服务端,服务端再反序列化数据后对数据进行操作。说白了,数据需要序列化以后才能在服务端和客户端之间传输。这个服务端和客户端的概念是广义的,可以在网络上,也可以在同一台机器的不同进程中,甚至在同一个进程中进行通信。在传统编程中,对象是通过调用栈间接的与客户端交互,但在面向服务的编程中,客户端永远都不会直接调用实例。
不同的CPU有不同的字节序类型,这些字节序是指 整数 在内存中保存的顺序,这个叫做 主机序。
最常见的有两种:
1.Little endian:将低序字节存储在起始地址
2.Big endian:将高序字节存储在起始地址
LE little-endian(小端)
- 最符合人的思维的字节序;
- 地址低位存储值的低位;
- 地址高位存储值的高位;
- 怎么讲是最符合人的思维的字节序,是因为从人的第一观感来说;
- 低位值小,就应该放在内存地址小的地方,也即内存地址低位;
- 反之,高位值就应该放在内存地址大的地方,也即内存地址高位;
BE big-endian(大端)
- 最直观的字节序;
- 地址低位存储值的高位;
- 地址高位存储值的低位;
- 为什么说直观,不要考虑对应关系;
- 只需要把内存地址从左到右按照由低到高的顺序写出;
- 把值按照通常的高位到低位的顺序写出;
- 两者对照,一个字节一个字节的填充进去;
x86系列CPU都是little-endian的字节序。
网络字节顺序是TCP/IP中规定好的一种数据表示格式,它与具体的CPU类型、操作系统等无关,从而可以保证数据在不同主机之间传输时能够被正确解释。网络字节顺序采用big endian排序方式。
为了进行转换 bsd socket提供了转换的函数 有下面四个
htons 把unsigned short类型从主机序转换到网络序
htonl 把unsigned long类型从主机序转换到网络序
ntohs 把unsigned short类型从网络序转换到主机序
ntohl 把unsigned long类型从网络序转换到主机序
在使用little endian的系统中,这些函数会把字节序进行转换;
在使用big endian类型的系统中,这些函数会定义成空宏;
同样,在网络程序开发时 或是跨平台开发时,也应该注意保证只用一种字节序,不然两方的解释不一样就会产生bug。
JSON(JavaScript Object Notation, JS 对象简谱) 是一种轻量级的数据交换格式。它基于 ECMAScript (欧洲计算机协会制定的js规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。
JSON有两种表示结构,对象和数组。
对象结构以”{”大括号开始,以”}”大括号结束。中间部分由0或多个以”,”分隔的”key(关键字)/value(值)”对构成,关键字和值之间以”:”分隔,语法结构如代码。
{
key1:value1,
key2:value2,
...
}
其中关键字是字符串,而值可以是字符串,数值,true,false,null,对象或数组
数组结构以”[”开始,”]”结束。中间由0或多个以”,”分隔的值列表组成,语法结构如代码。
[
{
key1:value1,
key2:value2
},
{
key3:value3,
key4:value4
}
]
认识JSON字符串
普通字符串,json字符串和json对象的区别:
字符串:这个很好解释,指使用“”双引号或’’单引号包括的字符。例如:var comStr = 'this is string';
json字符串:指的是符合json格式要求的js字符串。例如:var jsonStr = "{StudentID:'100',Name:'tmac',Hometown:'usa'}";
json对象:指符合json格式要求的js对象。例如:var jsonObj = { StudentID: "100", Name: "tmac", Hometown: "usa" };
c++中使用json
服务器端:
/*************************************************************************
> File Name: service.cpp
> Author: 计献之
> Mail: 1841531744@qq.com
> Created Time: 2018年11月22日 星期四 12时20分37秒
************************************************************************/
#include<iostream>
using namespace std;
#include<iostream>
#include<string.h>
#include<string>
#include<arpa/inet.h>
#include<errno.h>
#include<json/json.h>
#include<sys/socket.h>
using namespace std;
//服务器端程序
int main()
{
//创建服务器
int fd = socket(AF_INET,SOCK_STREAM,0);
if(-1 == fd)
{
cerr<<"fd creat fail;errno:"<<endl;
return 0;
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(5000);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
//绑定
if(-1 == bind(fd,(struct sockaddr*)&addr,sizeof(addr)))
{
cerr<<"fd bind fail;errno:"<<errno<<endl;
return 0;
}
cout <<"监听"<<endl;
//监听
if(-1 == listen(fd,20))
{
cerr<<"fd listen fail;errno:"<<endl;
return 0;
}
//连接客户端
struct sockaddr_in caddr;
socklen_t len = sizeof(caddr);
int clifd = accept(fd,(struct sockaddr*)&caddr,&len);
if(-1 == clifd)
{
cerr<<"accept client fail;errno:"<<errno<<endl;
return 0;
}
//接收消息
char buff[100] = {0};
if(0 < recv(clifd,buff,99,0))
{
cout<<buff<<endl;
}
//解析json
Json::Value val;
Json::Reader read;
if(-1 == read.parse(buff,val))
{
cerr<<"read fail;errno:"<<errno<<endl;
return 0;
}
//cout<<val.asString()<<endl;
cout<<"name:"<<val["name"].asString()<<endl;
cout<<"pw:"<<val["pw"].asString()<<endl;
//发送消息
Json::Value root;
root["reason"] = "ok";
if(-1 == send(clifd,root.toStyledString().c_str(),strlen(root.toStyledString().c_str()),0))
{
cerr<<"send reason fail;errno:"<<errno<<endl;
return 0;
}
return 0;
}
客户端:
/*************************************************************************
> File Name: client.cpp
> Author: 计献之
> Mail: 1841531744@qq.com
> Created Time: 2018年11月22日 星期四 12时05分17秒
************************************************************************/
#include<iostream>
#include<string.h>
#include<string>
#include<json/json.h>
#include<json/value.h>
#include<json/autolink.h>
#include<json/config.h>
#include<json/features.h>
#include<json/forwards.h>
#include<json/reader.h>
#include<json/writer.h>
#include<errno.h>
#include<arpa/inet.h>
using namespace std;
//客户端程序
int main()
{
int fd = socket(AF_INET,SOCK_STREAM,0);
if(-1 == fd)
{
cerr<<"clifd creat fail;errnoz:"<<errno<<endl;
return 0;
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(5000);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
if(-1 == connect(fd,(struct sockaddr*)&addr,sizeof(addr)))
{
cerr<<"clifd connect fail;errno:"<<errno<<endl;
return 0;
}
//创建json包
Json::Value val;
val["name"] = "zhangsan";
val["pw"] = "123456";
//发送数据
if(-1 == send(fd,val.toStyledString().c_str(),strlen(val.toStyledString().c_str()),0))
{
cerr<<"send reason fail;errno:"<<errno<<endl;
return 0;
}
char buff[100] = {0};
//接收数据
if(0 < recv(fd,buff,99,0))
{
//解析json包
Json::Value root;
Json::Reader read;
if(-1 == read.parse(buff,root))
{
cerr<<"json prase fail;errno:"<<errno<<endl;
return 0;
}
//输出json
//cout<<root.asString()<<endl;
cout<<"reason:"<<root["reason"].asString()<<endl;
}
return 0;
}
编译链接: