自定义应用层协议
例子:网络版本计算器
序列化反序列化
序列化:将消息,昵称,日期整合成消息-昵称-日期
反序列化:消息-昵称-日期->消息,昵称,日期
在序列化中,定义一个结构体
struct Message{
string message;
string name;
string data;};
用这个结构体进行通信,就是一种协议,约定好的一种结构化字段就叫做协议
再比如
struct complate{
int num1;
int num2;
char oper;
}
序列化:将协议规定的结构化数据转换成字符串字节流 为什么?发送效率变高
反序列化:将字符串字节流转换成结构化数据 为什么?方便与提取有效字段
协议定制procotol
1.直接传递struct
可以,但使用范围很小
2.自己实现序列化反序列化
前备
客户端发送到服务端,send/write函数其实是将自己的缓冲区拷贝到发送的缓冲区,并没有经过网络。发送缓冲区的数据,发多少,怎么发,出错怎么办,一致由Tcp协议决定,Tcp实际通信的时候,是由双方操作系统之间进行通信,同理read/recv也是拷贝函数,将接收缓冲区拷贝到用户缓冲区。
既然send/write是拷贝函数,也就是说当接收缓冲区为空,发送缓冲区为满,双方进程都会被阻塞住。
Tcp为什么是全双工通信协议?
当客户端进行发送,服务端进行接收的同时,服务端也可以进行发送,客户端进行接收
当我们发送helloworld的时候,对方一定收到helloworld吗?
不一定,当服务端的接受缓冲区只剩5个字节了,客户端以为自己发了10个字节,但事实上服务端此时只能读取到5个字节。这种特点我们叫做面向字节流
所以我们得明确报文和报文之间的边界
我们要解决的问题
a.解决结构化数据的序列化和反序列化
本质上是把结构化字段转换为字符串
b.解决用户区分报文边界问题
加入\n进行判断
namespace Proc_wrok
{
const string Seq = " ";
const string LineSep = "\n";
//a op b
//len\na op b\n
void Encode(string & message)
{
int len=message.size();
message=to_string(len)+LineSep+message+LineSep;
}
//len\na op b\n
//len
//len\n
//len\na op b
//len\na op b\n
//len\na op b\nlen\n
//a op b
bool Decode(string & Package,string * message)
{
auto LineSepPos=Package.find("\n");
if(LineSepPos==string::npos) return false;
int messagelen=stoi(Package.substr(0,LineSepPos));
int lensize=(Package.substr(0,LineSepPos)).size();
//没有完整的报文
int total_size=messagelen+LineSep.size()*2+lensize;
if(Package.size()<total_size) return false;
//有完整的报文
*message=Package.substr(LineSepPos+LineSep.size(),messagelen);
//cout<<"有完整的报文:"<<*message<<endl;
Package.erase(0,total_size);
return true;
}
class Request
{
public:
Request() {}
Request(int data_a, int data_b, char oper)
: _data_a(data_a), _data_b(data_b), _oper(oper)
{
}
void PrintDebug()
{
cout << "[" << _data_a << "]"
<< _oper
<< "[" << _data_b << "]"
<< "=?" << endl;
}
void testDebug()
{
_data_a++;
_data_b++;
}
// a op b
void Serializa(string *message)
{
string left = to_string(_data_a);
string right = to_string(_data_b);
*message = left + Seq + _oper + Seq + right;
}
// a op b
bool DesSerializa(string &message)
{
auto left = message.find(Seq);
if (left == string::npos)
return false;
auto right = message.rfind(Seq);
if (right == string::npos)
return false;
string numa = message.substr(0, left);
string oper = message.substr(left + 1, right - (left + 1));
if (oper.size() != 1)
{
cout << "Sep error" << endl;
return false;
}
string numb = message.substr(right + 1);
_data_a = stoi(numa);
_data_b = stoi(numb);
_oper = oper[0];
return true;
}
int GetA()
{
return _data_a;
}
int GetB()
{
return _data_b;
}
char GetOper()
{
return _oper;
}
private:
int _data_a;
int _data_b;
char _oper;
};
class Result
{
public:
Result() {}
Result(int result, int code)
: _result(result), _code(code)
{
}
void Serializa(string *message)
{
string left = to_string(_result);
string right = to_string(_code);
*message = left + Seq + right;
}
// result code
bool DesSerializa(string &message)
{
auto mid = message.find(Seq);
if (mid == string::npos)
return false;
string result = message.substr(0, mid);
string code = message.substr(mid + 1);
_result = stoi(result);
_code = stoi(code);
return true;
}
int GetResult()
{
return _result;
}
int GetCode()
{
return _code;
}
private:
int _result;
int _code;
};
}
3.引入成熟的序列化反序列化
JSON
sudo yum install jsoncpp-devel 安装Jsoncpp库
JSON 版本的序列化与反序列化
void Serializa(string *message)
{
Json::Value root;
Json::FastWriter writer;
root["_data_a"]=_data_a;
root["_data_b"]=_data_b;
root["_oper"]=_oper;
*message=writer.write(root);
}
// a op b
bool DesSerializa(string &message)
{
Json::Value root;
Json::Reader reader;
bool retbool=reader.parse(message,root);
_data_a=root["_data_a"].asInt();
_data_b=root["_data_b"].asInt();
_oper=(char)(root["_oper"].asInt());
return retbool;
}