Json介绍
Json是一种轻量级的数据交换格式(也叫数据序列化方式)。Json采用完全独立于编程语言的文本格式 来存储和表示数据。简洁和清晰的层次结构使得 Json 成为理想的数据交换语言。 易于人阅读和编 写,同时也易于机器解析和生成,并有效地提升网络传输效率。
比如说 我们发送一个聊天的消息,消息的种类很多,聊天的消息里可能分下面这些字段
json序列化与反序列化
string jsonstr = js.dump(); //json数据对象=》序列化=》json字符串
cout<<"jsonstr:"<<jsonstr<<endl;
// 模拟从网络接收到json字符串,通过json::parse函数把json字符串专程json对象
//json反序列化 json字符串=》反序列化 数据对象(看做容器方便反问)
json jsbuf= json::parse(jsonstr);
muduo网络库
简述
muduo网络库支持线程池和epoll 基于多路IO复用 epoll的网络模型
网络模块采用muduo库的话,它会解耦网络模块代码和业务模块代码,能够让开发者更专注于业务的开发。
muduo的网络设计:reactors in threads - one loop per thread
每个事件都有一个事件循环,EventLoop
方案的特点是one loop per thread,有一个main reactor负载accept连接,然后把连接分发到某个sub reactor(采用round-robin的方式来选择sub reactor),该连接的所用操作都在那个sub reactor所处 的线程中完成。多个连接可能被分派到多个线程中,以充分利用CPU。
I/O线程主要是做新用户的链接的,一般线程数量会和CPU对等,这样来说就尽量做到多并发了
用I/O复用好处就是可以监听多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。
这就是muduo网络库的底层实现。
若用户做耗时I/O操作 如传文件 音视频 这是需要单独开辟线程来处理,若还在同时工作线程epoll上操作比较耗时的I/O操作,那可能这个epoll无法及时处理其它注册到这个epoll上的FD(读写事件)
muduo中的reactor模型
The reactor design pattern is an event handling pattern for handling service requests delivered concurrently to a service handler by one or more inputs. The service handler then demultiplexes the incoming requests and dispatches them synchronously to the associated request handlers.
muduo_server.cpp(test)
muduo 网络库给用户提供了俩个主要的类
TcpServer:用于编写服务器程序的
TcpClient:用于编写客户端程序的
epoll+线程池
好处:能够把网络I/O的代码和业务代码区别开
用户的链接和断开 用户的可读写时间
#include <muduo/net/TcpServer.h>
#include <muduo/net/EventLoop.h>
#include <iostream>
#include <functional>
#include <string>
using namespace std;
using namespace muduo;
using namespace muduo::net;
using namespace placeholders;
// 基于muduo网络库开发服务器程序
/*
1.组合TcpServer对象
2.创建EventLoop时间循环对象的指针
3.明确TcpServer构造函数需要什么参数,输出ChtServer的构造函数
4.在当前服务器类的构造函数当中,注册处理链接的回调函数和处理读写事件的回调函数
5.设置合适的服务端线程数量,muduo库会自己分配I/O线程和worker线程
*/
class ChatServer
{
public:
ChatServer(EventLoop *loop, // 事件循环
const InetAddress &listenAddr, // IP+Port
const string &nameArg) // 服务器的名字
: _server(loop, listenAddr, nameArg), _loop(loop)
{
// 给服务器注册用户链接的创建和断开的回调
_server.setConnectionCallback(std::bind(&ChatServer::onConnection, this, _1));
// 给服务器注册用户读写事件回调
_server.setMessageCallback(std::bind(&ChatServer::onMessage, this, _1, _2, _3));
// 设置服务器端的线程数量 1个I/O线程 3个worker线程
_server.setThreadNum(4);
}
// 开启事件循环
void start()
{
_server.start();
}
private:
// 专门处理用户的链接创建和断开 epoll listenfd accept
void onConnection(const TcpConnectionPtr &conn)
{
if (conn->connected())
{
cout << conn->peerAddress().toIpPort() << " -> " << conn->localAddress().toIpPort() << "state:online" << endl;
}
else
{
cout << conn->peerAddress().toIpPort() << " -> " << conn->localAddress().toIpPort() << "state:downline" << endl;
conn->shutdown();//close(fd)
// _loop->quit();
}
}
// 专门处理用户的读写事件
void onMessage(const TcpConnectionPtr &conn, // 链接
Buffer *buffer, // 缓冲区
Timestamp time) // 接收到数据的时间信息
{
string buf = buffer->retrieveAllAsString();
cout << "recv data:" << buf << "time:" << time.toString() << endl;
conn->send(buf);
}
TcpServer _server; // #1
EventLoop *_loop; // #2 epoll
};
这些代码实际上都是“死”的,大家可以完全保存下来 这里需要改的也就是类的名字了,等用的时候直接拷贝粘贴就好,我们主要精力在onConnection和onMessage这俩个函数上。
接下来写个主函数
int main()
{
EventLoop loop;//epoll
InetAddress addr("ip",端口);
ChatServer server(&loop,addr,"ChatServer");
server.start();//listenfd epoll_ctl->epoll
loop.loop();//epoll_wait 以阻塞方式等待新用户链接,已连接用户的读写事件等
return 0;
}
创建ChatServer对象,需要三个参数EvenLoop(特别像大家创建一个epoll)InetAddress(linux的ip和端口)ChatServer(定义server对象)
(时间循环的地址,addr对象(它通过引用接受,直接传对象就可以,服务器的名称))
总结:
muduo库代码这就写完了,使用TcpServer很快的建立了一个性能相当不错的基于事件驱动的
I/O复用epoll的+线程池的这种模型网络服务器。
测试:
在vscode上终端进入testmuduo文件夹
g++ -o server muduo_server.cpp -lmuduo_net -lmuduo_base _lpthread
编译出可执行文件 ./server 运行
再打开个终端 链接ip和终端
telnet ********** ****
回响回来了 回响服务器
服务断127.0.0.1.42136这是我们客户端的端口连接到了127.0.0.1:6000这个端口 上线了
recv date:hello world 这里就被响应到了 并返回时间
telnet就相当于一个tcp客户端 退出以后onConnection这个函数又被回调了