1.重新理解read,write,recv,send和Tcp为什么支持全双工

- 在应用层缓冲区,用户层缓冲区通过write并不是把数据发送到网络中,而是把用户空间拷贝到发送缓冲区中,write直接返回。
- 发送缓冲区,什么时候发,发多少,出错了怎么办,由传输层TCP自己维护,所以TCP叫做传输控制协议。
- write本质是一个拷贝函数,接收发送缓冲区由内核维护。
- OS使用TCP逻辑,把发送缓冲区中的数据,发送到对方的接收缓冲区中。
- 当主机b调用read接口,如果接收缓冲区中有数据,就会把数据拷贝到用户层缓冲区,read函数本质也是拷贝函数,如果接收缓冲区中没有数据,read就会阻塞。
- 如果write写的很快,发送的很慢,把发送缓冲区写满了,write就会阻塞。
- 通信过程:用户A -> 用户缓冲区 ->系统调用->发送缓冲区 ->协议栈封包 ->网络->协议栈解包->接收缓冲区->系统调用->用户B缓冲区->用户B。
- 本质是双方的操作系统在进行网络通信。
总结:
- 用户只负责把数据给OS,OS进行数据通信,与其说我们是在进行网络编程,不如说在做文件操作,本质网络通信就是拷贝行为。
- tcp socket支持全双工:通信双方有一对缓冲区,发送的同时可以接收,反之也可(多线程)。
系统级认识:用户往缓冲区中放数据,操作系统从缓冲区中拿数据,典型的生产者消费者模型,两者由用户和内核承担;当缓冲区里没有数据了read就会阻塞住,发送缓冲区满了,write就会被阻塞,阻塞本质是用户与内核在进行同步过程(条件变量cond),有数据后read就会被唤醒(软件中断)。
操作系统从网络中获取报文,会有很多报文来不及拷贝到接收缓冲区,在OS中会同时存在很多的报文,那么OS就要对报文进行管理(先描述,再组织)。
OS中报文数据结构

- 报文通过指针移动添加报头


- 传输层的接收缓冲区和发送缓冲区,处于sock结构体中
2.应用层
2.1自定义协议的目的
- 用户使用write向发送缓冲区中拷贝字符串后,操作系统根据TCP可能只发送了该字符串的一部分,OS是面向字节流的,OS并不会关心发送的字符串是否完整,只会按照自己的规则发送数据。
- 接收方read读上了的字符串可能只是对方发送数据的一部分。
- 通信双方读写的数据不一定完全匹配,所以保证数据完整性的工作由上层用户自己做,比如:通信双方规定发送接收一次的数据必须是10字节,如果不够10字节,就不做处理,并且规定数据之间的分割符。
这就是一种通信双方的约定,协议也是约定,所以为了保证读取数据的完整性,通信双方在应用层自定义协议。
数据粘包:接收数据不完整。
2.2应用层自定义协议
2.2.1网络版本计算器
约定方案一:
- 客户端发送一个形如"1+2"的字符串;
- 这个字符串中有两个操作数,都是整形;
- 两个数字之间会有一个字符是运算符,运算符只能是+;
- 数字和运算符之间没有空格
约定方案二:
- 定义结构体来表示我们需要交互的信息;
- 发送数据时将这个结构体按照一个规则转换成字符串,接收到数据的时候再按照相同的规则把字符串转化回结构体;
- 这个过程叫做"序列化"和"反序列化"
2.3序列化与反序列化

- 序列化和反序列化可以使代码具有很好的扩展性。
- 数据序列化之后,还要给整个序列化字符串加上报头,比如报头大小4字节,保存整个字符串的长度,读取时必须先读取4字节,之后要读取多少个字节,长度也就有了,读不到该长度,就不给上层处理,读完后,反序列化交给上层处理。
- 添加报头解决粘包问题。
3.代码实现
recv

- 和read函数使用方面几乎完全一样
- 返回值:>0:读取了多少字节数;=0:连接断开;<0:读取出错
- flags:0表示阻塞读取
send

- 使用方法和write一样,多了一个参数,选择是否阻塞写。
Calculator.hpp
cpp
#include <iostream>
#include <memory>
#include "Socket.hpp"
#include "Protocol.hpp"
#include "InetAddr.hpp"
using namespace NS_SOCKET_MODULE;
static void Usage(const std::string &proc)
{
std::cout << "Usage:\n\r" << proc << "server_ip server_port" << std::endl;
}
static void HandlerResponse(Response &resp)
{
std::cout << "result: " << resp._result << "[" << resp._code << "]" << std::endl;
}
int main(int argc, char *argv[])
{
if (argc != 3)
{
Usage(argv[0]);
exit(1);
}
std::string server_ip = argv[1];
uint16_t server_port = std::stoi(argv[2]);
std::unique_ptr<Socket> socket = std::make_unique<TcpSocket>();
socket->BuildTcpClientSockMethod();
InetAddr serveraddress(server_port, server_ip);
bool n = socket->Connect(serveraddress);
if (!n)
{
std::cerr << "connect error:" << serveraddress.ToString() << std::endl;
exit(2);
}
Protocol protocol(HandlerResponse);
std::string inbuffer;
while (true)
{
int cnt = 3;
std::string outbuffer;
while (cnt--)
{
// 0.获取数据
int x, y;
char oper;
std::cout << "enter your x:";
std::cin >> x;
std::cout << "enter your y:";
std::cin >> y;
std::cout << "enter your oper:";
std::cin >> oper;
// 1.定义请求对象
Request req(x, y, oper);
// 2.序列化
std::string req_json;
req.Serialize(&req_json);
// 3.封装报头
std::string send_req_string = protocol.Packet(req_json);
outbuffer+=send_req_string;
}
std::cout<<"\n"<<outbuffer<<std::endl;
// 4.发送
socket->Send(outbuffer);
// 5.接收
socket->Recv(&inbuffer);
// 6.解析
protocol.ParseResponse(inbuffer);
}
// Protocol protocol(HandlerResponse);
// std::string inbuffer;
// while(true)
// {
// //0.获取数据
// int x,y;
// char oper;
// std::cout<<"enter your x:";
// std::cin>>x;
// std::cout<<"enter your y:";
// std::cin>>y;
// std::cout<<"enter your oper:";
// std::cin>>oper;
// //1.定义请求对象
// Request req(x,y,oper);
// //2.序列化
// std::string req_json;
// req.Serialize(&req_json);
// //3.封装报头
// std::string send_req_string = protocol.Packet(req_json);
// //4.发送
// socket->Send(send_req_string);
// //5.接收
// socket->Recv(&inbuffer);
// //6.解析
// protocol.ParseResponse(inbuffer);
// }
socket->Close();
return 0;
}
InetAddr.hpp
cpp
#pragma once
#include <iostream>
#include <string>
#include <strings.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define CONV(address) ((struct sockaddr *)address)
// 对客户端进行先描述
class InetAddr
{
public:
InetAddr()
{
}
InetAddr(const struct sockaddr_in &address) : _address(address), _len(sizeof(address))
{
// _ip = inet_ntoa(_address.sin_addr);
char ipstr[32];
inet_ntop(AF_INET, &(_address.sin_addr), ipstr, sizeof(ipstr));
_ip = ipstr;
_port = ntohs(_address.sin_port);
}
InetAddr(uint16_t port, const std::string &ip = "0.0.0.0") : _ip(ip), _port(port)
{
bzero(&_address, sizeof(_address));
_address.sin_family = AF_INET;
_address.sin_port = htons(_port); // h->n
//_address.sin_addr.s_addr = inet_addr(_ip.c_str()); // 1. 字符串ip->4字节IP 2. hton
inet_pton(AF_INET, ip.c_str(), &(_address.sin_addr));
_len = sizeof(_address);
}
bool operator==(const InetAddr &addr)
{
return (this->_ip == addr._ip) && (this->_port == addr._port);
}
void operator=(struct sockaddr_in &addr)
{
_address = addr;
char ipstr[32];
inet_ntop(AF_INET, &(_address.sin_addr), ipstr, sizeof(ipstr));
_ip = ipstr;
_port = ntohs(_address.sin_port);
}
std::string ToString()
{
return "[" + _ip + ":" + std::to_string(_port) + "]";
}
struct sockaddr *NetAddress()
{
return CONV(&_address);
}
socklen_t Len()
{
return _len;
}
~InetAddr()
{
}
private:
// net address
struct sockaddr_in _address;
socklen_t _len;
// host address
std::string _ip;
uint16_t _port;
};
Logger.hpp
cpp
#ifndef __LOGGER_HPP
#define __LOGGER_HPP
#include <iostream>
#include <cstdio>
#include <string>
#include <memory>
#include <sstream>
#include <ctime>
#include <sys/time.h>
#include <unistd.h>
#include <filesystem> // C++17
#include <fstream>
#include "Mutex.hpp"
namespace NS_LOG_MODULE
{
enum class LogLevel
{
INFO,
WARNING,
ERROR,
FATAL,
DEBUG
};
std::string LogLevel2Message(LogLevel level)
{
switch (level)
{
case LogLevel::INFO:
return "INFO";
case LogLevel::WARNING:
return "WARNING";
case LogLevel::ERROR:
return "ERROR";
case LogLevel::FATAL:
return "FATAL";
case LogLevel::DEBUG:
return "DEBUG";
default:
return "UNKNOWN";
}
}
// 1. 时间戳 2. 日期+时间
std::string GetCurrentTime()
{
struct timeval current_time;
int n = gettimeofday(¤t_time, nullptr);
(void)n;
// current_time.tv_sec; current_time.tv_usec;
struct tm struct_time;
localtime_r(&(current_time.tv_sec), &struct_time); // r: 可重入函数
char timestr[128];
snprintf(timestr, sizeof(timestr), "%04d-%02d-%02d %02d:%02d:%02d.%ld",
struct_time.tm_year + 1900,
struct_time.tm_mon + 1,
struct_time.tm_mday,
struct_time.tm_hour,
struct_time.tm_min,
struct_time.tm_sec,
current_time.tv_usec);
return timestr;
}
// 输出角度 -- 刷新策略
// 1. 显示器打印
// 2. 文件写入
// 策略模式,策略接口
class LogStrategy
{
public:
virtual ~LogStrategy() = default;
virtual void SyncLog(const std::string &message) = 0;
};
// 控制台日志刷新策略, 日志将来要向显示器打印
class ConsoleStrategy : public LogStrategy
{
public:
void SyncLog(const std::string &message) override
{
LockGuard lockguard(_mutex);
std::cerr << message << std::endl; // ??
}
~ConsoleStrategy()
{
}
private:
Mutex _mutex;
};
const std::string defaultpath = "./log";
const std::string defaultfilename = "log.txt";
// 文件策略
class FileLogStrategy : public LogStrategy
{
public:
FileLogStrategy(const std::string &path = defaultpath, const std::string &name = defaultfilename)
: _logpath(path),
_logfilename(name)
{
LockGuard lockguard(_mutex);
if (std::filesystem::exists(_logpath))
return;
try
{
std::filesystem::create_directories(_logpath);
}
catch (const std::filesystem::filesystem_error &e)
{
std::cerr << e.what() << '\n';
}
}
void SyncLog(const std::string &message) override
{
{
LockGuard lockguard(_mutex);
if (!_logpath.empty() && _logpath.back() != '/')
{
_logpath += "/";
}
std::string targetlog = _logpath + _logfilename; // "./log/log.txt"
std::ofstream out(targetlog, std::ios::app); // 追加方式写入
if (!out.is_open())
{
std::cerr << "open " << targetlog << "failed" << std::endl;
return;
}
out << message << "\n";
out.close();
}
}
~FileLogStrategy()
{
}
private:
std::string _logpath;
std::string _logfilename;
Mutex _mutex;
};
// 交给大家
// const std::string defaultfilename = "log.info";
// const std::string defaultfilename = "log.warning";
// const std::string defaultfilename = "log.fatal";
// const std::string defaultfilename = "log.error";
// const std::string defaultfilename = "log.debug";
// 文件策略&&分日志等级来进行保存
// class FileLogLevelStrategy : public LogStrategy
// {
// public:
// private:
// };
// 日志类:
// 1. 日志的生成
// 2. 根据不同的策略,进行刷新
class Logger
{
// 日志的生成:
// 构建日志字符串
public:
Logger()
{
UseConsoleStrategy();
}
void UseConsoleStrategy()
{
_strategy = std::make_unique<ConsoleStrategy>();
}
void UseFileStrategy()
{
_strategy = std::make_unique<FileLogStrategy>();
}
// 内部类, 标识一条完整的日志信息
// 一条完整的日志信息 = 做半部分固定部分 + 右半部分不固定部分
// LogMessage RAII风格的方式,进行刷新
class LogMessage
{
public:
LogMessage(LogLevel level, std::string &filename, int line, Logger &logger)
: _level(level),
_curr_time(GetCurrentTime()),
_pid(getpid()),
_filename(filename),
_line(line),
_logger(logger)
{
// 先构建出来左半部分
std::stringstream ss;
ss << "[" << _curr_time << "] "
<< "[" << LogLevel2Message(_level) << "] "
<< "[" << _pid << "] "
<< "[" << _filename << "] "
<< "[" << _line << "] "
<< " - ";
_loginfo = ss.str();
}
template <typename T>
LogMessage &operator<<(const T &info)
{
std::stringstream ss;
ss << info;
_loginfo += ss.str();
return *this; // 返回当前LogMessage对象,方便下次继续进行<<
}
~LogMessage()
{
if (_logger._strategy)
{
_logger._strategy->SyncLog(_loginfo);
}
}
private:
LogLevel _level;
std::string _curr_time;
pid_t _pid;
std::string _filename;
int _line;
std::string _loginfo; // 一条完整的日志信息
// 一个引用,引用外部的Logger类对象
Logger &_logger; // 方便我们后续进行策略式刷新
};
// 这里已经不是内部类了
// 故意采用拷贝LogMessage
LogMessage operator()(LogLevel level, std::string filename, int line)
{
return LogMessage(level, filename, line, *this);
}
~Logger()
{
}
private:
std::unique_ptr<LogStrategy> _strategy; // 刷新策略
};
// 日志对象,全局使用
Logger logger;
#define ENABLE_CONSOLE_LOG_STRATEGY() logger.UseConsoleStrategy();
#define ENABLE_FILE_LOG_STRATEGY() logger.UseFileStrategy();
#define LOG(level) logger(level, __FILE__, __LINE__)
}
#endif
Mutex.hpp
cpp
#pragma once
#include <iostream>
#include <pthread.h>
class Mutex
{
public:
Mutex()
{
pthread_mutex_init(&_lock, nullptr);
}
void Lock()
{
pthread_mutex_lock(&_lock);
}
pthread_mutex_t *Ptr()
{
return &_lock;
}
void Unlock()
{
pthread_mutex_unlock(&_lock);
}
~Mutex()
{
pthread_mutex_destroy(&_lock);
}
private:
pthread_mutex_t _lock;
};
class LockGuard // RAII风格代码
{
public:
LockGuard(Mutex &lock):_lockref(lock)
{
_lockref.Lock();
}
~LockGuard()
{
_lockref.Unlock();
}
private:
Mutex &_lockref;
};
NetCalClient.cc
cpp
#include <iostream>
#include <memory>
#include "Socket.hpp"
#include "Protocol.hpp"
#include "InetAddr.hpp"
using namespace NS_SOCKET_MODULE;
static void Usage(const std::string &proc)
{
std::cout << "Usage:\n\r" << proc << "server_ip server_port" << std::endl;
}
static void HandlerResponse(Response &resp)
{
std::cout << "result: " << resp._result << "[" << resp._code << "]" << std::endl;
}
int main(int argc, char *argv[])
{
if (argc != 3)
{
Usage(argv[0]);
exit(1);
}
std::string server_ip = argv[1];
uint16_t server_port = std::stoi(argv[2]);
std::unique_ptr<Socket> socket = std::make_unique<TcpSocket>();
socket->BuildTcpClientSockMethod();
InetAddr serveraddress(server_port, server_ip);
bool n = socket->Connect(serveraddress);
if (!n)
{
std::cerr << "connect error:" << serveraddress.ToString() << std::endl;
exit(2);
}
Protocol protocol(HandlerResponse);
std::string inbuffer;
while (true)
{
int cnt = 3;
std::string outbuffer;
while (cnt--)
{
// 0.获取数据
int x, y;
char oper;
std::cout << "enter your x:";
std::cin >> x;
std::cout << "enter your y:";
std::cin >> y;
std::cout << "enter your oper:";
std::cin >> oper;
// 1.定义请求对象
Request req(x, y, oper);
// 2.序列化
std::string req_json;
req.Serialize(&req_json);
// 3.封装报头
std::string send_req_string = protocol.Packet(req_json);
outbuffer+=send_req_string;
}
std::cout<<"\n"<<outbuffer<<std::endl;
// 4.发送
socket->Send(outbuffer);
// 5.接收
socket->Recv(&inbuffer);
// 6.解析
protocol.ParseResponse(inbuffer);
}
// Protocol protocol(HandlerResponse);
// std::string inbuffer;
// while(true)
// {
// //0.获取数据
// int x,y;
// char oper;
// std::cout<<"enter your x:";
// std::cin>>x;
// std::cout<<"enter your y:";
// std::cin>>y;
// std::cout<<"enter your oper:";
// std::cin>>oper;
// //1.定义请求对象
// Request req(x,y,oper);
// //2.序列化
// std::string req_json;
// req.Serialize(&req_json);
// //3.封装报头
// std::string send_req_string = protocol.Packet(req_json);
// //4.发送
// socket->Send(send_req_string);
// //5.接收
// socket->Recv(&inbuffer);
// //6.解析
// protocol.ParseResponse(inbuffer);
// }
socket->Close();
return 0;
}
NetCalServer.cc
cpp
#include"TcpServer.hpp"//网络层
#include"Protocol.hpp"//协议层
#include"Calculator.hpp"//业务层
#include<memory>
static void Usage(const std::string &proc)
{
std::cout<<"Usage:\n\r"<<proc<<"server_port"<<std::endl;
}
int main(int argc,char* argv[])
{
if(argc!=2)
{
Usage(argv[0]);
exit(1);
}
uint16_t port = std::stoi(argv[1]);
//1.定义计算器
std::unique_ptr<Calculator> cal=std::make_unique<Calculator>();
//2.定义协议对象
std::unique_ptr<Protocol> protocol = std::make_unique<Protocol>(
[&cal](Request& req)->Response{
return cal->Execute(req);
}
);
//3.定义网络对象
std::unique_ptr<TcpServer> tsvr = std::make_unique<TcpServer>(
[&protocol](std::string& inbuffer)->std::string
{
return protocol->ParseRequest(inbuffer);
},port
);
tsvr->Loop();
return 0;
}
Protocol.hpp
cpp
#pragma once
// 自定义协议
#include <iostream>
#include <string>
#include <jsoncpp/json/json.h>
#include <functional>
#include"Logger.hpp"
// 使用json做序列和反序列化
using namespace NS_LOG_MODULE;
// 请求报文
class Request
{
public:
Request()
{
}
Request(int x, int y, char oper) : _data_x(x),
_data_y(y),
_oper(oper)
{
}
// 提供序列化方式,客户端发送给服务端要序列化
bool Serialize(std::string *out)
{
Json::Value root;
root["left"] = _data_x;
root["right"] = _data_y;
root["oper"] = _oper;
Json::FastWriter writer;
*out = writer.write(root);
return true;
}
// 反序列化,服务端接收后,反序列化拿数据
bool DeSerialize(std::string &in)
{
// 字符串->结构
Json::Value root;
Json::Reader reader;
bool parsesuccess = reader.parse(in, root);
if (!parsesuccess)
return false;
_data_x = root["left"].asInt();
_data_y = root["right"].asInt();
_oper = root["oper"].asInt(); // Json没有提供aschar,转成int,赋值给char类型自然就是字符。
return true;
}
~Request()
{
}
public:
// 客户端发送给服务端
int _data_x = 0;
int _data_y = 0;
char _oper = 0; //'+' '-' '*' '/' '%'
};
// 应答报文
class Response
{
public:
Response()
{
}
Response(int result, int code) : _result(result),
_code(code)
{
}
// 提供序列化方式,服务端把数据发送前要序列化
bool Serialize(std::string *out)
{
Json::Value root;
root["result"] = _result;
root["code"] = _code;
Json::FastWriter writer;
*out = writer.write(root);
return true;
}
// 反序列化,客户端接收,反序列化拿结果
bool DeSerialize(std::string &in)
{
// 字符串->结构
Json::Value root;
Json::Reader reader;
bool parsesuccess = reader.parse(in, root);
if (!parsesuccess)
return false;
_result = root["result"].asInt();
_code = root["code"].asInt();
return true;
}
~Response()
{
}
public:
int _result = 0;
// 判断结果是否可信,状态码
int _code = 0;
};
using HandlerRequest_t = std::function<Response(Request &)>;
using HandlerResponse_t = std::function<void(Response &)>;
const std::string gsep = "\r\n";
class Protocol
{
public:
Protocol(HandlerRequest_t handler) : _version("1.0"), _handler_request(handler)
{
}
Protocol()
{}
Protocol(HandlerResponse_t handler_response) : _version("1.0"), _headler_response(handler_response)
{
}
// 添加协议报头:字符串长度,分割符:\r\n
// len\r\n{"left":10,"right":20,"oper":'+'}\r\n
std::string Packet(const std::string &json_string)
{
return std::to_string(json_string.size()) + gsep + json_string + gsep;
}
// 情况1:len\r\n{"left":10,"right":20,"oper":'+'}\r\n
// 情况2:len\r\n{"left":10,"right":20,"oper":'+'}\r\nlen\r\n{"left":10,"right":20,"oper":'+'}\r\nlen\r\n{"left":10,"right":20,"oper":'+'}\r\n
// 情况3:len\r\n{"left":10,"right":20,"oper":'+'}\r\nlen\r\n{"left":10,"right":20,"op
// 情况4:len\r\n{"left":10,"rig
// 情况5:le
// 一次提取一个,如果有多个将提取的报文从包中删除,如果还需提取,循环调用即可
// ret >0 : no error , json_string!=NULL
// ret==0:no error , json_string==NULL;没有完整报文
// ret <0:error
int Unpack(std::string &packet, std::string *json_string)
{
if (packet.empty())
return 0;
if (json_string == nullptr)
return -1;
// 分析报文,先找分隔符
auto pos = packet.find(gsep);
if (pos == std::string::npos) // 查报文时,没有找到\r\n,传进来的报文肯能连长度都没有发全
{
return 0;
}
// 到这说明有一个\r\n,提取一份报文的长度,从0下标截取到找到的分隔符
std::string lenstr = packet.substr(0, pos); // 左闭右开
int len = std::stoi(lenstr); // 有效载荷的长度
// 一个完整报文的长度
int total = lenstr.size() + len + 2 * gsep.size();
if (packet.size() < total) // 情况4
return 0;
// 一定有一个完整的报文,提取
*json_string = packet.substr(pos + gsep.size(), len);
// 提取一个完整报文后,在原报文内存空间中删除该报文
packet.erase(0, total);
return 1;
}
// 如果读到半个报文,什么都不做。
// 如果读到一个报文+,循环处理。把所有合法的报文统一处理
// std::string Parse(std::string &inbuffer)
std::string ParseRequest(std::string &inbuffer)//result判断解析是否正确
{
// std::string json_string;
// //1.解包
// int n=Unpack(inbuffer,&json_string);
// if(n<0)
// {
// LOG(LogLevel::DEBUG)<<"no way";
// return std::string();
// }
// if(n==0)//没有一个完整的报文
// {
// LOG(LogLevel::INFO)<<inbuffer<<"no full packet";
// return std::string();
// }
// //2.反序列化
// //得到一个完整的报文jsonstring
// //创建临时对象反序列化
// Request req;
// if(!req.DeSerialize(json_string))
// {
// //反序列化失败(格式不对)
// return std::string();
// }
// //3.业务计算
// Response resp;
// if(_handler_request)
// resp = _handler_request(req);
// //4.应答序列化
// std::string resp_json_string;
// resp.Serialize(&resp_json_string);
// //5.封包,添加报头
// return Packet(resp_json_string);
std::string result;
while (true)
{
std::string json_string;
// 1.解包
int n = Unpack(inbuffer, &json_string);
if (n < 0)
{
LOG(LogLevel::DEBUG) << "no way";
return std::string();
}
if (n == 0)
{
LOG(LogLevel::INFO) << inbuffer << "parse done";
return result;
}
LOG(LogLevel::DEBUG)<<"json_string:\n"<<json_string;
LOG(LogLevel::DEBUG)<<"unpack done , inbuffer:\n"<<inbuffer;
// 2.反序列化
// 得到一个完整的报文jsonstring
// 创建临时对象反序列化
Request req;
if (!req.DeSerialize(json_string))
{
// 反序列化失败(格式不对)
return std::string();
}
// 3.业务计算
Response resp;
if (_handler_request)
resp = _handler_request(req);
// 4.应答序列化
std::string resp_json_string;
resp.Serialize(&resp_json_string);
// 5.封包,添加报头
result+=Packet(resp_json_string);
}
}
std::string ParseResponse(std::string &inbuffer)//result判断解析是否正确
{
while (true)
{
std::string json_string;
// 1.解包
int n = Unpack(inbuffer, &json_string);
if (n < 0)
{
LOG(LogLevel::DEBUG) << "no way";
return std::string();
}
if (n == 0)
{
LOG(LogLevel::INFO) << inbuffer << "parse done";
return std::string();
}
// 2.反序列化
// 得到一个完整的报文jsonstring
// 创建临时对象反序列化
Response resp;
if (!resp.DeSerialize(json_string))
{
// 反序列化失败(格式不对)
return std::string();
}
// 3.回调处理
if (_headler_response)
_headler_response(resp);
}
}
~Protocol()
{
}
private:
std::string _version;
HandlerRequest_t _handler_request; // 计算函数
HandlerResponse_t _headler_response;
};
Sock.hpp
cpp
#pragma once
#include<iostream>
#include<unistd.h>
#include<cstdlib>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<sys/socket.h>
#include"InetAddr.hpp"
#include"Logger.hpp"
namespace NS_SOCKET_MODULE
{
using namespace NS_LOG_MODULE;
static const int gbacklog =16;
enum
{
OK=0,
SOCKET_ERR,
BIND_ERR,
LISTEN_ERR
};
//套接字接口,可能是TCP/UDP
//模板方法模式:方法的差异由纯虚接口底层继承实现,相同逻辑公共方法
class Socket
{
public:
~Socket()
{}
protected:
virtual void CreateSocketOrDie()=0;
virtual void BindSocketOrDie(uint16_t port)=0;
virtual void ListenSocketOrDie()=0;
public:
virtual std::shared_ptr<Socket> Accepter(InetAddr &addr)=0;
virtual int Sockfd()=0;
virtual ssize_t Recv(std::string *out)=0;
virtual int Send(const std::string &in)=0;
virtual void Close()=0;
virtual bool Connect(InetAddr &addr)=0;
public:
void BuildTcpSocketMethod(uint16_t port)
{
CreateSocketOrDie();
BindSocketOrDie(port);
ListenSocketOrDie();
}
void BuildTcpClientSockMethod()
{
CreateSocketOrDie();
}
// void BuildUdpSocketMethod()
// {
// CreateSocketOrDie();
// BindSocketOrDie();
// }
};
class TcpSocket:public Socket
{
public:
TcpSocket():
_sockfd(0)
{
}
TcpSocket(int sockfd):_sockfd(sockfd)
{
}
void CreateSocketOrDie() override
{
_sockfd=socket(AF_INET,SOCK_STREAM,0);
if(_sockfd<0)
{
LOG(LogLevel::FATAL)<<"create socket error";
exit(SOCKET_ERR);
}
}
void BindSocketOrDie(uint16_t port) override
{
InetAddr addr(port);
if(bind(_sockfd,addr.NetAddress(),addr.Len())!=0)
{
LOG(LogLevel::FATAL)<<"bind error";
exit(BIND_ERR);
}
}
void ListenSocketOrDie() override
{
if(listen(_sockfd,gbacklog)!=0)
{
LOG(LogLevel::FATAL)<<"listen error";
exit(LISTEN_ERR);
}
}
std::shared_ptr<Socket> Accepter(InetAddr &clientaddr) override
{
struct sockaddr_in addr;
socklen_t len = sizeof(addr);
int sockfd=accept(_sockfd,CONV(&addr),&len);
if(sockfd<0)
{
LOG(LogLevel::WARNING)<<"accept error";
return nullptr;
}
clientaddr=addr;
return std::make_shared<TcpSocket>(sockfd);
}
int Sockfd() override
{
return _sockfd;
}
ssize_t Recv(std::string *out) override
{
char inbuffer[1024];
ssize_t n =recv(_sockfd,inbuffer,sizeof(inbuffer)-1,0);
if(n>0)
{
inbuffer[n]=0;
*out+=inbuffer;
}
return n;
}
int Send(const std::string& in) override
{
return send(_sockfd,in.c_str(),in.size(),0);
}
void Close() override
{
if(_sockfd>=0)
{
close(_sockfd);
_sockfd=-1;
}
}
bool Connect(InetAddr& addr) override
{
int n = connect(_sockfd, addr.NetAddress(),addr.Len());
if(n<0)
return false;
else
return true;
}
~TcpSocket(){}
private:
int _sockfd;
};
// class UdpSocket:public Socket
// {
// };
}
TcpServer.hpp
cpp
#include "Logger.hpp"
#include "InetAddr.hpp"
#include "Socket.hpp"
#include <memory>
#include <unistd.h>
#include <signal.h>
#include<functional>
static uint16_t gport = 8080;
using namespace NS_SOCKET_MODULE;
//参数&,在解包函数中,解出来一个完整的报文,需要在原串中删除。
using Handler_t = std::function<std::string(std::string&)>;
class TcpServer
{
public:
TcpServer(Handler_t handler,uint16_t port = gport) : _port(port), _listensock(std::make_unique<TcpSocket>()),_handler(handler)
{
_listensock->BuildTcpSocketMethod(_port);
LOG(LogLevel::INFO)<<"create listen socket success: "<<_listensock->Sockfd();
}
void Loop()
{
signal(SIGCHLD, SIG_IGN);
while (true)
{
InetAddr clientaddr;
auto sockfd = _listensock->Accepter(clientaddr);
if (!sockfd)
continue;
LOG(LogLevel::DEBUG) << "get new link,socket address:" << clientaddr.ToString() << "sockfd:" << sockfd->Sockfd();
if (fork() == 0)
{
service(sockfd, clientaddr);
exit(0);
}
}
}
~TcpServer() {}
private:
void service(std::shared_ptr<Socket> sockfd, InetAddr &clientaddr)
{
// std::string inbuffer;
//输入缓冲区,输出缓冲区
std::string inbuffer,outbuffer;
while (true)
{
outbuffer.clear();
int n=sockfd->Recv(&inbuffer);
if(n<=0)//读取失败不做处理
{
LOG(LogLevel::WARNING)<<"recv : client quit,"<<clientaddr.ToString();
break;
}
LOG(LogLevel::DEBUG)<<"inbuffer:\n"<<inbuffer;
if(_handler)
outbuffer += _handler(inbuffer);
if(outbuffer.empty())//没有一个完整的报文,不发
{
continue;
}
LOG(LogLevel::DEBUG)<<"outbuffer:\n"<<outbuffer;
n=sockfd->Send(outbuffer);
if(n<0)
{
LOG(LogLevel::WARNING)<<"send:client quit,"<<clientaddr.ToString();
break;
}
}
}
private:
uint16_t _port;
std::unique_ptr<Socket> _listensock;
Handler_t _handler; //让上层去处理数据
};

4.Jsoncpp
Jsoncpp是一个用于处理JSON数据的C++库。它提供了将JSON数据序列化为字符串以及从字符串
反序列化为C++数据结构的功能。Jsoncpp是开源的,广泛用于各种需要处理JSON数据的C++目中。
特性
1.简单易用:Jsoncpp提供了直观的APl,使得处理JSON数据变得简单。
2.高性能:Jsoncpp的性能经过优化,能够高效地处理大量JSON数据。
3.全面支持:支持JSON标准中的所有数据类型,包括对象、数组、字符串、数字、布尔值和null。
4.错误处理:在解析JSON数据时,Jsoncpp提供了详细的错误信息和位置,方便开发者调试。
bash
ubuntu:sudo apt-get install libjsoncpp-dev
Centos: sudo yum install jsoncpp-devel
头文件位置

4.1序列化
序列化指的是将数据结构或对象转换为一种格式,以便在网络上传输或存储到文件中。Jsoncpp提供了多种方式进行序列化:
1.使用Json::Value的toStyledString方法:
优点:将Json::Value对象直接转换为格式化的JSON字符串。
示例:
cpp
#include <iostream>
#include <string>
#include <jsoncpp/json/json.h>
int main()
{
Json::Value root;
root["name"] = "joe";
root["sex"] = "男";
std::string s = root.toStyledString();
std::cout << s << std::endl;
return 0;
}
$./ test.exe{
"name" : "joe",
"sex" : "男"
}
2.使用Json::StreamWriter:
- 优点:提供了更多的定制选项,缩进,换行。
- 示例:
cpp
#include <iostream>
#include <string>
#include <sstream>
#include <memory>
#include <jsoncpp/json/json.h>
int main()
{
Json::Value root;
root["name"] = "joe";
root["sex"] = "男";
Json::StreamWriterBuilder wbuilder; // StreamWriter的⼯⼚
std::unique_ptr<Json::StreamWriter>
writer(wbuilder.newStreamWriter());
std::stringstream ss;
writer->write(root, &ss);
std::cout << ss.str() << std::endl;
return 0;
}
$./ test.exe{
"name" : "joe",
"sex" : "男"
}
3.使用Json::FasrWriter
- 优点:⽐ StyledWriter 更快,因为它不添加额外的空格和换⾏符。
- 示例:
cpp
#include <iostream>
#include <string>
#include <sstream>
#include <memory>
#include <jsoncpp/json/json.h>
int main()
{
Json::Value root;
root["name"] = "joe";
root["sex"] = "男";
Json::FastWriter writer;
std::string s = writer.write(root);
std::cout << s << std::endl;
return 0;
}
$./ test.exe{"name" : "joe", "sex" : "男"}
cpp
#include <iostream>
#include <string>
#include <sstream>
#include <memory>
#include <jsoncpp/json/json.h>
int main()
{
Json::Value root;
root["name"] = "joe";
root["sex"] = "男";
// Json::FastWriter writer;
Json::StyledWriter writer;
std::string s = writer.write(root);
std::cout << s << std::endl;
return 0;
}
$./ test.exe{
"name" : "joe",
"sex" : "男"
}
4.对Json::Value对象序列化
cpp
#include<iostream>
#include<sstream>
#include<jsoncpp/json/json.h>
int main()
{
Json::Value sub_root1;
sub_root1["name"]="zhangsan";
sub_root1["sex"]="man";
sub_root1["age"]="18";
Json::Value sub_root2;
sub_root2["math"]=88;
sub_root2["chinese"]=1;
Json::Value root;
root["who"]=sub_root1;
root["score"] = sub_root2;
Json::StreamWriterBuilder swbuilder;
std::unique_ptr<Json::StreamWriter> swriter(swbuilder.newStreamWriter());
std::stringstream ss;
swriter->write(root,&ss);
std::cout<<ss.str()<<std::endl;
}
bash
{
"score" :
{
"chinese" : 1,
"math" : 88
},
"who" :
{
"age" : "18",
"name" : "zhangsan",
"sex" : "man"
}
}
cpp
#include<iostream>
#include<sstream>
#include<jsoncpp/json/json.h>
int main()
{
Json::Value sub_root1;
sub_root1["name"]="zhangsan";
sub_root1["sex"]="man";
sub_root1["age"]="18";
Json::Value sub_root2;
sub_root2["math"]=88;
sub_root2["chinese"]=1;
Json::Value one;
one["who"]=sub_root1;
one["score"] = sub_root2;
Json::Value root;
for(int i=0;i<10;i++)
{
root.append(one);
}
Json::StreamWriterBuilder swbuilder;
std::unique_ptr<Json::StreamWriter> swriter(swbuilder.newStreamWriter());
std::stringstream ss;
swriter->write(root,&ss);
std::cout<<ss.str()<<std::endl;
}
4.2反序列化
反序列化指的是将序列化后的数据重新转换为原来的数据结构或对象。
1.使用Json::Reader
- 优点:提供详细的错误信息和位置,方便调试
- 示例:
cpp
#include <iostream>
#include <string>
#include <jsoncpp/json/json.h>
int main()
{
// JSON 字符串
std::string json_string = "{\"name\":\"张三\", \"age\":30,
\"city\":\"北京\"}";
// 解析 JSON 字符串
Json::Reader reader;
Json::Value root;
// 从字符串中读取 JSON 数据
bool parsingSuccessful = reader.parse(json_string, root);
if (!parsingSuccessful)
{
// 解析失败,输出错误信息
std::cout << "Failed to parse JSON: " << reader.getFormattedErrorMessages() << std::endl;
return 1;
}
// 访问 JSON 数据
std::string name = root["name"].asString();
int age = root["age"].asInt();
std::string city = root["city"].asString();
// 输出结果
std::cout << "Name: " << name << std::endl;
std::cout << "Age: " << age << std::endl;
std::cout << "City: " << city << std::endl;
return 0;
}
$./ test.exe
Name : 张三
Age : 30
City : 北京
总结:
- toStyledString、StreamWriter和FastWriter提供了不同的序列化选项,可以根据具体需求选择使用。
- Json::Reader和parseFromStream函数是Jsoncpp中主要的反序列化工具,它们提供了强大的错误处理机制。
- 在进行序列化和反序列化时,请确保处理所有可能的错误情况,并验证输入和输出的有效性