muduo库的使用需要链接libmuduo_base.so、libmuduo_net.so和libpthread.so库,一般so库在默认的路径里有/usr/lib和/usr/local/lib,so库放在这两个默认路径我们也不需要添加相应的头文件搜索路径,因为这两个路径是会自动搜索的(因为它们处于环境变量中),所以我们在代码上如果依赖muduo库写的链接的时候应该加-lmuduo_net -lmuduo_base -lpthread,那我们如何去配置这些东西?
muduo库给用户提供了两个主要的类:
- TcpServer:用于编写服务器程序
- TcpClient:用于编写客户端程序
实际上不管用那个网络库,都是把epoll+线程池给封装起来了,好处就是能够把网络I/O代码和业务代码区分开,让我们用户直接使用网络库,它把最主要的main就放在业务开发代码上,至于网络I/O的代码由网络库来直接封装。
不管是libevent、muduo网络库和其他的一些网络库,我们会发现它们有一个特别相似的点:我们只需要关注用户的连接和断开和用户的可读写事件,至于什么时候发生以及如何监听都是由网络库实现好的,我们不需要关心,这样我们就可以快速开发项目。
#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事件循环对象的指针,可以看做epoll,可以向loop上注册感兴趣的事件,如果有事件发生了,loop会上报
3. 明确TcpServer构造函数需要什么参数,输出ChatServer的构造函数
4. 在当前服务器类型的构造函数当中,注册处理连接的回调函数和处理读写事件的回调函数
5. 设置合适的服务端线程数量,muduo库会自己分配IO线程和worker线程
*/
class ChatServer
{
public:
//TcpServer没有默认的构造函数,所以这里需要指定相应的构造
ChatServer(EventLoop* loop, //事件循环
const InetAddress& listenAddr, //ip+port
const string& nameArg) //服务器名字
:_server(loop,listenAddr,nameArg)
,_loop(loop)
{
//给服务器注册用户连接的创建和断开回调
//人家这个setConnectionCallback是没有返回值以及有一个形参变量,而我们现在写的是一个成员方法,写成成员方法是因为想访问对象的成员变量,
//写成员方法的话就有一个this指针,和人家setConnectionCallback的类型就不同了,所以在这里我们用绑定器绑定
_server.setConnectionCallback(std::bind(&ChatServer::onConnection,this,_1));
//给服务器注册用户读写事件回调
_server.setMessageCallback(std::bind(&ChatServer::onMessage,this,_1,_2,_3));
//设置服务器端的线程数量 1个io线程,3个worker线程
_server.setThreadNum(4);
}
//开始事件循环
void start()
{
_server.start();
}
private:
//专门处理用户的连接创建和断开
/*
我们自己在编写epoll,从epoll拿过来事件以后,发现它如果是listenfd,会从listenfd上去accept,这就表示有新用户连接了,
拿出来一个和该用户专门通信的socket,这一切底层muduo库都封装了,只暴露了一个回调的接口,只管写就行了,不用关心什么时候会调用,
我们都把这个注册到muduo库上了,当有新用户连接的创建以及原来用户连接的断开,这个方法就会响应
*/
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:offline"<<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
};
int main()
{
EventLoop loop; //epoll
InetAddress addr("127.0.0.1",6000);
ChatServer server(&loop,addr,"ChatServer");
server.start(); //启动服务,把listenfd通过epoll_ctl添加到epoll上
loop.loop(); //epoll_wait以阻塞的方式等待新用户的链接,已连接用户的读写事件等
return 0;
}