TcpServer类
TcpServer类中的数据成员如下:
Acceptor类的对象是TcpServer类的成员!Acceptor类与TcpServer类是组合关系。
TcpConnection类
TcpConnection类与Acceptor类非常类似,都有两个重要的数据成员:Socket类的对象和Channel类的对象。只是Acceptor类专门用来处理监听事件,建立新连接,每建立一个新连接就会创建一个TcpConnection类的对象;而TcpConnection类在连接建立完成后用来处理事件(这个事件就不再是监听事件了,而是各个TcpConnection类的对象所关注的事件)。
调用顺序(核心!!!!!!)
调用1
创建TcpServer类的对象–>通过该对象调用TcpServer::start(该函数可以跨线程调用,且多次调用是无害的)–>在TcpServer::start中判断其Acceptor类的对象成员acceptor_是否处于监听的状态,如果acceptor_未处于监听的状态,那么通过EventLoop::runInLoop向IO线程布置任务,即让IO线程通过acceptor_的指针调用Acceptor::listen–>Acceptor::listen中调用 Channel::enableReading 注册可读事件(即把监听事件挂到epoll的红黑树上去)
调用2
创建EventLoop类的对象–>通过该对象调用EventLoop::loop–>EventLoop::loop中调用Poller::poll(多态)返回活跃的Channel对象–>调用Channel::handleEvent–>在Channel::handleEvent中,如果到来的是监听事件,由于监听事件是读事件,所以调用读事件回调函数(这里已经在Acceptor类的构造函数中通过其Channel对象调用Channel::setReadCallback将Acceptor::handleRead设置为读事件回调函数)–>在Acceptor::handleRead中,除了调用::accept外,还调用了应用层回调函数 Acceptor::newConnectionCallback_(这里已经在TcpServer类的构造函数中调用Acceptor::setNewConnectionCallback将应用层回调函数newConnectionCallback_设置为TcpServer::newConnection)–>在TcpServer::newConnection中,首先创建了TcpConnection类的对象,并将其包装成boost::shared_ptr<TcpConnection>
类型的对象,加入std::map(即连接列表)中;接着将TcpServer类中的连接到来时的回调函数connectionCallback_和消息到来时的回调函数messageCallback_通过调用TcpConnection::setConnectionCallback、TcpConnection::setMessageCallback 赋值给TcpConnection类中的connectionCallback_和messageCallback_;最后,调用TcpConnection::connectEstablished–>在TcpConnection::connectEstablished中,先调用 Channel::enableReading 注册可读事件(即把当前TcpConnection类的对象关注的事件挂到epoll的红黑树上去),再调用连接到来时的回调函数TcpConnection::connectionCallback_
调用3
创建EventLoop类的对象–>通过该对象调用EventLoop::loop–>EventLoop::loop中调用Poller::poll(多态)返回活跃的Channel对象–>调用Channel::handleEvent–>在Channel::handleEvent中,如果到来的是某个TcpConnection类的对象所关注的事件,由于该事件是读事件,所以调用读事件回调函数(这里已经在TcpConnection类的构造函数中通过其Channel对象调用Channel::setReadCallback将TcpConnection::handleRead设置为读事件回调函数)–>在TcpConnection::handleRead中,调用消息到来时的回调函数TcpConnection::messageCallback_
调用4
创建TcpServer类的对象–>通过该对象调用TcpServer::setConnectionCallback、TcpServer::setMessageCallback,设置TcpServer类中的连接到来时的回调函数connectionCallback_和消息到来时的回调函数messageCallback_
测试代码
测试代码也很好理解,就是做了如下几件事:
1.创建TcpServer类的对象;
2.通过该TcpServer类的对象调用TcpServer::setConnectionCallback、TcpServer::setMessageCallback,设置TcpServer::connectionCallback_和TcpServer::messageCallback_;
3.通过该TcpServer类的对象调用TcpServer::start
Reactor_test08.cc
#include <muduo/net/TcpServer.h>
#include <muduo/net/EventLoop.h>
#include <muduo/net/InetAddress.h>
#include <stdio.h>
using namespace muduo;
using namespace muduo::net;
//自定义的新连接到来时的回调函数
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,
const char* data,
ssize_t len)
{
printf("onMessage(): received %zd bytes from connection [%s]\n",
len, conn->name().c_str());
}
int main()
{
printf("main(): pid = %d\n", getpid());
InetAddress listenAddr(8888);
EventLoop loop;
TcpServer server(&loop, listenAddr, "TestServer");
server.setConnectionCallback(onConnection);
server.setMessageCallback(onMessage);
server.start();
loop.loop();
}
作者在这里又进一步封装,多搞出了一个TestServer类,类中记录了所属的IO线程(即所属的EventLoop对象,one loop per thread),使程序相对于Reactor_test08.cc整体看起来更加清爽、简洁。
Reactor_test09.cc
#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;
//作者在这里又进一步封装,多搞出了一个TestServer类,类中记录了所属的IO线程(即所属的EventLoop对象,one loop per thread),使程序整体看起来更加清爽、简洁。
class TestServer
{
public:
TestServer(EventLoop* loop,
const InetAddress& listenAddr)
: loop_(loop),
server_(loop, listenAddr, "TestServer")
{
server_.setConnectionCallback(
boost::bind(&TestServer::onConnection, this, _1));//类的成员函数使用bind
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,
const char* data,
ssize_t len)
{
printf("onMessage(): received %zd bytes from connection [%s]\n",
len, conn->name().c_str());
}
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();
}
新开一个终端:
原终端:
新终端中发送消息:
原终端:
源码
Callbacks.h
// This is a public header file, it must only include public header files.
#ifndef MUDUO_NET_CALLBACKS_H
#define MUDUO_NET_CALLBACKS_H
#include <boost/function.hpp>
#include <boost/shared_ptr.hpp>
#include <muduo/base/Timestamp.h>
namespace muduo
{
/*
// Adapted from google-protobuf stubs/common.h
// see License in muduo/base/Types.h
template<typename To, typename From>
inline ::boost::shared_ptr<To> down_pointer_cast(const ::boost::shared_ptr<From>& f) {
if (false) {
implicit_cast<From*, To*>(0);
}
#ifndef NDEBUG
assert(f == NULL || dynamic_cast<To*>(get_pointer(f)) != NULL);
#endif
return ::boost::static_pointer_cast<To>(f);
}*/
namespace net
{
// All client visible callbacks go here.
/*
class Buffer;
*/
class TcpConnection;
typedef boost::shared_ptr<TcpConnection> TcpConnectionPtr;
typedef boost::function<void()> TimerCallback;
typedef boost::function<void (const TcpConnectionPtr&)> ConnectionCallback;
/*typedef boost::function<void (const TcpConnectionPtr&)> CloseCallback;
typedef boost::function<void (const TcpConnectionPtr&)> WriteCompleteCallback;
typedef boost::function<void (const TcpConnectionPtr&, size_t)> HighWaterMarkCallback;
// the data has been read to (buf, len)
typedef boost::function<void (const TcpConnectionPtr&,
Buffer*,
Timestamp)> MessageCallback;
void defaultConnectionCallback(const TcpConnectionPtr& conn);
void defaultMessageCallback(const TcpConnectionPtr& conn,
Buffer* buffer,
Timestamp receiveTime);
*/
typedef boost::function<void (const TcpConnectionPtr&,
const char* data,
ssize_t len)> MessageCallback;
}
}
#endif // MUDUO_NET_CALLBACKS_H
TcpServer.h
// This is a public header file, it must only include public header files.
#ifndef MUDUO_NET_TCPSERVER_H
#define MUDUO_NET_TCPSERVER_H
#include <muduo/base/Types.h>
#include <muduo/net/TcpConnection.h>
#include <map>
#include <boost/noncopyable.hpp>
#include <boost/scoped_ptr.hpp>
namespace muduo
{
namespace net
{
class Acceptor;
class EventLoop;
///
/// TCP server, supports single-threaded and thread-pool models.
///
/// This is an interface class, so don't expose too much details.
class TcpServer : boost::noncopyable
{
public:
//typedef boost::function<void(EventLoop*)> ThreadInitCallback;
//TcpServer(EventLoop* loop, const InetAddress& listenAddr);
TcpServer(EventLoop* loop,
const InetAddress& listenAddr,
const string& nameArg);
~TcpServer(); // force out-line dtor, for scoped_ptr members.
const string& hostport() const { return hostport_; }
const string& name() const { return name_; }
/// Starts the server if it's not listenning.
///
/// It's harmless to call it multiple times.
/// Thread safe.
void start();
/// Set connection callback.
/// Not thread safe.
// 设置连接到来或者连接关闭回调函数
void setConnectionCallback(const ConnectionCallback& cb)
{ connectionCallback_ = cb; }
/// Set message callback.
/// Not thread safe.
// 设置消息到来回调函数
void setMessageCallback(const MessageCallback& cb)
{ messageCallback_ = cb; }
private:
/// Not thread safe, but in loop
void newConnection(int sockfd, const InetAddress& peerAddr);
typedef std::map<string, TcpConnectionPtr> ConnectionMap;//typedef boost::shared_ptr<TcpConnection> TcpConnectionPtr;
EventLoop* loop_; // the acceptor loop //Acceptor对象acceptor_所属的EventLoop
const string hostport_; // 服务端口
const string name_; // 服务名
boost::scoped_ptr<Acceptor> acceptor_; // avoid revealing Acceptor //TcpServer类与Acceptor类是组合关系
ConnectionCallback connectionCallback_;//连接到来时的回调函数
MessageCallback messageCallback_;//消息到来时的回调函数
bool started_;
// always in loop thread
int nextConnId_; // 下一个连接ID
ConnectionMap connections_; // 连接列表 //typedef std::map<string, boost::shared_ptr<TcpConnection> TcpConnectionPtr> ConnectionMap;
};
}
}
#endif // MUDUO_NET_TCPSERVER_H
TcpServer.cc
#include <muduo/net/TcpServer.h>
#include <muduo/base/Logging.h>
#include <muduo/net/Acceptor.h>
#include <muduo/net/EventLoop.h>
#include <muduo/net/SocketsOps.h>
#include <boost/bind.hpp>
#include <stdio.h> // snprintf
using namespace muduo;
using namespace muduo::net;
TcpServer::TcpServer(EventLoop* loop,
const InetAddress& listenAddr,
const string& nameArg)
: loop_(CHECK_NOTNULL(loop)),
hostport_(listenAddr.toIpPort()),
name_(nameArg),
acceptor_(new Acceptor(loop, listenAddr)),//重要!创建Acceptor类的对象acceptor_(Acceptor类与TcpServer类是组合关系)
started_(false),
nextConnId_(1)
{
//Acceptor::handleRead函数中会回调用应用层回调函数 Acceptor::newConnectionCallback_
//这里通过Acceptor::setNewConnectionCallback将应用层回调函数newConnectionCallback_设置为TcpServer::newConnection
//_1对应的是socket文件描述符,_2对应的是对等方的地址(InetAddress)
acceptor_->setNewConnectionCallback(
boost::bind(&TcpServer::newConnection, this, _1, _2));
}
TcpServer::~TcpServer()
{
loop_->assertInLoopThread();
LOG_TRACE << "TcpServer::~TcpServer [" << name_ << "] destructing";
}
// 该函数多次调用是无害的
// 该函数可以跨线程调用!
void TcpServer::start()
{
if (!started_)
{
started_ = true;
}
if (!acceptor_->listenning())//判断Acceptor类的对象acceptor_是否处于监听的状态
{
// get_pointer返回原生指针
//如果acceptor_未处于监听的状态,那么通过EventLoop::runInLoop向IO线程布置任务,即让IO线程通过acceptor_的指针调用Acceptor::listen
loop_->runInLoop(
boost::bind(&Acceptor::listen, get_pointer(acceptor_)));//类的成员函数使用boost::bind
}
}
void TcpServer::newConnection(int sockfd, const InetAddress& peerAddr)
{
loop_->assertInLoopThread();//断言当前在IO线程中
char buf[32];
snprintf(buf, sizeof buf, ":%s#%d", hostport_.c_str(), nextConnId_);//格式化字符串
++nextConnId_;
string connName = name_ + buf;
LOG_INFO << "TcpServer::newConnection [" << name_
<< "] - new connection [" << connName
<< "] from " << peerAddr.toIpPort();
InetAddress localAddr(sockets::getLocalAddr(sockfd));//获取已连接套接字的本地地址
// FIXME poll with zero timeout to double confirm the new connection
// FIXME use make_shared if necessary
TcpConnectionPtr conn(new TcpConnection(loop_,
connName,
sockfd,
localAddr,
peerAddr));//创建TcpConnection类的对象,并创建其对应的智能指针对象
connections_[connName] = conn;//加入std::map(即连接列表)中
conn->setConnectionCallback(connectionCallback_);//设置TcpConnection类的连接到来时的回调函数
conn->setMessageCallback(messageCallback_);//设置TcpConnection类的消息到来时的回调函数
conn->connectEstablished();//调用TcpConnection::connectEstablished
}
TcpConnection.h
// This is a public header file, it must only include public header files.
#ifndef MUDUO_NET_TCPCONNECTION_H
#define MUDUO_NET_TCPCONNECTION_H
#include <muduo/base/Mutex.h>
#include <muduo/base/StringPiece.h>
#include <muduo/base/Types.h>
#include <muduo/net/Callbacks.h>//Callbacks.h中包含了各个回调函数类型的typedef
//#include <muduo/net/Buffer.h>
#include <muduo/net/InetAddress.h>
//#include <boost/any.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/noncopyable.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/shared_ptr.hpp>
namespace muduo
{
namespace net
{
class Channel;
class EventLoop;
class Socket;
///
/// TCP connection, for both client and server usage.
///
/// This is an interface class, so don't expose too much details.
//TcpConnection类与Acceptor类是类似的,都有两个重要的数据成员:Socket类的对象和Channel类的对象
class TcpConnection : boost::noncopyable,
public boost::enable_shared_from_this<TcpConnection>
{
public:
/// Constructs a TcpConnection with a connected sockfd
///
/// User should not create this object.
TcpConnection(EventLoop* loop,
const string& name,
int sockfd,
const InetAddress& localAddr,
const InetAddress& peerAddr);
~TcpConnection();
EventLoop* getLoop() const { return loop_; }
const string& name() const { return name_; }
const InetAddress& localAddress() { return localAddr_; }
const InetAddress& peerAddress() { return peerAddr_; }
bool connected() const { return state_ == kConnected; }
void setConnectionCallback(const ConnectionCallback& cb)
{ connectionCallback_ = cb; }
void setMessageCallback(const MessageCallback& cb)
{ messageCallback_ = cb; }
// called when TcpServer accepts a new connection
void connectEstablished(); // should be called only once
private:
enum StateE { /*kDisconnected, */kConnecting, kConnected/*, kDisconnecting*/ };//连接的状态
void handleRead(Timestamp receiveTime);
void setState(StateE s) { state_ = s; }
EventLoop* loop_; //Channel对象channel_所属的EventLoop
string name_; // 连接名
StateE state_; // FIXME: use atomic variable
// we don't expose those classes to client.
boost::scoped_ptr<Socket> socket_;
boost::scoped_ptr<Channel> channel_;
InetAddress localAddr_;
InetAddress peerAddr_;
ConnectionCallback connectionCallback_;
MessageCallback messageCallback_;
};
typedef boost::shared_ptr<TcpConnection> TcpConnectionPtr;
}
}
#endif // MUDUO_NET_TCPCONNECTION_H
TcpConnection.cc
#include <muduo/net/TcpConnection.h>
#include <muduo/base/Logging.h>
#include <muduo/net/Channel.h>
#include <muduo/net/EventLoop.h>
#include <muduo/net/Socket.h>
#include <muduo/net/SocketsOps.h>
#include <boost/bind.hpp>
#include <errno.h>
#include <stdio.h>
using namespace muduo;
using namespace muduo::net;
TcpConnection::TcpConnection(EventLoop* loop,
const string& nameArg,
int sockfd,
const InetAddress& localAddr,
const InetAddress& peerAddr)
: loop_(CHECK_NOTNULL(loop)),
name_(nameArg),
state_(kConnecting),
socket_(new Socket(sockfd)),
channel_(new Channel(loop, sockfd)),
localAddr_(localAddr),
peerAddr_(peerAddr)
{
//通过Channel对象调用Channel::setReadCallback将TcpConnection::handleRead设置为读事件回调函数,_1是事件发生时间
channel_->setReadCallback(
boost::bind(&TcpConnection::handleRead, this, _1));
LOG_DEBUG << "TcpConnection::ctor[" << name_ << "] at " << this
<< " fd=" << sockfd;
socket_->setKeepAlive(true);
}
TcpConnection::~TcpConnection()
{
LOG_DEBUG << "TcpConnection::dtor[" << name_ << "] at " << this
<< " fd=" << channel_->fd();
}
void TcpConnection::connectEstablished()
{
loop_->assertInLoopThread();
assert(state_ == kConnecting);
setState(kConnected);
channel_->tie(shared_from_this());
channel_->enableReading(); //调用 Channel::enableReading 注册可读事件(即把TcpConnection关注的事件挂到epoll的红黑树上去)
connectionCallback_(shared_from_this());
}
void TcpConnection::handleRead(Timestamp receiveTime)
{
loop_->assertInLoopThread();
char buf[65536];
ssize_t n = ::read(channel_->fd(), buf, sizeof buf);
messageCallback_(shared_from_this(), buf, n);//调用消息到来时的回调函数
}