这是一个客户端和服务器通信的小项目,小编自己手写了一个json工具,测试比jsoncpp,protobuf "效率更高"(浅拷贝),改写成深拷贝效率会急剧下降(小编能力有限,知识点(多线程,json,c++11(万能模板,完美转发,包装器等),网络套接字)
改进:后序可以引进多线程,epoll模型(反应堆)或者引入负载均衡(并发数量大的情况),Json API更加完善。
Json/json.h
#pragma once
#include <string>
#include <vector>
#include <map>
#include <sstream>
#include <fstream>
#include <unordered_map>
using namespace std;
namespace yazi
{
namespace json
{
class Json
{
public:
typedef std::vector<Json>::iterator iterator;
iterator begin()
{
return _vaule._array->begin();
}
iterator end()
{
return _vaule._array->end();
}
enum Type
{
json_null = 0,
json_bool,
json_int,
json_double,
json_string,
json_array,
json_object
};
Json();
Json(bool vaule);
Json(int vaule);
Json(double vaule);
Json(const char* vaule);
Json(const std::string& vaule);
Json(Type type);
Json(const Json& other);
operator bool();
operator int();
operator double();
operator string();
Json& operator[](int index);
Json& operator[](const char* key);
Json& operator[](const std::string& key);
void operator=(const Json& other);
bool operator==(const Json& other);
bool operator!=(const Json& other);
//Json& operator*();
void append(const Json& other);
size_t size() const;
string str()const;
void copy(const Json& other);
void clear();
bool isNull()const { return _type == json_null; }
bool isBool()const { return _type == json_bool; }
bool isInt()const { return _type == json_int; }
bool isDouble()const { return _type == json_double; }
bool isString()const { return _type == json_string; }
bool isArray()const { return _type == json_array; }
bool isObject()const { return _type == json_object; }
bool asBool() const;
int asInt()const;
double asDouble()const;
std::string asString()const;
bool has(int index);
bool has(const char* key);
bool has(const std::string& key);
void remove(int index);
void remove(const char* key);
void remove(const std::string& key);
void parse(const std::string& str);
private:
union Vaule
{
bool _bool;
int _int;
double _double;
std::string* _string;
std::vector<Json>* _array;
std::map<std::string, Json>* _object;
};
Type _type;
Vaule _vaule;
};
}
}
json/json.cc
#include "json.h"
#include "parser.h"
using namespace yazi::json;
Json::Json() : _type(json_null)
{
}
Json::Json(bool vaule) : _type(json_bool)
{
_vaule._bool = vaule;
}
Json::Json(int vaule) : _type(json_int)
{
_vaule._int = vaule;
}
Json::Json(double vaule) : _type(json_double)
{
_vaule._double = vaule;
}
Json::Json(const char* vaule) : _type(json_string)
{
_vaule._string = new std::string(vaule);
}
Json::Json(const std::string& vaule) : _type(json_string)
{
_vaule._string = new std::string(vaule);
}
Json::Json(Type type) : _type(type)
{
switch (_type)
{
case json_null:
break;
case json_bool:
_vaule._bool = false;
break;
case json_int:
_vaule._int = 0;
break;
case json_double:
_vaule._double = 0.0;
break;
case json_string:
_vaule._string = new std::string("");
break;
case json_array:
_vaule._array = new std::vector<Json>();
break;
case json_object:
_vaule._object = new std::map<std::string, Json>();
break;
default:
break;
}
}
Json::Json(const Json& other)
{
copy(other);
}
Json::operator bool()
{
if (_type != json_bool)
{
throw new logic_error("type error,not bool vaule");
}
return _vaule._bool;
}
Json::operator int()
{
if (_type != json_int)
{
throw new logic_error("type error,not int vaule");
}
return _vaule._int;
}
Json::operator double()
{
if (_type != json_double)
{
throw new logic_error("type error,not double vaule");
}
return _vaule._double;
}
Json::operator string()
{
if (_type != json_string)
{
throw new logic_error("type error,not string vaule");
}
return *(_vaule._string);
}
Json& Json::operator[](int index)
{
if (_type != json_array)
{
clear();
_type = json_array;
_vaule._array = new std::vector<Json>();
}
// if(index < 0)
// {
// throw new logic_error("array[] index < 0");
// }
size_t size = (_vaule._array)->size();
if (index >= size) // 扩容
{
for (size_t i = size; i <= index; ++i)
{
(_vaule._array)->push_back(Json());
}
}
return (_vaule._array)->at(index);
}
Json& Json::operator[](const char* key)
{
std::string name(key);
return (*(this))[name];
}
Json& Json::operator[](const std::string& key)
{
if (_type != json_object)
{
clear();
_type = json_object;
_vaule._object = new std::map<std::string, Json>();
}
return (*(_vaule._object))[key];
}
void Json::operator=(const Json& other)
{
clear();
copy(other);
}
void Json::append(const Json& other)
{
if (_type != json_array)
{
_type = json_array;
_vaule._array = new std::vector<Json>();
}
(_vaule._array)->push_back(other);
}
size_t Json::size() const
{
if (_type != json_array)
{
throw new logic_error("type error,not array vaule");
}
return (_vaule._array)->size();
}
string Json::str() const
{
std::stringstream ss;
switch (_type)
{
case json_null:
ss << "null";
break;
case json_bool:
if (_vaule._bool)
{
ss << "true";
}
else
{
ss << "false";
}
break;
case json_int:
ss << _vaule._int;
break;
case json_double:
ss << _vaule._double;
break;
case json_string:
ss << '\"' << *(_vaule._string) << '\"';
break;
case json_array:
{
ss << '[';
for (auto it = (_vaule._array)->begin(); it != (_vaule._array)->end(); ++it)
{
if (it != (_vaule._array)->begin())
{
ss << ',';
}
ss << it->str();
}
ss << ']';
break;
}
case json_object:
{
ss << '{';
for (auto it = (_vaule._object)->begin(); it != (_vaule._object)->end(); ++it)
{
if (it != (_vaule._object)->begin())
{
ss << ',';
}
ss << '\"' << it->first << '\"' << ':' << it->second.str();
}
ss << '}';
break;
}
default:
break;
}
return ss.str();
}
void Json::copy(const Json& other)
{
_type = other._type;
switch (_type)
{
case json_null:
break;
case json_bool:
_vaule._bool = other._vaule._bool;
break;
case json_int:
_vaule._int = other._vaule._int;
break;
case json_double:
_vaule._double = other._vaule._double;
break;
case json_string:
_vaule._string = other._vaule._string;
break;
case json_array:
_vaule._array = other._vaule._array;
break;
case json_object:
_vaule._object = other._vaule._object;
break;
default:
break;
}
}
void Json::clear()
{
switch (_type)
{
case json_null:
break;
case json_bool:
_vaule._bool = false;
break;
case json_int:
_vaule._int = 0;
break;
case json_double:
_vaule._double = 0.0;
break;
case json_string:
delete _vaule._string;
break;
case json_array:
{
for (auto it = _vaule._array->begin(); it != _vaule._array->end(); ++it)
{
it->clear();
}
delete _vaule._array;
break;
}
case json_object:
{
for (auto it = _vaule._object->begin(); it != _vaule._object->end(); ++it)
{
(it->second).clear();
}
delete _vaule._object;
break;
}
default:
break;
}
_type = json_null;
}
bool Json::operator==(const Json& other) //浅拷贝(效率)
{
if (_type != other._type)
{
return false;
}
switch (_type)
{
case json_null:
return true;
case json_bool:
return _vaule._bool == other._vaule._bool;
case json_int:
return _vaule._int == other._vaule._int;
case json_double:
return _vaule._double == other._vaule._double;
case json_string:
return *(_vaule._string) == *(other._vaule._string);
case json_array:
{
/*if(_vaule._array->size() != other._vaule._array->size())
{
return false;
}
for (size_t i = 0; i < _vaule._array->size(); ++i)
{
if ((*(_vaule._array))[i] != (* (other._vaule._array))[i])
{
return false;
}
}
return true;*/
return _vaule._array == other._vaule._array;
}
case json_object:
{
return _vaule._object == other._vaule._object;
}
default:
break;
}
return false;
}
bool Json::operator!=(const Json& other)
{
return !(*this == other);
}
bool Json::asBool() const
{
if (_type != json_bool)
{
throw new std::logic_error("type error,not bool vaule");
}
return _vaule._bool;
}
int Json::asInt()const
{
if (_type != json_int)
{
throw new std::logic_error("type error,not int vaule");
}
return _vaule._int;
}
double Json::asDouble()const
{
if (_type != json_double)
{
throw new std::logic_error("type error,not double vaule");
}
return _vaule._double;
}
std::string Json::asString()const
{
if (_type != json_string)
{
throw new std::logic_error("type error,not string vaule");
}
return *(_vaule._string);
}
bool Json::has(int index)
{
if (_type != json_array)
{
return false;
}
int size = static_cast<int>((_vaule._array)->size());
return (index >= 0 && index < size);
}
bool Json::has(const char* key)
{
string name(key);
return has(name);
}
bool Json::has(const std::string& key)
{
if (_type != json_object)
{
return false;
}
return (_vaule._object)->find(key) != (_vaule._object)->end();
}
void Json::remove(int index)
{
if (_type != json_array)
{
return;
}
int size = static_cast<int>((_vaule._array)->size());
if (index < 0 || index >= size)
{
return;
}
(_vaule._array)->at(index).clear();
(_vaule._array)->erase((_vaule._array)->begin() + index);
}
void Json::remove(const char* key)
{
string name(key);
return remove(name);
}
void Json::remove(const std::string& key)
{
auto it = (_vaule._object)->find(key);
if (it == (_vaule._object)->end())
{
return;
}
(*(_vaule._object))[key].clear();
(_vaule._object)->erase(key);
}
void Json::parse(const std::string& str)
{
Parser p;
p.load(str);
*this = p.parse();
}
json/parser.h
#pragma once
#include "json.h"
#include <string>
#include <regex>
namespace yazi
{
namespace json
{
class Parser
{
public:
Parser();
void load(const std::string& str);
Json parse();
private:
void skip_white_space();
char get_next_token();
Json parse_null();
Json parse_bool();
Json parse_number();
std::string parse_string();
Json parse_array();
Json parse_object();
private:
std::string _str;
int _index;
};
}
}
json.parser.cc
#include "parser.h"
using namespace yazi::json;
Parser::Parser() : _str(""), _index(0)
{
}
void Parser::load(const std::string& str)
{
_str = str;
_index = 0;
}
Json Parser::parse()
{
char ch = get_next_token();
switch (ch)
{
case 'n':
--_index;
return parse_null();
case 't':
case 'f':
--_index;
return parse_bool();
case '-':
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
--_index;
return parse_number();
case '"':
return Json(parse_string());
case '[':
return parse_array();
case '{':
return parse_object();
default:
break;
}
throw new std::logic_error("unexpected char");
}
void Parser::skip_white_space()
{
while (_str[_index] == ' ' || _str[_index] == '\n' || _str[_index] == '\r' || _str[_index] == '\t')
{
++_index;
}
}
char Parser::get_next_token()
{
skip_white_space();
return _str[_index++];
}
Json Parser::parse_null()
{
if (_str.compare(_index, 4, "null") == 0)
{
_index += 4;
return Json();
}
throw new std::logic_error("parse null error");
}
Json Parser::parse_bool()
{
if (_str.compare(_index, 4, "true") == 0)
{
_index += 4;
return Json(true);
}
else if (_str.compare(_index, 5, "false") == 0)
{
_index += 5;
return Json(false);
}
else
{
throw new std::logic_error("parse bool error");
}
}
Json Parser::parse_number()
{
int pos = _index;
if (_str[_index] == '-')
{
++_index;
}
if (isdigit(_str[_index]) == 0)
{
throw new std::logic_error("parse number error");
}
while (isdigit(_str[_index]) != 0)
{
++_index;
}
if (_str[_index] != '.')
{
int i = std::atoi(_str.c_str() + pos);
return Json(std::move(i));
}
if (isdigit(_str[++_index]) == 0)
{
throw new std::logic_error("parse number error");
}
while (isdigit(_str[_index]) != 0)
{
++_index;
}
double f = std::atof(_str.c_str() + pos);
return Json(std::move(f));
}
std::string Parser::parse_string()
{
std::string out;
while (true)
{
char ch = _str[_index++];
if (ch == '"')
{
break;
}
if (ch == '\\')
{
ch = _str[_index++];
switch (ch)
{
case '\n':
out += '\n';
break;
case '\r':
out += '\r';
break;
case '\t':
out += '\t';
break;
case '\b':
out += '\b';
break;
case '\f':
out += '\f';
break;
case '"':
out += "\\\"";
break;
case '\\':
out += "\\\\";
break;
case 'u':
out += "\\u";
for (int i = 0; i < 4; ++i)
{
out += _str[_index++];
}
break;
default:
break;
}
}
else
{
out += ch;
}
}
return std::move(out);
}
Json Parser::parse_array()
{
Json arr(Json::json_array);
char ch = get_next_token();
if (ch == ']')
{
return arr;
}
--_index;
while (true)
{
arr.append(parse()); //递归解析
ch = get_next_token();
if (ch == ']')
{
break;
}
if (ch != ',')
{
throw new std::logic_error("parse array error");
}
//++_index;//ERROR
}
return arr;
}
Json Parser::parse_object()
{
Json obj(Json::json_object);
char ch = get_next_token();
if(ch == '}')
{
return obj;
}
--_index;
while(true)
{
ch = get_next_token();
if(ch != '"')
{
throw new std::logic_error("parse object error");
}
std::string key = parse_string();
ch = get_next_token();
if(ch != ':')
{
throw new std::logic_error("parse object error");
}
obj[key] = parse();
ch = get_next_token();
if(ch == '}')
{
break;
}
if(ch != ',')
{
throw new std::logic_error("parse object error");
}
//++_index;//ERROR
}
return obj;
}
tcp_socket.hpp
#pragma once
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <string>
#include <cassert>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include "./json/json.h"
#include "./json/parser.h"
using namespace yazi::json;
typedef struct sockaddr sockaddr;
typedef struct sockaddr_in sockaddr_in;
#define CHECK_RET(exp) \
if (!(exp)) \
{ \
return false; \
}
class TcpSocket
{
public:
TcpSocket() : fd_(-1) {}
TcpSocket(int fd) : fd_(fd) {}
bool Socket()
{
fd_ = socket(AF_INET, SOCK_STREAM, 0);
if (fd_ < 0)
{
perror("socket");
return false;
}
printf("open fd = %d\n", fd_);
return true;
}
bool Close() const
{
close(fd_);
printf("close fd = %d\n", fd_);
return true;
}
bool Bind(const std::string &ip, uint16_t port) const
{
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(ip.c_str());
addr.sin_port = htons(port);
int ret = bind(fd_, (sockaddr *)&addr, sizeof(addr));
if (ret < 0)
{
perror("bind");
return false;
}
return true;
}
bool Listen(int num) const
{
int ret = listen(fd_, num);
if (ret < 0)
{
perror("listen");
return false;
}
return true;
}
bool Accept(TcpSocket *peer, std::string *ip = NULL, uint16_t *port = NULL) const
{
sockaddr_in peer_addr;
socklen_t len = sizeof(peer_addr);
int new_sock = accept(fd_, (sockaddr *)&peer_addr, &len);
if (new_sock < 0)
{
perror("accept");
return false;
}
printf("accept fd = %d\n", new_sock);
peer->fd_ = new_sock;
if (ip != NULL)
{
*ip = inet_ntoa(peer_addr.sin_addr);
}
if (port != NULL)
{
*port = ntohs(peer_addr.sin_port);
}
return true;
}
bool Recv(std::string *buf) const
{
buf->clear();
char tmp[1024 * 10] = {0};
// [注意!] 这里的读并不算很严谨, 因为一次 recv 并不能保证把所有的数据都全部读完
// 参考 man 手册 MSG_WAITALL 节.
ssize_t read_size = recv(fd_, tmp, sizeof(tmp), 0);
if (read_size < 0)
{
perror("recv");
return false;
}
if (read_size == 0)
{
return false;
}
buf->assign(tmp, read_size);
return true;
}
bool Recv(Json* result) const
{
result->clear();
char tmp[1024 * 10] = {0};
ssize_t read_size = recv(fd_, tmp, sizeof(tmp), 0);
if (read_size < 0)
{
perror("recv");
return false;
}
if (read_size == 0)
{
return false;
}
std::string name(tmp);
(*result).parse(name);
return true;
}
bool Send(const std::string &buf) const
{
ssize_t write_size = send(fd_, buf.data(), buf.size(), 0);
if (write_size < 0)
{
perror("send");
return false;
}
return true;
}
bool Send(const Json &vaule) const
{
ssize_t write_size = send(fd_, vaule.str().data(), vaule.str().size(), 0);
if (write_size < 0)
{
perror("send");
return false;
}
return true;
}
bool Connect(const std::string &ip, uint16_t port) const
{
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(ip.c_str());
addr.sin_port = htons(port);
int ret = connect(fd_, (sockaddr *)&addr, sizeof(addr));
if (ret < 0)
{
perror("connect");
return false;
}
return true;
}
int GetFd() const
{
return fd_;
}
private:
int fd_;
};
tcp_client.hpp
#pragma once
#include "tcp_socket.hpp"
class TcpClient
{
public:
TcpClient(const std::string &ip, uint16_t port) : ip_(ip), port_(port)
{
// [注意!!] 需要先创建好 socket
sock_.Socket();
}
~TcpClient()
{
sock_.Close();
}
bool Connect()
{
return sock_.Connect(ip_, port_);
}
bool Recv(std::string *buf)
{
return sock_.Recv(buf);
}
bool Recv(Json* result)
{
return sock_.Recv(result);
}
bool Send(const std::string &buf)
{
return sock_.Send(buf);
}
bool Send(const Json& vaule)
{
return sock_.Send(vaule);
}
private:
TcpSocket sock_;
std::string ip_;
uint16_t port_;
};
tcp_server.hpp
#pragma once
#include <functional>
#include <pthread.h>
#include "tcp_socket.hpp"
typedef std::function<void(const std::string &, std::string *)> Handler;
struct ThreadArg
{
TcpSocket new_sock;
std::string ip;
uint16_t port;
Handler handler;
};
class TcpThreadServer
{
public:
TcpThreadServer(const std::string &ip, uint16_t port) : ip_(ip), port_(port)
{
}
bool Start(Handler handler)
{
// 1. 创建 socket;
CHECK_RET(listen_sock_.Socket());
// 2. 绑定端口号
CHECK_RET(listen_sock_.Bind(ip_, port_));
// 3. 进行监听
CHECK_RET(listen_sock_.Listen(5));
// 4. 进入循环
for (;;)
{
// 5. 进行 accept
ThreadArg *arg = new ThreadArg();
arg->handler = handler;
bool ret = listen_sock_.Accept(&arg->new_sock, &arg->ip, &arg->port);
if (!ret)
{
continue;
}
printf("[client %s:%d] connect\n", arg->ip.c_str(), arg->port);
// 6. 创建新的线程完成具体操作
pthread_t tid;
pthread_create(&tid, NULL, ThreadEntry, arg);
pthread_detach(tid);
}
return true;
}
// 这里的成员函数为啥非得是 static?
static void *ThreadEntry(void *arg)
{
// C++ 的四种类型转换都是什么?
ThreadArg *p = reinterpret_cast<ThreadArg *>(arg);
ProcessConnect(p);
// 一定要记得释放内存!!! 也要记得关闭文件描述符
p->new_sock.Close();
delete p;
return NULL;
}
// 处理单次连接. 这个函数也得是 static
static void ProcessConnect(ThreadArg *arg)
{
// 1. 循环进行读写
for (;;)
{
std::string req;
// 2. 读取请求
bool ret = arg->new_sock.Recv(&req);
if (!ret)
{
printf("[client %s:%d] disconnected!\n", arg->ip.c_str(), arg->port);
break;
}
std::string resp;
// 3. 根据请求计算响应
arg->handler(req, &resp);
// 4. 发送响应
arg->new_sock.Send(resp);
printf("[client %s:%d] req: %s, resp: %s\n", arg->ip.c_str(),
arg->port, req.c_str(), resp.c_str());
}
}
private:
TcpSocket listen_sock_;
std::string ip_;
uint16_t port_;
};
dict_client.cc
#include "tcp_client.hpp"
#include <iostream>
int main(int argc, char *argv[])
{
if (argc != 3)
{
printf("Usage ./dict_client [ip] [port]\n");
return 1;
}
TcpClient client(argv[1], atoi(argv[2]));
bool ret = client.Connect();
if (!ret)
{
return 1;
}
// for (;;)
// {
// std::cout << "请输入要查询的单词:" << std::endl;
// std::string word;
// std::cin >> word;
// if (!std::cin)
// {
// break;
// }
// client.Send(word);
// std::string result;
// client.Recv(&result);
// std::cout << result << std::endl;
// }
for (;;) //客户端创建json串发往服务器
{
std::cout << "请输入你的协议:" << std::endl;
// const std::string& str = "{\"a\":true,\"b\":{\"a\":true,\"b\":[1, \"a\", 3, null, -1.2345, true, false, \"hello world\"],\"c\":123,\"4\":\"hello world\"},\"c\":123,\"4\":\"hello world\"}";
// Json v;
// v.parse(str);
// std::cout << v.str() << std::endl;
// client.Send(v.str());
Json arr;
arr[1] = true;
arr[2] = 123;
arr[3] = 1.23;
arr[4] = "hello";
Json obj;
obj["bool"] = true;
obj["int"] = 123;
obj["double"] = 1.23;
obj["arr"] = arr;
std::cout << obj.str() << std::endl;
client.Send(obj);
obj.clear();
Json result;
// client.Recv(result);
// std::string result;
client.Recv(&result);
std::cout << result.str() << std::endl;
}
return 0;
}
dict_server.cc
#include <unordered_map>
#include "tcp_server.hpp"
#include "./json/json.h"
#include "./json/parser.h"
std::unordered_map<std::string, std::string> g_dict;
// void Translate(const std::string &req, std::string *resp)
// {
// auto it = g_dict.find(req);
// if (it == g_dict.end())
// {
// *resp = "未找到";
// return;
// }
// *resp = it->second;
// return;
// }
void Translate(const std::string &req, std::string *resp)
{
// auto it = g_dict.find(req);
// if (it == g_dict.end())
// {
// *resp = "未找到";
// return;
// }
// *resp = it->second;
// return;
Json vaule;
vaule.parse(req);
if(vaule["bool"])
{
Json obj;
//收到Json串这里可以有服务器自己处理业务
//obj["服务器"] = "收到了";
Json arr;
arr[0] = "服务器收到客户端的数据了";
arr[1] = "服务器发往客户端的协议为:932";
arr[2] = 932;
obj["服务器"] = arr;
*resp = obj.str();
obj.clear();
}
vaule.clear();
}
int main(int argc, char *argv[])
{
if (argc != 3)
{
printf("Usage ./dict_server [ip] [port]\n");
return 1;
}
// 1. 初始化词典
//g_dict.insert(std::make_pair("hello", "你好"));
//g_dict.insert(std::make_pair("world", "世界"));
//g_dict.insert(std::make_pair("bit", "贼NB"));
// 2. 启动服务器
TcpThreadServer server(argv[1], atoi(argv[2]));
server.Start(Translate);
return 0;
}
makefile
.PHONY:all
all:dict_client dict_server
dict_client:dict_client.cc ./json/json.cc ./json/parser.cc
dict_client.cc:
g++ -o $@ $^ -std=c++11
dict_server:dict_server.cc ./json/json.cc ./json/parser.cc
g++ -o $@ $^ -std=c++11 -lpthread
.PHONY:clean
clean:
rm -rf dict_client dict_server
测试截图:
githup地址:
基于Josn的网络通信
josn工具
protobuf工具