-
Acceptor类的主要功能是socket、bind、listen
-
一般来说,在上层应用程序中,我们不直接使用Acceptor,而是把它作为TcpServer的成员
-
TcpServer还包含了一个TcpConnection列表
-
TcpConnection与Acceptor类似,有两个重要的数据成员,Socket与Channel
时序图
TcpServer头文件
TcpServer.h
// Copyright 2010, Shuo Chen. All rights reserved. // http://code.google.com/p/muduo/ // // Use of this source code is governed by a BSD-style license // that can be found in the License file. // Author: Shuo Chen (chenshuo at chenshuo dot com) // // 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 // Acceptor::handleRead函数中会回调用TcpServer::newConnection // _1对应的是socket文件描述符,_2对应的是对等方的地址(InetAddress) //应该还在IO线程里面 // newConnection和connectionCallback_都是连接回调函数,但是connectionCallback_ // 是给应用程序使用的,newConnection是库函数,不会暴露给用户。 // 其实就是newConnection函数主要负责注册connectionCallback_ 函数 void newConnection(int sockfd, const InetAddress& peerAddr); typedef std::map<string, TcpConnectionPtr> ConnectionMap; EventLoop* loop_; // the acceptor's loop,不一定是连接所属的eventloop const string hostport_; // 服务端口 const string name_; // 服务名 boost::scoped_ptr<Acceptor> acceptor_; // avoid revealing Acceptor /*连接到来的回调函数*/ ConnectionCallback connectionCallback_; /*消息到来的回调函数*/ MessageCallback messageCallback_; bool started_; /*连接是否启动*/ // always in loop thread int nextConnId_; // 下一个连接ID ConnectionMap connections_; // 连接列表 }; } } #endif // MUDUO_NET_TCPSERVER_H
TcpServer源文件
TcpServer.cc
// Copyright 2010, Shuo Chen. All rights reserved. // http://code.google.com/p/muduo/ // // Use of this source code is governed by a BSD-style license // that can be found in the License file. // Author: Shuo Chen (chenshuo at chenshuo dot com) #include <muduo/net/TcpServer.h> #include <muduo/base/Logging.h> #include <muduo/net/Acceptor.h> #include <muduo/net/EventLoop.h> //#include <muduo/net/EventLoopThreadPool.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)), /*threadPool_(new EventLoopThreadPool(loop)), connectionCallback_(defaultConnectionCallback), messageCallback_(defaultMessageCallback),*/ started_(false), nextConnId_(1) { // Acceptor::handleRead函数中会回调用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; } /*如果还没有被执行,那么让他在IO线程中执行listen函数*/ if (!acceptor_->listenning()) { // get_pointer返回原生指针 loop_->runInLoop( boost::bind(&Acceptor::listen, get_pointer(acceptor_))); } } void TcpServer::newConnection(int sockfd, const InetAddress& peerAddr) { loop_->assertInLoopThread(); 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)); connections_[connName] = conn; conn->setConnectionCallback(connectionCallback_); conn->setMessageCallback(messageCallback_); conn->connectEstablished(); }
TcpConnection头文件
TcpConnection.h
// Copyright 2010, Shuo Chen. All rights reserved. // http://code.google.com/p/muduo/ // // Use of this source code is governed by a BSD-style license // that can be found in the License file. // Author: Shuo Chen (chenshuo at chenshuo dot com) // // 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> //#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. 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_; // 所属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源文件
TcpConnection.cc
// Copyright 2010, Shuo Chen. All rights reserved. // http://code.google.com/p/muduo/ // // Use of this source code is governed by a BSD-style license // that can be found in the License file. // Author: Shuo Chen (chenshuo at chenshuo dot com) #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; /* void muduo::net::defaultConnectionCallback(const TcpConnectionPtr& conn) { LOG_TRACE << conn->localAddress().toIpPort() << " -> " << conn->peerAddress().toIpPort() << " is " << (conn->connected() ? "UP" : "DOWN"); } void muduo::net::defaultMessageCallback(const TcpConnectionPtr&, Buffer* buf, Timestamp) { buf->retrieveAll(); } */ 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)/*, highWaterMark_(64*1024*1024)*/ { // 通道可读事件到来的时候,回调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() { //断言在eventloop IO线程中 loop_->assertInLoopThread(); //断言还没有连接 assert(state_ == kConnecting); setState(kConnected); channel_->tie(shared_from_this()); /*如果连接成功,则关注可读事件*/ channel_->enableReading(); // TcpConnection所对应的通道加入到Poller关注 connectionCallback_(shared_from_this()); } void TcpConnection::handleRead(Timestamp receiveTime) { /* loop_->assertInLoopThread(); int savedErrno = 0; ssize_t n = inputBuffer_.readFd(channel_->fd(), &savedErrno); if (n > 0) { messageCallback_(shared_from_this(), &inputBuffer_, receiveTime); } else if (n == 0) { handleClose(); } else { errno = savedErrno; LOG_SYSERR << "TcpConnection::handleRead"; handleError(); } */ loop_->assertInLoopThread(); char buf[65536]; ssize_t n = ::read(channel_->fd(), buf, sizeof buf); messageCallback_(shared_from_this(), buf, n); }
CallBack源文件
CallBack.cc
// Copyright 2010, Shuo Chen. All rights reserved. // http://code.google.com/p/muduo/ // // Use of this source code is governed by a BSD-style license // that can be found in the License file. // Author: Shuo Chen (chenshuo at chenshuo dot com) // // 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
测试程序1
#include <muduo/net/TcpServer.h> #include <muduo/net/EventLoop.h> #include <muduo/net/InetAddress.h> #include <stdio.h> /* 这个程序主要用来测试TcpServer、TcpConnection 由于这里的TcpServer还没有实现关闭的事件处理,所以当对等放关闭时,服务器会一直处于高电平状态, 也就是说会一直触发 **/ 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(); }
程序输出:
客户端
aaahuiahsdui crosoft Telnet Client ????????haracter is 'CTRL+]' sadasd asduhasuidhuiahsdiahsdjksdjfhsdiohahsoidoasudguagsdhjgashjdgajhksd as d a s d sad Microsoft Telnet> quit C:\Users\LaoHan>
服务器端
^Cubuntu@ubuntu-virtual-machine:~/pro/33/jmuduo$ ../build/debug/bin/reactor_test08 main(): pid = 6066 20131023 01:08:39.593827Z 6066 TRACE updateChannel fd = 4 events = 3 - EPollPoller.cc:104 20131023 01:08:39.594123Z 6066 TRACE EventLoop EventLoop created 0xBFBEBEDC in thread 6066 - EventLoop.cc:62 20131023 01:08:39.594172Z 6066 TRACE updateChannel fd = 5 events = 3 - EPollPoller.cc:104 20131023 01:08:39.594331Z 6066 TRACE updateChannel fd = 6 events = 3 - EPollPoller.cc:104 20131023 01:08:39.594387Z 6066 TRACE loop EventLoop 0xBFBEBEDC start looping - EventLoop.cc:94 20131023 01:08:43.065719Z 6066 TRACE poll 1 events happended - EPollPoller.cc:65 20131023 01:08:43.066560Z 6066 TRACE printActiveChannels {6: IN } - EventLoop.cc:257 20131023 01:08:43.066665Z 6066 INFO TcpServer::newConnection [TestServer] - new connection [TestServer:0.0.0.0:8888#1] from 172.21.95.221:50007 - TcpServer.cc:74 20131023 01:08:43.066762Z 6066 DEBUG TcpConnection TcpConnection::ctor[TestServer:0.0.0.0:8888#1] at 0x9D80468 fd=8 - TcpConnection.cc:56 20131023 01:08:43.066802Z 6066 TRACE updateChannel fd = 8 events = 3 - EPollPoller.cc:104 onConnection(): new connection [TestServer:0.0.0.0:8888#1] from 172.21.95.221:50007 20131023 01:08:43.845782Z 6066 TRACE poll 1 events happended - EPollPoller.cc:65 20131023 01:08:43.845853Z 6066 TRACE printActiveChannels {8: IN } - EventLoop.cc:257 onMessage(): received 1 bytes from connection [TestServer:0.0.0.0:8888#1] 20131023 01:08:44.122627Z 6066 TRACE poll 1 events happended - EPollPoller.cc:65 20131023 01:08:44.122695Z 6066 TRACE printActiveChannels {8: IN } - EventLoop.cc:257 onMessage(): received 1 bytes from connection [TestServer:0.0.0.0:8888#1] 20131023 01:08:44.306479Z 6066 TRACE poll 1 events happended - EPollPoller.cc:65 20131023 01:08:44.306545Z 6066 TRACE printActiveChannels {8: IN } - EventLoop.cc:257 onMessage(): received 1 bytes from connection [TestServer:0.0.0.0:8888#1] onMessage(): received 0 bytes from connection [TestServer:0.0.0.0:8888#1] 20131023 01:08:23.429136Z 6063 TRACE poll 1 events happended - EPollPoller.cc:65 20131023 01:08:23.429156Z 6063 TRACE printActiveChannels {8: IN } - EventLoop.cc:257 onMessage(): received 0 bytes from connection [TestServer:0.0.0.0:8888#1] 20131023 01:08:23.429186Z 6063 TRACE poll 1 events happended - EPollPoller.cc:65 20131023 01:08:23.429205Z 6063 TRACE printActiveChannels {8: IN } - EventLoop.cc:257 onMessage(): received 0 bytes from connection [TestServer:0.0.0.0:8888#1] 20131023 01:08:23.429235Z 6063 TRACE poll 1 events happended - EPollPoller.cc:65 20131023 01:08:23.429253Z 6063 TRACE printActiveChannels {8: IN } - EventLoop.cc:257 onMessage(): received 0 bytes from connection [TestServer:0.0.0.0:8888#1] 20131023 01:08:23.429283Z 6063 TRACE poll 1 events happended - EPollPoller.cc:65 20131023 01:08:23.429302Z 6063 TRACE printActiveChannels {8: IN } - EventLoop.cc:257 onMessage(): received 0 bytes from connection [TestServer:0.0.0.0:8888#1] 20131023 01:08:23.429332Z 6063 TRACE poll 1 events happended - EPollPoller.cc:65 20131023 01:08:23.429351Z 6063 TRACE printActiveChannels {8: IN } - EventLoop.cc:257 onMessage(): received 0 bytes from connection [TestServer:0.0.0.0:8888#1] 20131023 01:08:23.429381Z 6063 TRACE poll 1 events happended - EPollPoller.cc:65 20131023 01:08:23.429399Z 6063 TRACE printActiveChannels {8: IN } - EventLoop.cc:257 onMessage(): received 0 bytes from connection [TestServer:0.0.0.0:8888#1] 20131023 01:08:23.429447Z 6063 TRACE poll 1 events happended - EPollPoller.cc:65 20131023 01:08:23.429466Z 6063 TRACE printActiveChannels {8: IN } - EventLoop.cc:257 onMessage(): received 0 bytes from connection [TestServer:0.0.0.0:8888#1] 20131023 01:08:23.429497Z 6063 TRACE poll 1 events happended - EPollPoller.cc:65 20131023 01:08:23.429515Z 6063 TRACE printActiveChannels {8: IN } - EventLoop.cc:257 onMessage(): received 0 bytes from connection [TestServer:0.0.0.0:8888#1] 20131023 01:08:23.429545Z 6063 TRACE poll 1 events happended - EPollPoller.cc:65
测试程序2
#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, 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(); }