文章目录
自顶向下去写,我们现在每次写的协议都是基于上一层的协议
序列化和反序列化 我们程序员写的一个一个程序,都是在应用层
我们发送的数据都是结构化的数据,这种结构话的数据就很好看也很好使用
1 2 3 4 5 6 7 struct message { 我的昵称:会跳的鹅 我的头像:唐老鸭.png 我的消息:在吗? 消息时间:2022 -07 -10 11 :31 :59 }
这就是一个结构化的消息,我们要把这个结构化的数据转化成一个从“字符串”,我们要把这个结构化的数据 “{昵称:xxx,头像:xxx,消息:xxx}”,把它传送到网络里面,到对端之后,再把它解析出来,读取到对应的结构体里面
序列化 :把结构化的数据转化为字符串的过程就是序列化的过程,
反序列化 :把字符串转化为结构体信息就是反序列化的过程,因为结构化的数据,再网络里面不方便传输,字符串便于网络传输
为什么要进行序列化和反序列化
为了应用层网络通信的方便
为了方便上层进行使用内部成员,将应用和网络进行解耦,我们只关心使用,不关心发送的过程
而我们之前使用的TCP和UDP是没有序列化的过程,我们必须要有结构化的数据 而这些序列化和反序列化的数据,它实际上就是协议的表现
我们自己写一个实现序列化和反序列化 也可以直接使用别人写好的组件,java里面(json,xml,protobuff)
我们这里就使用jsoncpp jsoncpp的安装
1 sudo yum install -y jsoncpp-devel
网络计算器 我们这里自己定制协议
没有使用组件
发送端口 伪代码
1 2 3 4 5 string x="123" ; string opt="+" ; string y="321" ; string data=x+opt+y;send (data);
接收端
1 2 3 4 5 6 7 8 9 10 11 12 recv (data); recv="123+321" ;int opt==recv.find ("+" ); string x=recv.substr (0 ,opt); string y=recv.substr (opt);int _x=to_int (x);int _y=to_int (y);int z=x+y; string _z=to_string (z)send (z)
这个方案,我们是把数据分开了 我们发现这个过程实际上是非常麻烦的 ,都由我们自己做就很麻烦
jsoncpp组件的使用 约定方案2
定义结构体 标识我们需要交互的信息
发送数据时将这个结构i体按照一个规则转化成字符串,接收数据的时候再按照相同的规则把字符串转化为结构体
这个过程就叫做“序列化”和“反序列化的过程”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 struct request { int x; int y; char op; }struct request req={10 ,20 ,"+" };write (sock,&req,sizeof (req));struct request req;read (sock,&req,sizeof (req));
jsontest 测试json
t e s t . c p p test.cpp test.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 #include <iostream> #include <string> #include <jsoncpp/json/json.h> using namespace std;struct request_t { int x; int y; char opt; request_t () = default ; }; int main () { request_t req={10 ,20 ,'*' }; Json::Value root; root["datax" ]=req.x; root["datay" ]=req.y; root["operator" ]=req.opt; Json::FastWriter writer; string json_string=writer.write (root); cout<<json_string<<endl; string jsontostruct=R"({"datax":10,"datay":20,"operator":42})" ; Json::Reader reader; Json::Value rooter; reader.parse (jsontostruct,rooter); request_t reqr; reqr.x=rooter["datax" ].asInt (); reqr.y=rooter["datay" ].asInt (); reqr.opt=(char )root["operator" ].asUInt (); cout<<reqr.opt<<endl; cout<<reqr.x<<reqr.opt<<reqr.y<<endl; return 0 ; }
网络在线计算器 S o c k . h p p Sock.hpp Sock.hpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 #include <iostream> #include <unistd.h> using namespace std;#include <sys/socket.h> #include <sys/types.h> #include <cstdlib> #include <arpa/inet.h> #include <netinet/in.h> #include <cstring> #include <string> class Sock {public : static int Socket () { int sockfd=socket (AF_INET,SOCK_STREAM,0 ); if (sockfd<0 ) { perror ("socket" ); exit (2 ); } return sockfd; } static void Listen (int sockfd) { if (listen (sockfd,5 )<0 ) { perror ("listen" ); exit (4 ); } } static void Bind (int sockfd,uint16_t port) { struct sockaddr_in local; memset (&local,0 ,sizeof (local)); local.sin_family=AF_INET; local.sin_addr.s_addr=INADDR_ANY; local.sin_port=htons (port); if (bind (sockfd,(struct sockaddr*)&local,sizeof (local))<0 ) { perror ("bind" ); exit (3 ); } } static int Accept (int sockfd) { struct sockaddr_in peer; bzero (&peer,0 ); socklen_t len=sizeof (peer); int fd=accept (sockfd,(struct sockaddr*)&peer,&len); if (fd<0 ) { perror ("accept" ); exit (5 ); } return fd; } static void Connect (int sockfd,string ip,int port) { struct sockaddr_in svr; memset (&svr,0 ,sizeof (svr)); svr.sin_family=AF_INET; svr.sin_port=htons (port); svr.sin_addr.s_addr=inet_addr (ip.c_str ()); if (connect (sockfd,(struct sockaddr*)&svr,sizeof (svr))<0 ) { perror ("connect" ); exit (6 ); } } };
p r o t o c o l . h p p protocol.hpp protocol.hpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 #pragma once #include <iostream> #include <string> #include <pthread.h> #include <jsoncpp/json/json.h> using namespace std;struct request_t { int x; int y; char opt; request_t () = default ; }; struct response_t { int code; int result; };request_t ---->string string ReqSerialize (const request_t &req) { Json::Value root; root["one" ] = req.x; root["two" ] = req.y; root["operator" ] = req.opt; Json::FastWriter writer; string sendwriter = writer.write (root); return sendwriter; }void ReqReSerialize (const string &jsonstring, request_t &req) { Json::Reader reader; Json::Value root; reader.parse (jsonstring, root); req.x = root["one" ].asInt (); req.y = root["two" ].asInt (); req.opt =(char ) root["operator" ].asUInt (); }response_t ----->string string RespSerialize (const response_t &resp) { Json::Value root; root["code" ]=resp.code; root["result" ]=resp.result; Json::FastWriter writer; string sendwriter = writer.write (root); return sendwriter; }void RespReSerialize (const string &jsonstring, response_t &resp) { Json::Reader reader; Json::Value root; reader.parse (jsonstring, root); resp.code = root["code" ].asInt (); resp.result = root["result" ].asInt (); }
c l i e n t . c c client.cc client.cc
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 #include "protocol.hpp" #include "Sock.hpp" int main (int argc, char *argv[]) { if (argc != 3 ) { cout << "ip+port" << endl; exit (1 ); } uint16_t port = atoi (argv[2 ]); int sockfd = Sock::Socket (); Sock::Connect (sockfd, argv[1 ], port); request_t req; cout << "Please Enter Data One# " ; cin >> req.x; cout << "Please Enter Data Two# " ; cin >> req.y; cout << "Please Enter Data Opt# " ; cin >> req.opt; string sendwriter=ReqSerialize (req); write (sockfd, sendwriter.c_str (), sendwriter.size ()); char buf[1024 ]; ssize_t s = read (sockfd, buf, sizeof (buf) - 1 ); response_t resp; if (s > 0 ) { buf[s] = 0 ; string msg = buf; RespReSerialize (msg, resp); cout << "code[0:success]: " << resp.code; cout << "result " << resp.result << endl; } else { exit (1 ); } return 0 ; }
s e r v e r . c c server.cc server.cc
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 #include "protocol.hpp" #include "Sock.hpp" void *HandlerRequest (void *args) { pthread_detach (pthread_self ()); int sockfd = *(int *)args; delete args; char buf[1024 ]; ssize_t s = read (sockfd, buf, sizeof (buf) - 1 ); if (s < 0 ) { cout << "error" << endl; close (sockfd); } else if (s == 0 ) { cout << "client quit..." << endl; close (sockfd); } else { buf[s] = 0 ; string msg = buf; request_t req; ReqReSerialize (msg, req); response_t resp = {0 , 0 }; switch (req.opt) { case '+' : resp.result = req.x + req.y; break ; case '-' : resp.result = req.x - req.y; break ; case '*' : resp.result = req.x * req.y; break ; case '/' : if (req.y == 0 ) resp.code = -1 ; else resp.result = req.x / req.y; break ; case '%' : if (req.y == 0 ) resp.code = -2 ; else resp.result = req.x % req.y; break ; default : resp.code = -3 ; break ; } cout << "request " << req.x << req.opt << req.y << endl; cout<<"response " <<resp.result<<endl; string send_msg = RespSerialize (resp); cout<<send_msg<<endl; write (sockfd, send_msg.c_str (), send_msg.size ()); cout << "server finish" << endl; } }int main (int argc, char *argv[]) { if (argc != 2 ) { cout << "ip" << endl; exit (1 ); } uint16_t port = atoi (argv[1 ]); int sockfd = Sock::Socket (); Sock::Bind (sockfd, port); Sock::Listen (sockfd); while (true ) { int newsockfd = Sock::Accept (sockfd); if (newsockfd < 0 ) { continue ; } pthread_t tid; int *pram = new int (newsockfd); pthread_create (&tid, nullptr , HandlerRequest, (void *)pram); } return 0 ; }
我们刚刚写的cs模式的在线版本计算器,本质上是一个应用层网络服务
基本通信代码是我们自己写的————————(会话层:进行通信管理)
序列化和反序列化时我们使用组件完成的————(表示层:设备固有数据格式和网络标准数据格式的转换)
请求,结果格式,code含义,等约定是我们自己写的————(针对特定应用的协议)
业务逻辑(计算也是我们自己写的)
HTTP协议,本质上,在定位上和我们刚刚写的网络计算机,没有区别,都是应用层协议
网络通信
序列化和反序列化
协议细节 http协议把这3点都实现了