应用层:自定义网络协议:序列化和反序列化,如果是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 万能类型
那么我们序列化和反序列化就可以用这样的:
总结:发送的数据为结构体,这个结构体就是我们自定义的协议,他的序列化反序列化 这些都是应用层的协议