下面就是利用muduo进行客户端与服务器端的实例:
网络编程关注三个半事件:连接建立、连接断开、消息到达这算三个,还有半个是消息发送完毕(之所以是半个,因为对于低流量的服务,通常不需要关注该事件)
所以,我们利用muduo编写自己的服务器时,需要有一个XXXServer类,在该类中包含一个TcpServer对象和一个EventLoop对象。这个类需要提供3个成员函数OnConnection、OnMessage、OnWriteComplete作为回调函数,连接建立和连接断开都是回调OnConnection函数,消息到达回调OnMessage函数,消息发送完毕调用OnWriteComplete函数
#include <muduo/net/TcpServer.h>
#include <muduo/net/EventLoop.h>
#include <muduo/net/InetAddress.h>
#include <boost/bind.hpp>
#include <stdio.h>
using namespace muduo;
using namespace muduo::net;
class TestServer
{
public:
TestServer(EventLoop* loop,
const InetAddress& listenAddr)
: loop_(loop),
server_(loop, listenAddr, "TestServer")
{
//连接到来和连接断开时的回调函数
server_.setConnectionCallback(
boost::bind(&TestServer::onConnection, this, _1));
//消息到来时的回调函数
server_.setMessageCallback(
boost::bind(&TestServer::onMessage, this, _1, _2, _3));
}
void start()
{
server_.start();
}
private:
void onConnection(const TcpConnectionPtr& conn)
{
if (conn->connected())
{
printf("onConnection(): new connection [%s] from %s\n",
conn->name().c_str(),
conn->peerAddress().toIpPort().c_str());
}
else
{
printf("onConnection(): connection [%s] is down\n",
conn->name().c_str());
}
}
void onMessage(const TcpConnectionPtr& conn,
Buffer* buf,
Timestamp receiveTime)
{
//获取应用层缓冲区的数据
string msg(buf->retrieveAllAsString());
printf("onMessage(): received %zd bytes from connection [%s] at %s\n",
msg.size(),
conn->name().c_str(),
receiveTime.toFormattedString().c_str());
//将数据回射回去
conn->send(msg);
}
EventLoop* loop_;
TcpServer server_;
};
int main()
{
printf("main(): pid = %d\n", getpid());
InetAddress listenAddr(8888);
EventLoop loop;
TestServer server(&loop, listenAddr);
//服务端启动
server.start();
//开始事件循环
loop.loop();
}
分析:
1.server.starrt()也就是server_.start(),此时服务端启动
//该函数可以跨线程调用
//启动线程池
void TcpServer::start()
{
if (started_.getAndSet(1) == 0)
{
threadPool_->start(threadInitCallback_);
//判断是否处于监听状态
assert(!acceptor_->listenning());
loop_->runInLoop(
//进入&Acceptor::listen
boost::bind(&Acceptor::listen, get_pointer(acceptor_)));
}
}
2.执行Acceptor::listen(),此时服务器处于监听状态,并且注册被动连接通道,关注其可读事件
//监听
void Acceptor::listen()
{
loop_->assertInLoopThread();
listenning_ = true;
//监听
acceptSocket_.listen();
//关注可读事件
acceptChannel_.enableReading();
}
3.注册被动连接通道,关注其可读事件
//关注可读事件,把通道注册到EventLoop,从而注册到EventLoop所持有的poll对象中
void enableReading() { events_ |= kReadEvent; update(); }
void Channel::update()
{
addedToLoop_ = true;
//调用loop的updateChannel
loop_->updateChannel(this);
}
进而调用loop的updateChannel()
void EventLoop::updateChannel(Channel* channel)
{
assert(channel->ownerLoop() == this);
assertInLoopThread();
poller_->updateChannel(channel);
}
进而调用poller(有可能是PollPoller或者EPollPoller)的updateChannel()
//注册或者更新通道
//注册某个文件描述符的一些可读可写事件,更新就是原来关注可读可写,现在只关注可读
void PollPoller::updateChannel(Channel* channel)
{
//断言是在Loop线程中调用Poller
Poller::assertInLoopThread();
LOG_TRACE << "fd = " << channel->fd() << " events = " << channel->events();
//如果index_<0,位置还不知道,说明是一个新的通道
if (channel->i