阅读量:0
应用层:自定义网络协议:序列化和反序列化,如果是TCP传输的:还要关心区分报文边界(在序列化设计的时候设计好)——粘包问题
——两个问题:粘包、序列化反序列化
1、首先想要使用TCP协议传输的网络,服务器和客户端都应该要创建自己的套接字,因为两个都要创建,所以我们把套接字封装为一个类:
封装方法:设计模式:模版方法:先写一个模版类(基类),里面有各种函数,然后再写一个派生类里面有各种方法的实现,创建对象的时候
#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> #define Convert(addrptr) ((struct sockaddr *)addrptr) namespace Net_Work { const static int defaultsockfd = -1; const int backlog = 5; enum { SocketError = 1, BindError, ListenError, }; // 封装一个基类,Socket接口类 // 设计模式:模版方法类 class Socket { public: virtual ~Socket() {} virtual void CreateSocketOrDie() = 0; virtual void BindSocketOrDie(uint16_t port) = 0; virtual void ListenSocketOrDie(int backlog) = 0; virtual Socket *AcceptConnection(std::string *peerip, uint16_t *peerport) = 0; virtual bool ConnectServer(std::string &serverip, uint16_t serverport) = 0; virtual int GetSockFd() = 0; virtual void SetSockFd(int sockfd) = 0; virtual void CloseSocket() = 0; virtual bool Recv(std::string *buffer, int size) = 0; // TODO public: // 创建服务器端的套接字,并设置为监听状态监听套接字 void BuildListenSocketMethod(uint16_t port, int backlog) { CreateSocketOrDie(); BindSocketOrDie(port); ListenSocketOrDie(backlog); } // 创建客户端的套接字,并且申请链接 bool BuildConnectSocketMethod(std::string &serverip, uint16_t serverport) { CreateSocketOrDie(); return ConnectServer(serverip, serverport); } void BuildNormalSocketMethod(int sockfd) { SetSockFd(sockfd); } }; class TcpSocket : public Socket { public: TcpSocket(int sockfd = defaultsockfd) : _sockfd(sockfd) { } ~TcpSocket() { } void CreateSocketOrDie() override // 创建套接字 { _sockfd = ::socket(AF_INET, SOCK_STREAM, 0); if (_sockfd < 0) exit(SocketError); } void BindSocketOrDie(uint16_t port) override // 绑定套接字 { 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); int n = ::bind(_sockfd, Convert(&local), sizeof(local)); if (n < 0) exit(BindError); } void ListenSocketOrDie(int backlog) override // 设置套接字为监听状态 { int n = ::listen(_sockfd, backlog); if (n < 0) exit(ListenError); } // 获取链接套接字——服务器 Socket *AcceptConnection(std::string *peerip, uint16_t *peerport) override { struct sockaddr_in peer; socklen_t len = sizeof(peer); int newsockfd = ::accept(_sockfd, Convert(&peer), &len); if (newsockfd < 0) return nullptr; *peerport = ntohs(peer.sin_port); *peerip = inet_ntoa(peer.sin_addr); Socket *s = new TcpSocket(newsockfd); return s; } // 申请链接——客户端 bool ConnectServer(std::string &serverip, uint16_t serverport) override { struct sockaddr_in server; memset(&server, 0, sizeof(server)); server.sin_family = AF_INET; server.sin_addr.s_addr = inet_addr(serverip.c_str()); server.sin_port = htons(serverport); int n = ::connect(_sockfd, Convert(&server), sizeof(server)); if (n == 0) return true; else return false; } int GetSockFd() override { return _sockfd; } void SetSockFd(int sockfd) override { _sockfd = sockfd; } void CloseSocket() override { if (_sockfd > defaultsockfd) ::close(_sockfd); } bool Recv(std::string *buffer, int size)//接收消息到buffer中 { char inbuffer[size]; ssize_t n = recv(_sockfd, inbuffer, size - 1, 0); if (n > 0) { inbuffer[n] = 0; *buffer += inbuffer; return true; } else if (n == 0 || n < 0) return false; } private: int _sockfd; }; }
创建服务器:
1、创建套接字
2、把套接字设置为监听状态
3、获取连接,产生新的套接字
4、把新的套接字作为新线程的参数传到新线程执行的代码中,实现收发消息的操作
代码:
#pragma once #include"Socket.hpp" #include<pthread.h> #include<functional> using func_t=std::function<void(Net_Work::Socket* sockp)>; //服务器类 class TcpServer; class ThreadData { public: ThreadData(TcpServer*tcp_this, Net_Work::Socket *sockp): _this(tcp_this), _sockp(sockp) {} public: TcpServer *_this; Net_Work::Socket *_sockp; }; class TcpServer { public: TcpServer(uint16_t port, func_t handler_request):_port(port),_listensocket(new Net_Work::TcpSocket()),_handler_request(handler_request) { _listensocket->BuildListenSocketMethod(_port,Net_Work::backlog); } static void *ThreadRun(void *args) { pthread_detach(pthread_self());//分离线程 ThreadData *td=static_cast<ThreadData*>(args); td->_this->_handler_request(td->_sockp); td->_sockp->CloseSocket(); delete td->_sockp; delete td; return nullptr; } void Loop() { while(true) { //获取连接 std::string peerip; uint16_t peerport; Net_Work::Socket* newsock=_listensocket->AcceptConnection(&peerip,&peerport); if(newsock==nullptr) continue;; std::cout<<"获取了一个新链接,sockfd: "<<newsock->GetSockFd()<<"client info:"<< peerip<<":"<<peerport<<std::endl; //创建线程去完成此次sockfd收发 pthread_t tid; ThreadData *td=new ThreadData(this,newsock); pthread_create(&tid,nullptr,ThreadRun,td); } } ~TcpServer() { delete _listensocket; } private: int _port; Net_Work::Socket *_listensocket; public: func_t _handler_request; };
客户端:
2、TCP是向字节流(字符串)
序列化、反序列化
自定义协议就是定义双方都认识的结构化字段,并且协议中有序列化和反序列化的实现
先定义一个协议(结构化字段)——双方都能看到
设计模式:工厂模式:
客户端发送的结构体+序列化函数+反序列化函数
服务器反序列化的接收的结构体+反序列函数+序列化函数
代码:
#pragma once #include <iostream> #include <memory> namespace Protocol { const std::string ProtSep = " "; const std::string LineBreakSep = "\n"; // 封装为报文——序列化的一部分 std::string Encode(const std::string &message) //"x op y"或者"_result _code" { std::string len = std::to_string(message.size()); std::string package = len + LineBreakSep + message + LineBreakSep; //"len\n""x op y\n" return package; } // 解报——反序列化的一部分 bool Decode(std::string &package, std::string *message) //"len\n""x op y\n" { // 除了解报,我们还要判断是否认正确 auto pos = package.find(LineBreakSep); if (pos == std::string::npos) return false; std::string lens = package.substr(0, pos); int messagelen = std::stoi(lens); int total = lens.size() + messagelen + 2 * LineBreakSep.size(); if (package.size() < total) return false; // 至少有一个完整报文 *message = package.substr(pos + LineBreakSep.size(), messagelen); // 收到的报文可能是"len\n""x op y\n""len\n""x op y\n""len\n""x op y\n" // 所以我解报一个报文后,要删除这个报文,让后面的继续Dcode解报 package.erase(0, total); return true; } class Requset { public: Requset() : _data_x(0), _data_y(0), _oper(0) { } Requset(int x, int y, int op) : _data_x(x), _data_y(y), _oper(op) { } ~Requset() { } void Debug() { std::cout << "_data_x:" << _data_x << std::endl; std::cout << "_data_y:" << _data_y << std::endl; std::cout << "_oper:" << _oper << std::endl; } void Inc() { _data_x++; _data_y++; } // 自定义序列化协议:结构体数据->字符串 bool Serialize(std::string *out) { //"x op y" *out = std::to_string(_data_x) + ProtSep + _oper + ProtSep + std::to_string(_data_y); return true; } // 反序列化 : 字符串->结构体数据 bool Deserialize(std::string &in) //"x op y" { auto left = in.find(ProtSep); if (left == std::string::npos) return false; auto right = in.rfind(ProtSep); if (right == std::string::npos) return false; //[) _data_x = std::stoi(in.substr(0, left)); _data_y = std::stoi(in.substr(right + ProtSep.size())); std::string oper = in.substr(left + ProtSep.size(), right - (left + ProtSep.size())); if (oper.size() == 1) return false; _oper = oper[0]; return true; } int Getx() { return _data_x; } int Gety() { return _data_y; } char GetOper() { return _oper; } //_data_x+_data_y // 报文的自描述 //"len\n""x op y\n" private: int _data_x; // 第一个参数 int _data_y; // 第二个参数 char _oper; //+ - * / % }; class Response { public: Response(): _rseult(0), _code(0) { } Response(int result, int code) : _result(result), _code(code) { } // 自定义序列化协议:结构体数据->字符串 bool Serialize(std::string *out) { //"_result _code" *out = std::to_string(_result) + ProtSep + std::to_string(_code); return true; } // 反序列化:字符串->数据架构数据 bool Deserialize(std::string &in) //"_result _code" { auto pos = in.find(ProtSep); if (pos == std::string::npos) return false; _result = std::stoi(in.substr(0, pos)); _code = std::stoi(in.substr(pos + ProtSep.size())); return true; } void SetResult(int res) { _result = res; } void SetCode(int code){ _code=code;} private: int _result; // 运算结果 int _code; // 运算状态 }; // 简单的工厂模式,建造类设计模式 class Factory { public: std::shared_ptr<Requset> BulidRequest() { std::shared_ptr<Requset> req = std::make_shared<Requset>(); return req; } std::shared_ptr<Requset> BulidRequest(int x, int y, int op) { std::shared_ptr<Requset> req = std::make_shared<Requset>(x, y, op); return req; } std::shared_ptr<Response> BulidResponse() { std::shared_ptr<Response> resp = std::make_shared<Response>(); return resp; } std::shared_ptr<Response> BulidResponse(int result, int code) { std::shared_ptr<Response> resp = std::make_shared<Response>(result, code); return resp; } }; }
成熟的序列化反序列化:
Json:Value 万能类型
那么我们序列化和反序列化就可以用这样的:
总结:发送的数据为结构体,这个结构体就是我们自定义的协议,他的序列化反序列化 这些都是应用层的协议