中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁 > news >正文

動態(tài)網(wǎng)頁技術(shù)seo排名優(yōu)化公司

動態(tài)網(wǎng)頁技術(shù),seo排名優(yōu)化公司,長沙房地產(chǎn),wordpress同步谷歌博客模擬實現(xiàn)應(yīng)用層協(xié)議 文章目錄 模擬實現(xiàn)應(yīng)用層協(xié)議應(yīng)用層再談協(xié)議 序列化和反序列化 網(wǎng)絡(luò)版計算器自定義協(xié)議利用Json進(jìn)行序列化和反序列化json庫的安裝條件編譯 應(yīng)用層 應(yīng)用層(Application layer)是OSI模型的第七層。應(yīng)用層直接和應(yīng)用程序接口并提供常見…

模擬實現(xiàn)應(yīng)用層協(xié)議

文章目錄

  • 模擬實現(xiàn)應(yīng)用層協(xié)議
      • 應(yīng)用層
        • 再談協(xié)議
      • 序列化和反序列化
    • 網(wǎng)絡(luò)版計算器
      • 自定義協(xié)議
      • 利用Json進(jìn)行序列化和反序列化
        • json庫的安裝
        • 條件編譯

應(yīng)用層

image-20230829191153866

應(yīng)用層(Application layer)是OSI模型的第七層。應(yīng)用層直接和應(yīng)用程序接口并提供常見的網(wǎng)絡(luò)應(yīng)用服務(wù)。應(yīng)用層也向表示層發(fā)出請求。應(yīng)用層是開放系統(tǒng)的最高層,是直接為應(yīng)用進(jìn)程提供服務(wù)的。其作用是在實現(xiàn)多個系統(tǒng)應(yīng)用進(jìn)程相互通信的同時,完成一系列業(yè)務(wù)處理所需的服務(wù)。我們程序員寫的一個個解決我們實際問題, 滿足我們?nèi)粘P枨蟮木W(wǎng)絡(luò)程序, 都是在應(yīng)用層。

再談協(xié)議

運輸層為應(yīng)用進(jìn)程提供了端到端的通信服務(wù),但不同的網(wǎng)絡(luò)應(yīng)用的應(yīng)用進(jìn)程之間,還需要有不同的通信規(guī)則,因此在運輸層協(xié)議之上,還需要有應(yīng)用層協(xié)議。協(xié)議作為一種”約定“,那么必須遵守一些準(zhǔn)則。對應(yīng)應(yīng)用層協(xié)議一般要遵守:

  1. 應(yīng)用進(jìn)程交換的報文類型,如請求報文和響應(yīng)報文。
  2. 各種報文類型的語法,如報文中的各個字段及其詳細(xì)描述。
  3. 字段的語義,即包含在字段中的信息的含義。
  4. 進(jìn)程何時,如何發(fā)送報文,以及對報文進(jìn)行響應(yīng)的規(guī)則

序列化和反序列化

官方定義:序列化 (Serialization)是將對象的狀態(tài)信息轉(zhuǎn)換為可以存儲或傳輸?shù)男问降倪^程。在序列化期間,對象將其當(dāng)前狀態(tài)寫入到臨時或持久性存儲區(qū)。以后,可以通過從存儲區(qū)中讀取或反序列化對象的狀態(tài),重新創(chuàng)建該對象。

序列化有兩個用途:

  • 把對象的字節(jié)序列永久保存到硬盤上,通常存放在一個文件中(序列化對象)
  • 在網(wǎng)絡(luò)上傳送對象的字節(jié)序列(網(wǎng)絡(luò)傳輸對象)

實際上就是將數(shù)據(jù)持久化,防止一直存儲在內(nèi)存當(dāng)中,消耗內(nèi)存資源。而且序列化后也能更好的便于網(wǎng)絡(luò)運輸何傳播。

image-20230829223851219

例如在微信里你給對方發(fā)送一條消息,實際上會將頭像、昵稱、消息內(nèi)容、發(fā)送時間等構(gòu)建一個結(jié)構(gòu),然后將該結(jié)構(gòu)進(jìn)行序列化,即將該結(jié)構(gòu)形成一個字節(jié)流報文,通過網(wǎng)絡(luò)將該報文發(fā)送給對方,對方進(jìn)行反序列化,將該報文轉(zhuǎn)化為結(jié)構(gòu),然后重新拆解為頭像、昵稱、消息內(nèi)容、發(fā)送時間等。

而序列化還解決了網(wǎng)絡(luò)傳輸結(jié)構(gòu)體由于大小端、內(nèi)存對齊導(dǎo)致數(shù)據(jù)出錯等問題

網(wǎng)絡(luò)版計算器

現(xiàn)通過指定簡單的協(xié)議,實現(xiàn)一個服務(wù)器版的計算器。我們需要在客戶端把要計算的兩個數(shù)和運算符發(fā)過去, 然后由服務(wù)器進(jìn)行計算, 最 后再把結(jié)果返回給客戶端。

對應(yīng)網(wǎng)絡(luò)計算器約定協(xié)議:

  • 客戶端發(fā)送一個形如"1+1"的字符串
  • 這個字符串中有兩個操作數(shù)都是整形
  • 兩個操作數(shù)只間有一個運算符,操作數(shù)和運算符之間不能有空格

對應(yīng)網(wǎng)絡(luò)的序列化和反序列化:

  • 定義一個結(jié)構(gòu)體來表示我們需要交互的信息
  • 發(fā)送數(shù)據(jù)時將這個結(jié)構(gòu)體按照一個規(guī)則轉(zhuǎn)換成字符串, 接收到數(shù)據(jù)的時候再按照相同的規(guī)則把字符串轉(zhuǎn)化回結(jié)構(gòu)體。這個過程就稱為序列化和反序列化

在TCP協(xié)議中,如何保證接收方收到的是完整的報文呢?

image-20230829231938344

  • 我們調(diào)用的發(fā)送或接收函數(shù),本質(zhì)上是拷貝函數(shù)。

  • 應(yīng)用層調(diào)用的發(fā)送或接收函數(shù),并不是直接從網(wǎng)絡(luò)中讀取或發(fā)送數(shù)據(jù)。例如客戶端在應(yīng)用層調(diào)用發(fā)送函數(shù)發(fā)送數(shù)據(jù)時,是將數(shù)據(jù)從應(yīng)用層的發(fā)送緩沖區(qū)拷貝一份到傳輸層的發(fā)送緩沖區(qū)。然后由傳輸層自主決定何時將數(shù)據(jù)發(fā)送至網(wǎng)絡(luò)中,服務(wù)器的傳輸層再通過網(wǎng)絡(luò)將數(shù)據(jù)讀取到接收緩沖區(qū),然后將數(shù)據(jù)拷貝一份到應(yīng)用層的接收緩沖區(qū)。因此TCP協(xié)議是一種傳輸控制協(xié)議

  • 由于使用在TCP協(xié)議的雙方都有發(fā)送緩沖區(qū)和接收緩沖區(qū),即讀取數(shù)據(jù)和發(fā)送數(shù)據(jù)不會互相干擾,可以同時雙向傳輸數(shù)據(jù),因此TCP協(xié)議是一種全雙工的通信協(xié)議。

  • 由于TCP協(xié)議是一種全雙工的通信協(xié)議,因此也會產(chǎn)生客戶端發(fā)送數(shù)據(jù)的速度遠(yuǎn)遠(yuǎn)大于服務(wù)器讀取數(shù)據(jù)的速度,此時會造成服務(wù)器的接收緩沖區(qū)內(nèi)積攢大量的報文,這些報文是線性連接在一起的,那么如何將一條條報文完整的讀取上來呢?**通過指定的協(xié)議,按照協(xié)議規(guī)定的方式讀取上來。**協(xié)議定制的方式有:

    • 定長:規(guī)定該報文的長度
    • 間隔符號:規(guī)定報文之間存在間隔符號
    • 自描述方式:自定義協(xié)議

該網(wǎng)絡(luò)版計算器對應(yīng)的協(xié)議如下:

image-20230829235118910

  • 由于UDP協(xié)議發(fā)送和接收數(shù)據(jù)都是以數(shù)據(jù)報的形式,在傳輸過程中數(shù)據(jù)是完整的,因此并不需要通過序列和反序列化、對數(shù)據(jù)添加特殊內(nèi)容的方式去界定報文邊界;而TCP協(xié)議發(fā)送和接收數(shù)據(jù)是以字節(jié)流的方式,就必須使用相關(guān)協(xié)議手段去標(biāo)識、保護(hù)報文。

自定義協(xié)議

protocol.hpp

#pragma once
#include<iostream>
#include<string>
#include <sys/types.h>
#include <sys/socket.h>
#include <cstring>
using namespace std;#define SEP " "
#define SEP_LEN strlen(SEP)//strlen統(tǒng)計'\0'之前的字符個數(shù),而sizeof統(tǒng)計的是所占內(nèi)存的空間大小,使用sizeof會越界出問題
#define LINE_SEP "\r\n"
#define LINE_SEP_LEN strlen(LINE_SEP)enum {NONE=0,DIV_ZERO,MOD_ZERO,OP_ERR
};
//"x op y"->"text_len"\r\n"x op y"\r\n---給內(nèi)容加上報頭
std::string enLength(const std::string& text)//協(xié)議定制
{std::string send_str=to_string(text.size());send_str+=LINE_SEP;send_str+=text;send_str+=LINE_SEP;return send_str;
}
//"text_len"\r\n"x op y"\r\n -> "x op y"---去掉報頭,取出里面的內(nèi)容
bool deLength(const std::string& str,string* ret)//協(xié)議定制
{auto it=str.find(LINE_SEP);//找到報頭if(it==std::string::npos) return false;//如果沒找到則直接返回int len=stoi(str.substr(0,it));//取出字符串的長度*ret=str.substr(it+LINE_SEP_LEN,len);//取出數(shù)據(jù)return true;
}class Request
{
public:
Request():_x(0),_y(0),_op(0){}
Request(int x,int y,int op):_x(x),_y(y),_op(op){}bool Serialize(std::string* out)//序列化,將傳入的x op y轉(zhuǎn)化為字符串"x op y"
{*out="";*out+=to_string(_x);*out+=SEP;*out+=to_string(_op);*out+=SEP;*out+=to_string(_y);return true;
}bool Deserialize( const string& origin)//反序列化,將傳過來的字符串拆出來傳參給_x _op _y
{//"_xSEP_opSEP_y"-> _x,_op,_yauto leftit=origin.find(SEP);cout<<"Deserialize找到了leftSEP: "<<leftit<<endl;auto rightit=origin.rfind(SEP);cout<<"Deserialize找到了rightSEP: "<<rightit<<endl;if(leftit==string::npos|| rightit==string::npos) return false;if(leftit==rightit) return false;int opsize=rightit-leftit-1;cout<<"opsize: "<<opsize<<endl;
//1 43 1--leftit=1,rightit=4,opsize=rightit-leftit-1=4-1-1=2;
//1 3 1--leftit=1,right=3,opsize=rightit-leftit-1=3-1-1=1// if(rightit-(leftit+SEP_LEN)!=1) return false;if(rightit-(leftit+SEP_LEN)!=opsize) return false;//+號ASCII碼是43,從char轉(zhuǎn)int被解析成43即stringlen為兩位,這里的運算rightit-(leftit+SEP_LEN)!=1就出問題
//4-(1+1)==2;3-(1+1)=1std::string origin_x=origin.substr(0,leftit);std::string origin_y=origin.substr(rightit+SEP_LEN);if(origin_x.empty()) return false;if(origin_y.empty()) return false;cout<<"origin_x: "<<origin_x<<" origin_y: "<<origin_y<<endl;_x=stoi(origin_x);int opf=stoi(origin.substr(leftit,rightit));_op=opf;cout<<"opf: "<<opf<<"_op: "<<_op<<endl;_y=stoi(origin_y);return true;}public:int _x;int _y;char _op;
};class Response
{
public:
Response():_exitcode(0),_result(0){}
Response(int exitcode,int result):_exitcode(exitcode),_result(result){}
bool Serialize(string*out)//序列化
{//_exitcode _result ->"_exitcodeSEP_result"
*out="";
*out+=to_string(_exitcode);
*out+=SEP;
*out+=to_string(_result);return true;
}bool Deserialize(const string& in)//反序列化
{//_exitcodeSEP_result"->_exitcode _resultauto pos=in.find(SEP);
if(pos==string::npos) return false;string excstr=in.substr(0,pos);
string resstr=in.substr(pos+SEP_LEN);
if(excstr.empty()||resstr.empty()) return false;_exitcode=stoi(excstr);
_result=stoi(resstr);return true;}public:
int _exitcode;//退出碼
int _result;//結(jié)果
};//"text_len"\r\n"x op y"\r\n
bool recvPackage(int sock,string& inbuffer,string*out)
{
char buffer[1024];while(true)
{
ssize_t  s=recv(sock,buffer,sizeof(buffer)-1,0);
if(s>0)
{buffer[s]=0;inbuffer+=buffer;auto pos=inbuffer.find(LINE_SEP);if(pos==string::npos)continue;//沒找到報頭和有效載荷之間的分隔符---如果字節(jié)流式的報文沒讀全就繼續(xù)讀string text_len=inbuffer.substr(0,pos);//報頭是有效載荷的長度int len=stoi(text_len);int totallen=text_len.size()+LINE_SEP_LEN*2+len;//整個報文的長度if(inbuffer.size()<totallen) {cout<<"輸入的消息不完整,請繼續(xù)輸入.continue..."<<endl;continue;//報文沒讀完繼續(xù)讀}cout<<"處理前的inbuffer: \n"<<inbuffer<<endl;*out=inbuffer.substr(0,totallen);inbuffer.erase(0,totallen);cout<<"處理后的inbuffer: \n"<<inbuffer<<endl;break;
}
else return false;
}return true;}

介紹一下:

  • 在Request類內(nèi):
  1. 外部傳參兩個操作數(shù)和一個運算符進(jìn)來構(gòu)造Request對象,調(diào)用Serialize序列化函數(shù)構(gòu)造字符串"_xSEP_opSEP_y",其中SEP是空格, _x是左操作數(shù), _op是運算符, _y是右操作數(shù)
  2. 外部傳參數(shù)字符串"_xSEP_opSEP_y"進(jìn)來,調(diào)用Deserialize反序列化通過該字符串構(gòu)造內(nèi)嵌參數(shù) _x, _op, _y。注意的是,運算符 _op在傳入時會將符號轉(zhuǎn)化成其對應(yīng)的ASCII碼。例如"+“會被轉(zhuǎn)化成"43”,此時需要將字符串里的"43"取出來再轉(zhuǎn)化成整形43,方便后續(xù)轉(zhuǎn)化回"+"
  • 在Response類內(nèi):
  1. 外部傳字符串"_exitcodeSEP_result"進(jìn)來,調(diào)用Deserialize反序列化函數(shù)構(gòu)造內(nèi)嵌參數(shù) _exitcode和 _result。其中 exitcode是退出碼,當(dāng)計算結(jié)果正確時退出碼為NONE。計算時出現(xiàn)除零錯誤退出碼為DIV_ZERO。計算時出現(xiàn)商零錯誤退出碼為MOD_ZERO。傳入?yún)?shù)時運算符傳入錯誤,退出碼為OP_ERR; _result是計算結(jié)果。
  2. 調(diào)用Serialize序列化函數(shù)利用內(nèi)嵌參數(shù) _exitcode和 _result構(gòu)造字符串" _exitcodeSEP_result"
  • enLength協(xié)議定制函數(shù),給傳入的字符串加上"報頭",傳入字符串"xSEPopSEPy",加上"報頭后字符串為"text_len"\r\n"xSEPopSEPy"\r\n。其中"xSEPopSEPy"為有效載荷,text_len為有效載荷的長度,SEP是空格。

  • deLength協(xié)議定制函數(shù)。函數(shù)作用是給傳入的字符串str去"報頭",并把有效載荷通過ret傳出去。傳入的字符串是"text_len"\r\n"xSEPopSEPy"\r\n,去掉"報頭"后,取出字符串 “xSEPopSEPy”。其中SEP是空格。

  • recvPackage函數(shù)是供服務(wù)器調(diào)用接收數(shù)據(jù)包的函數(shù)。服務(wù)器接收客戶端發(fā)送來的報文,若報文不符合定義的協(xié)議形式或者沒讀上來的報文不是完整的則阻塞式讀取,直到讀上來完整的報文。然后通過輸出型參數(shù)out將報文傳出去。

calserver.cc


#include"calserver.hpp"
#include"log.hpp"
#include<iostream>
#include<stdlib.h>
#include<memory>
using namespace Server;
using namespace std;static void Usage(string proc)
{cout<<"\nUsage:\n\t"<<proc<<" local_port\n\n"<<endl;
}
//req是一個已經(jīng)處理的完整的對象
bool cal(const Request& req,Response& rep)
//根據(jù)req填充rep 
{
switch(req._op)
{case '+':rep._result=req._x+req._y;break;case '-':rep._result=req._x-req._y;break;    case '*':rep._result=req._x*req._y;break;    case '/':{if(req._y==0) rep._exitcode=DIV_ZERO;elserep._result=req._x/req._y;}   break;    case '%':{if(req._y==0) rep._exitcode=MOD_ZERO;elserep._result=req._x%req._y;}break;default:rep._exitcode=OP_ERR;break;
}
return true;}int main(int argc,char* argv[])
{
if(argc!=2)
{Usage(argv[0]);exit(USAGE_ERR);
}uint16_t port=atoi(argv[1]);//將字符串轉(zhuǎn)化為整數(shù)unique_ptr<calserver> ts(new calserver(port));
ts->initserver();
ts->start(cal);return 0;
}
  • cal是計算函數(shù),傳入Request對象和Response對象。Request對象中參數(shù)有兩個操作數(shù) _ x、_ y 和一個運算符 _ op,計算出結(jié)果放到Response對象的參數(shù)_result中,退出碼放到 _exitcode。

calserver.hpp

#pragma once
#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#include <pthread.h>
#include<functional>
#include"log.hpp"
#include"protocol.hpp"
#define NUM 1024using namespace std;
static const int gbacklog = 5;
namespace Server
{enum{USAGE_ERR=1,SOCK_ERR,BIND_ERR,LISTEN_ERR};typedef function<bool(const Request&req,Response& res)> func_t;void handlerentry(int sock,func_t func)
{string inbuffer;while(true){//1. 獲取客戶端發(fā)送來的數(shù)據(jù)報,確定數(shù)據(jù)報是帶報頭的數(shù)據(jù)報//"text_len"\r\n"x op y"\r\n string req_text,req_str;if(!recvPackage(sock,inbuffer,&req_text)) return;cout<<"帶報頭的請求(數(shù)據(jù)報): "<<req_text<<endl;//2.對數(shù)據(jù)報進(jìn)行反序列化//"text_len"\r\n"x op y"\r\n -> "x op y"---去掉報頭,取出里面的內(nèi)容if(!deLength(req_text,&req_str)) return;cout<<"去掉報頭的請求(數(shù)據(jù)報):"<<req_str<<endl;//走到這里再往下就卡主了,只打印到上面那條日志后面都沒打印到!!!//3.獲得一個結(jié)構(gòu)化的請求對象Request req;if(!req.Deserialize(req_str)) return;//如果反序列化失敗直接返回//4.對對象進(jìn)行操作---進(jìn)行服務(wù)器業(yè)務(wù)//4.1.獲得一個結(jié)構(gòu)化響應(yīng)Response rep;func(req,rep);//5.對對象進(jìn)行序列化//_exitcode _result ->"_exitcodeSEP_result"string rep_str;rep.Serialize(&rep_str);cout<<"計算完成后的響應(yīng): "<<rep_str<<endl;//6.給有效載荷加上報頭//"exitcode result" -> "content_len"\r\n"exitcode result"\r\nstring rep_text=enLength(rep_str);cout<<"加上報頭的完整響應(yīng)(報文): "<<rep_text<<endl;//7.把報文發(fā)送回給客戶端send(sock,rep_text.c_str(),rep_text.size(),0);}}
typedef function<bool(const Request&req,Response& res)> func_t;
class calserver
{public:
calserver(const uint16_t& port):_port(port),_listensock(-1){}void initserver()
{
//1.創(chuàng)建套接字
_listensock=socket(AF_INET,SOCK_STREAM,0);
if(_listensock<0)
{logMessage(FATAL,"create listensocket error");exit(SOCK_ERR);
}logMessage(NORMAL, "create socket success: %d", _listensock);
//2.bind ip和port
struct sockaddr_in local;
local.sin_family=AF_INET;
local.sin_port=htons(_port);
local.sin_addr.s_addr=INADDR_ANY;
if(bind(_listensock,(struct sockaddr*)&local,sizeof(local))<0)//綁定失敗
{logMessage(FATAL,"bind error");exit(BIND_ERR);
}logMessage(NORMAL,"bind success");
//3.將套接字設(shè)置為監(jiān)聽模式
if(listen(_listensock,gbacklog)<0)
{logMessage(FATAL,"listen error");exit(LISTEN_ERR);
}
logMessage(NORMAL,"listen success");
}
void start(func_t fun)
{while(true){struct sockaddr_in cli;socklen_t len=sizeof(cli);bzero(&cli,len);int sock=accept(_listensock,(struct sockaddr*)&cli,&len);if(sock<0){logMessage(FATAL,"accept client error");continue;}logMessage(NORMAL,"accept client success");cout<<"accept sock: "<<sock<<endl;//多進(jìn)程版---//一個客戶端占用一個文件描述符,原因在于孫子進(jìn)程執(zhí)行IO任務(wù)需要占用獨立的文件描述符,而文件描述符是繼承父進(jìn)程的,而每次客戶端進(jìn)來都要占用新的文件描述符//因此若接收多個客戶端不退出的話文件描述符會越來越少。pid_t id=fork();//創(chuàng)建子進(jìn)程if(id==0)//子進(jìn)程進(jìn)入{close(_listensock);//子進(jìn)程不需要用于監(jiān)聽因此關(guān)閉該文件描述符handlerentry(sock,fun);close(sock);exit(0);}//父進(jìn)程close(sock);pid_t ret=waitpid(id,nullptr,0);if(ret<0){cout << "waitsuccess: " << ret << endl;}}
}
~calserver(){}
private:
int _listensock;//用于監(jiān)聽服務(wù)器的sock文件描述符
uint16_t _port;//端口號
};}
  • handlerentry函數(shù)是供服務(wù)器調(diào)用的接收發(fā)送函數(shù)。傳入?yún)?shù)為用于通信的文件描述符sock,調(diào)用的計算函數(shù)cal(cal在calserver.cc文件中)。服務(wù)器調(diào)用handlerentry函數(shù),接收到客戶端發(fā)送來的報文,形式形如"text_len"\r\n"x op y"\r\n;調(diào)用deLength函數(shù)去"報頭",轉(zhuǎn)化后的字符串形如"x op y";調(diào)用Request對象的反序列函數(shù)Deserialize,通過字符串"x op y"填充Request對象的 _ x、_ op、 _ y參數(shù);調(diào)用func即cal函數(shù)利用Request對象內(nèi)的參數(shù)計算得出結(jié)果并構(gòu)造Response對象;調(diào)用Response對象的序列化函數(shù)將 參數(shù)_exitcode _result 轉(zhuǎn)化為字符串" _exitcodeSEP_result";調(diào)用enLength函數(shù)加上報頭,轉(zhuǎn)化后的字符串形如"content_len"\r\n"exitcode result"\r\n;然后通過send函數(shù)將字符串發(fā)送給客戶端。
  • 注意的是服務(wù)器是多進(jìn)程版,即能夠與多個客戶端進(jìn)行并行通信。

calclient.cc

#include<iostream>
#include<string>
#include<memory>
#include"calclient.hpp"
using namespace std;
using namespace client;
static void Usage(string proc)
{cout<<"\nUsage :\n\t"<<proc<<" serverip serverport\n"<<endl;
}
int main(int argc, char* argv[])
{if(argc!=3){Usage(argv[0]);exit(1);}string serverip=argv[1];
uint16_t serverport=atoi(argv[2]);unique_ptr<calclient> tc(new calclient(serverip,serverport));tc->initclient();
tc->start();return 0;
}

calclient.hpp

#pragma once
#include <iostream>
#include <string>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <ctype.h>
#include"protocol.hpp"
using namespace std;
#define NUM 1024
namespace client
{class calclient
{public:
calclient(const string& ip,const uint16_t& port)
:_sock(-1)
,_port(port)
,_ip(ip)
{}void initclient()
{
//1.創(chuàng)建sockfd
_sock=socket(AF_INET,SOCK_STREAM,0);
if(_sock<0)
{cerr<<"socket create error"<<endl;exit(2);
}
//2.綁定 ip port,不顯示綁定,OS自動綁定
}void start()
{
struct sockaddr_in ser;
bzero(&ser,sizeof(ser));
socklen_t len=sizeof(ser);
ser.sin_family=AF_INET;
ser.sin_port=htons(_port);
ser.sin_addr.s_addr=inet_addr(_ip.c_str());
if(connect(_sock,(struct sockaddr *)&ser,len)!=0)
{cerr<<"connect error"<<endl;
}else
{string line;string inbuffer;while(true){cout<<"mycal>>: ";//輸入"xopy"getline(cin,line);Request req=ParseLine(line);//用"xopy"取出x op y構(gòu)造Request對象string context;req.Serialize(&context);//序列化,用x op y構(gòu)造字符串"xSEPopSEPy"string send_str=enLength(context);//定制協(xié)議---"x op y"->"text_len"\r\n"x op y"\r\n---給內(nèi)容加上報頭cout<<"calclient send str: "<<send_str<<endl;send(_sock,send_str.c_str(),send_str.size(),0);//客戶端把報文發(fā)送給服務(wù)器string package;if(!recvPackage(_sock,inbuffer,&package)) continue;//服務(wù)器處理完數(shù)據(jù),客戶端接收服務(wù)器發(fā)送來的報文//  "content_len"\r\n"exitcode result"\r\nstring reser_len;if(!deLength(package,&reser_len)) continue;//去報頭//  "content_len"\r\n"exitcode result"\r\n -> "exitcode result"Response rep;rep.Deserialize(reser_len);//反序列化://_exitcodeSEP_result"->_exitcode _resultcout<<"_exitcode: "<<rep._exitcode<<endl;cout<<"_result: "<<rep._result<<endl;}
}
}~calclient()
{if(_sock>=0) close(_sock);
}Request ParseLine(const string& line)
{//"xopy"->取出來到x op y 上
int i=0;
int status=0;
int num=line.size();
string left,right;
char op;while(i<num)
{
switch(status)
{case 0:{if(!isdigit(line[i])){op=line[i];//取出運算符**status=1;}elseleft.push_back(line[i++]);//取出左操作數(shù)}break;case 1:i++;status=2;break;case 2:right.push_back(line[i++]);break;
}
}
cout<<"left: "<<stoi(left)<<" op: "<<op<<" right: "<<stoi(right)<<endl;
return Request(stoi(left),stoi(right),op);//返回Request對象}private:
int _sock;
uint16_t _port;
string _ip;};
}
  • ParseLine函數(shù)接收形如"1+1"的字符串,解析字符串后構(gòu)造Request對象
  • 客戶端調(diào)用start函數(shù),接收命令行發(fā)送來的形如"1+1"的字符串,然后調(diào)用ParseLine函數(shù)構(gòu)造Request對象。調(diào)用Request對象的序列化函數(shù)Serialize構(gòu)造字符串"xSEPopSEPy"。調(diào)用協(xié)議定制函數(shù)enLength給字符串加上"報頭",轉(zhuǎn)化后的字符串為"text_len"\r\n"x op y"\r\n;調(diào)用send函數(shù)將字符串發(fā)送給服務(wù)器;服務(wù)器計算完成后將結(jié)果發(fā)送回來,調(diào)用recvPackage函數(shù)接收服務(wù)器發(fā)送回來的字符串,字符串形如 “content_len”\r\n"exitcode result"\r\n;調(diào)用deLength函數(shù)去報頭,轉(zhuǎn)化后的字符串形如"exitcode result";調(diào)用Response對象的反序列化函數(shù)Deserialize通過字符串獲取參數(shù)_exitcode _result,并進(jìn)行打印。

makefile

.PHONY:all
all:calclient calservercalclient:calclient.ccg++ -o $@ $^ -std=c++11calserver:calserver.ccg++ -o $@ $^ -std=c++11 .PHONY:clean
clean:rm -rf calserver calclient

image-20230830121135167

利用Json進(jìn)行序列化和反序列化

JSON(JavaScript Object Notation)是一種輕量級的數(shù)據(jù)交換格式,它以易于閱讀和寫作的文本形式表示結(jié)構(gòu)化數(shù)據(jù)。JSON由兩種結(jié)構(gòu)構(gòu)成:鍵值對(鍵值對集合)和值的有序列表。在這里以鍵值對的方式使用。

json庫的安裝

輸入以下指令安裝

sudo yum install -y jsoncpp-devel

安裝完后可通過ls查詢

image-20230830151558485

makefile

cc=g++
LD=-DMYPRO
.PHONY:all
all:calclient calservercalclient:calclient.cc$(cc) -o $@ $^ -std=c++11 -ljsoncpp #${LD}calserver:calserver.cc$(cc) -o $@ $^ -std=c++11 -ljsoncpp #${LD}.PHONY:clean
clean:rm -rf calserver calclient
  • 如果不使用Json序列化就注釋掉第二行LD=-DMYPRO

protocol.hpp

#pragma once
#include<iostream>
#include<string>
#include <sys/types.h>
#include <sys/socket.h>
#include <cstring>
#include <jsoncpp/json/json.h>
using namespace std;#define SEP " "
#define SEP_LEN strlen(SEP)//strlen統(tǒng)計'\0'之前的字符個數(shù),而sizeof統(tǒng)計的是所占內(nèi)存的空間大小,使用sizeof會越界出問題
#define LINE_SEP "\r\n"
#define LINE_SEP_LEN strlen(LINE_SEP)enum {NONE=0,DIV_ZERO,MOD_ZERO,OP_ERR
};
//"x op y"->"text_len"\r\n"x op y"\r\n---給內(nèi)容加上報頭
std::string enLength(const std::string& text)//協(xié)議定制
{std::string send_str=to_string(text.size());send_str+=LINE_SEP;send_str+=text;send_str+=LINE_SEP;return send_str;
}
//"text_len"\r\n"x op y"\r\n -> "x op y"---去掉報頭,取出里面的內(nèi)容
bool deLength(const std::string& str,string* ret)//協(xié)議定制
{auto it=str.find(LINE_SEP);//找到報頭if(it==std::string::npos) return false;//如果沒找到則直接返回int len=stoi(str.substr(0,it));//取出字符串的長度*ret=str.substr(it+LINE_SEP_LEN,len);//取出數(shù)據(jù)return true;
}class Request
{
public:
Request():_x(0),_y(0),_op(0){}
Request(int x,int y,int op):_x(x),_y(y),_op(op){}bool Serialize(std::string* out)//序列化,將傳入的x op y轉(zhuǎn)化為字符串"x op y"
{
#ifdef MYPRO*out="";*out+=to_string(_x);*out+=SEP;*out+=to_string(_op);*out+=SEP;*out+=to_string(_y);
#else
Json::Value root;//json的對象是鍵值對[key,value]
root["first"]=_x;//int類型被設(shè)置進(jìn)json的鍵值對時自動轉(zhuǎn)換為string類型
root["second"]=_y;
root["oper"]=_op;Json::FastWriter writer;*out=writer.write(root);//調(diào)用接口序列化返回值為字符串#endifreturn true;
}bool Deserialize( const string& origin)//反序列化,將傳過來的字符串拆出來傳參給_x _op _y
{//"_xSEP_opSEP_y"-> _x,_op,_y#ifdef MYPROauto leftit=origin.find(SEP);cout<<"Deserialize找到了leftSEP: "<<leftit<<endl;auto rightit=origin.rfind(SEP);cout<<"Deserialize找到了rightSEP: "<<rightit<<endl;if(leftit==string::npos|| rightit==string::npos) return false;if(leftit==rightit) return false;int opsize=rightit-leftit-1;cout<<"opsize: "<<opsize<<endl;
//1 43 1--leftit=1,rightit=4,opsize=rightit-leftit-1=4-1-1=2;
//1 3 1--leftit=1,right=3,opsize=rightit-leftit-1=3-1-1=1// if(rightit-(leftit+SEP_LEN)!=1) return false;if(rightit-(leftit+SEP_LEN)!=opsize) return false;//+號ASCII碼是43,從char轉(zhuǎn)int被解析成43即stringlen為兩位,這里的運算rightit-(leftit+SEP_LEN)!=1就出問題
//4-(1+1)==2;3-(1+1)=1std::string origin_x=origin.substr(0,leftit);std::string origin_y=origin.substr(rightit+SEP_LEN);if(origin_x.empty()) return false;if(origin_y.empty()) return false;cout<<"origin_x: "<<origin_x<<" origin_y: "<<origin_y<<endl;_x=stoi(origin_x);int opf=stoi(origin.substr(leftit,rightit));_op=opf;cout<<"opf: "<<opf<<"_op: "<<_op<<endl;_y=stoi(origin_y);
#else
Json::Value root;
Json::Reader reader;
reader.parse(origin,root);//反序列化,將字符串中的協(xié)議字符串填進(jìn)對象對應(yīng)的元素中_x=root["first"].asInt();
_y=root["second"].asInt();
_op=root["oper"].asInt();
#endifreturn true;}public:int _x;int _y;char _op;
};class Response
{
public:
Response():_exitcode(0),_result(0){}
Response(int exitcode,int result):_exitcode(exitcode),_result(result){}
bool Serialize(string*out)//序列化
{//_exitcode _result ->"_exitcodeSEP_result"
#ifdef MYPRO
*out="";
*out+=to_string(_exitcode);
*out+=SEP;
*out+=to_string(_result);
#else
Json::Value root;
root["exitcode"]=_exitcode;
root["result"]=_result;Json::FastWriter writer;
*out= writer.write(root);
#endifreturn true;
}bool Deserialize(const string& in)//反序列化
{//_exitcodeSEP_result"->_exitcode _result#ifdef MYPRO
auto pos=in.find(SEP);
if(pos==string::npos) return false;string excstr=in.substr(0,pos);
string resstr=in.substr(pos+SEP_LEN);
if(excstr.empty()||resstr.empty()) return false;_exitcode=stoi(excstr);
_result=stoi(resstr);#else
Json::Value root;
Json::Reader reader;
reader.parse(in,root);
_exitcode=root["exitcode"].asInt();
_result=root["result"].asInt();
#endif
return true;}public:
int _exitcode;//退出碼
int _result;//結(jié)果
};//"text_len"\r\n"x op y"\r\n
bool recvPackage(int sock,string& inbuffer,string*out)
{
char buffer[1024];while(true)
{
ssize_t  s=recv(sock,buffer,sizeof(buffer)-1,0);
if(s>0)
{buffer[s]=0;inbuffer+=buffer;auto pos=inbuffer.find(LINE_SEP);if(pos==string::npos)continue;//沒找到報頭和有效載荷之間的分隔符---如果字節(jié)流式的報文沒讀全就繼續(xù)讀string text_len=inbuffer.substr(0,pos);//報頭是有效載荷的長度int len=stoi(text_len);int totallen=text_len.size()+LINE_SEP_LEN*2+len;//整個報文的長度if(inbuffer.size()<totallen) {cout<<"輸入的消息不完整,請繼續(xù)輸入.continue..."<<endl;continue;//報文沒讀完繼續(xù)讀}cout<<"處理前的inbuffer: \n"<<inbuffer<<endl;*out=inbuffer.substr(0,totallen);inbuffer.erase(0,totallen);cout<<"處理后的inbuffer: \n"<<inbuffer<<endl;break;
}
else return false;
}return true;}
  • Json序列化在Request類和Response類中使用。

條件編譯

  • #ifdef指令說明,如果預(yù)處理已經(jīng)定義了后面的標(biāo)識符(DEBUG),即DEBUG為真,則執(zhí)行 #ifdef 與 #else 之間的所有所有代碼,不執(zhí)行#else之后的代碼。若DEBUFG為未定義,即DEBUG為假,則執(zhí)行#else與#endif之間的代碼。#endif 用于結(jié)束該條件編譯指令。#ifdef和#endif搭配使用。

格式

#ifdef DEBUG
//......
#else
//......
#endif
//Request類內(nèi)的Json序列化片段-Serialize
Json::Value root;//json的對象是鍵值對[key,value]
root["first"]=_x;//int類型被設(shè)置進(jìn)json的鍵值對時自動轉(zhuǎn)換為string類型
root["second"]=_y;
root["oper"]=_op;Json::FastWriter writer;*out=writer.write(root);//調(diào)用接口序列化返回值為字符串
  • 創(chuàng)建一個Json的Value對象,鍵為"first"對應(yīng)的值為_x,將操作數(shù)和運算符設(shè)置進(jìn)Value對象里,然后通過Json的FastWriter對象調(diào)用write進(jìn)行序列化。
Request類內(nèi)的Json反序列化片段-Deserialize
Json::Value root;
Json::Reader reader;
reader.parse(origin,root);//反序列化,將字符串中的協(xié)議字符串填進(jìn)對象對應(yīng)的元素中_x=root["first"].asInt();
_y=root["second"].asInt();
_op=root["oper"].asInt();
  • 創(chuàng)建一個Json的Value對象,然后再創(chuàng)建一個Json的Reader對象,調(diào)用Reader對象的parse把攜帶協(xié)議的字符串填進(jìn)Value對象對應(yīng)的元素里。然后再通過鍵值對的方式把元素取出。

image-20230830150840323

http://m.risenshineclean.com/news/65049.html

相關(guān)文章:

  • 局域網(wǎng)內(nèi)個人網(wǎng)站建設(shè)軟文大全
  • 太原網(wǎng)站設(shè)計公司泉州關(guān)鍵詞快速排名
  • 交三百能在網(wǎng)站上找兼職做的地推十大推廣app平臺
  • 坪地網(wǎng)站建設(shè)包括哪些河源新聞最新消息
  • 淘客推廣平臺濟(jì)南網(wǎng)站優(yōu)化培訓(xùn)
  • 藍(lán)色腳手架織夢企業(yè)網(wǎng)站模板網(wǎng)站新站整站排名
  • 網(wǎng)站開發(fā)廣告語大全網(wǎng)絡(luò)營銷師課程
  • wordpress主題圖標(biāo)亂碼武漢seo搜索引擎優(yōu)化
  • 深圳市龍崗區(qū)疫情百度推廣優(yōu)化是什么?
  • 做網(wǎng)上商城網(wǎng)站設(shè)計靠譜的廣告聯(lián)盟
  • 哈爾濱做網(wǎng)站哈爾濱學(xué)院seovip培訓(xùn)
  • icp備案網(wǎng)站接入信息 ip地址段怎么創(chuàng)建個人網(wǎng)站
  • wordpress commentseo優(yōu)化人員
  • 深圳網(wǎng)站開發(fā)哪家服務(wù)專業(yè)怎么申請網(wǎng)站
  • 網(wǎng)站后臺用什么做優(yōu)化流程
  • 臨沂網(wǎng)站開發(fā)如何優(yōu)化企業(yè)網(wǎng)站
  • 東軟 網(wǎng)站群平臺建設(shè)怎么利用互聯(lián)網(wǎng)推廣
  • 專門做網(wǎng)站代購的盈利路子郴州seo
  • 武漢市有做網(wǎng)站的嗎營業(yè)推廣策劃方案
  • 電商網(wǎng)站有哪些功能模塊bt磁力bt天堂
  • 怎么制作一個app應(yīng)用佛山做seo推廣公司
  • 門戶網(wǎng)站cmssem競價代運營
  • 移動網(wǎng)站建設(shè)方面廊坊百度關(guān)鍵詞優(yōu)化
  • 網(wǎng)站開發(fā)怎樣建立后臺數(shù)據(jù)推廣產(chǎn)品的軟文怎么寫
  • 怎么搭建釣魚網(wǎng)站百度指數(shù)第一
  • 門戶網(wǎng)站如何運營百度電商推廣
  • 哪個網(wǎng)站做攻略比較好seo 網(wǎng)站優(yōu)化推廣排名教程
  • 新人做網(wǎng)站不懂設(shè)計版面怎么辦西安seo培訓(xùn)
  • 裝修公司網(wǎng)站dede模板seo網(wǎng)站優(yōu)化專家
  • 做海報賺錢的網(wǎng)站搜索引擎優(yōu)化指的是什么