muduo網路庫學習筆記(12):TcpServer和TcpConnection類
TcpServer類的主要功能是管理accept(2)獲得的TcpConnection,TcpConnection則表示的是“一次TCP連線”,一旦連線斷開,這個TcpConnection物件就沒啥用了。
由於TcpConnection類較複雜,本篇我們先學習TcpConnection類關於連線建立的處理。
當有新連線到來時,TcpServer新建連線的相關函式的呼叫時序圖如下:
分析:當一個連線到來,EventLoop的事件迴圈loop()函式返回一個活躍的通道Channel,然後呼叫Channel的handleEvent()函式來處理這個事件。連線到來屬於可讀事件,又會回撥Acceptor的handleRead()函式,在handleRead()函式中又呼叫accept()來接受這個新的連線,然後又回撥了TcpServer的newConnection()函式。newConnection()函式會建立一個TcpConnection物件,然後這個物件呼叫TcpConnection的成員函式connectEstablished(),在這個函式中,回撥了使用者設定的連線到來回撥函式connCb()。
TcpServer類
TcpServer類的介面簡單易懂,如下:
程式碼片段1:TcpServer的介面
檔名:TcpServer.h
class TcpServer : boost::noncopyable
{
public:
// 建構函式,InetAddress是對網際地址sockaddr_in的封裝
TcpServer(EventLoop* loop,
const InetAddress& listenAddr,
const string& nameArg);
// 解構函式
~TcpServer();
const string& hostport() const { return hostport_; }
const string& name() const { return name_; }
void start(); // 啟動TcpServer
// 設定連線到來或者連線關閉回撥函式
void setConnectionCallback(const ConnectionCallback& cb)
{ connectionCallback_ = cb; }
// 設定訊息到來回撥函式
void setMessageCallback(const MessageCallback& cb)
{ messageCallback_ = cb; }
private:
// 此函式會建立TcpConnection物件,下面會著重分析
void newConnection(int sockfd, const InetAddress& peerAddr);
// 連線列表
// key為TcpConnection的名字,value是指向TcpConnectinon物件的指標
typedef std::map<string, TcpConnectionPtr> ConnectionMap;
EventLoop* loop_;
const string hostport_; // 服務埠
const string name_; // 服務名
boost::scoped_ptr<Acceptor> acceptor_; // Acceptor中有對accept()的封裝,獲得新的連線
// 使用者提供的ConnectionCallback和MessageCallback
// 在新建TcpConnection時會原樣傳給後者
ConnectionCallback connectionCallback_;
MessageCallback messageCallback_;
bool started_; // TcpServer是否啟動
int nextConnId_; // 下一個連線ID
ConnectionMap connections_; // 連線列表
};
程式碼片段2:TcpServer::newConnection()
檔名:TcpServer.cc
void TcpServer::newConnection(int sockfd, const InetAddress& peerAddr)
{
loop_->assertInLoopThread();
char buf[32];
snprintf(buf, sizeof buf, ":%s#%d", hostport_.c_str(), nextConnId_);
++nextConnId_;
// 連線名稱以 服務名+服務埠+連線ID 格式命名
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,此處使用make_shared()可以節約一次new
// 建立TcpConnection物件
TcpConnectionPtr conn(new TcpConnection(loop_,
connName,
sockfd,
localAddr,
peerAddr));
// 把TcpConnection物件加入ConnectionMap
connections_[connName] = conn;
// 設定ConnectionCallback和MessageCallback
conn->setConnectionCallback(connectionCallback_);
conn->setMessageCallback(messageCallback_);
// 此函式會回撥使用者提供的ConnectionCallback
conn->connectEstablished();
}
TcpConnection類
本篇暫只討論TcpConnection類關於連線建立的處理,介面也很簡單:
程式碼片段3:TcpConnection的介面
檔名:TcpConnection.h
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 { kConnecting, kConnected }; // 連線狀態
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_;
};
程式碼片段4:TcpConnection.cc(處理連線建立事件)
檔名: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::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)
{
// 通道可讀事件到來的時候,回撥TcpConnection::handleRead,_1是事件發生時間
channel_->setReadCallback(
boost::bind(&TcpConnection::handleRead, this, _1));
LOG_DEBUG << "TcpConnection::ctor[" << name_ << "] at " << this
<< " fd=" << sockfd;
socket_->setKeepAlive(true); // 設定SO_KEEPALIVE選項
}
// 解構函式
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(); // 將TcpConnection所對應的通道加入到Poller關注
// 回撥使用者設定的函式
connectionCallback_(shared_from_this());
}
void TcpConnection::handleRead(Timestamp receiveTime)
{
loop_->assertInLoopThread();
char buf[65536];
// 呼叫read將訊息讀到buf中
ssize_t n = ::read(channel_->fd(), buf, sizeof buf);
messageCallback_(shared_from_this(), buf, n);
}
示例
測試程式:
#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); // 構造地址INADDR_ANY,埠號為8888
EventLoop loop; // 構造一個EventLoop物件
TcpServer server(&loop, listenAddr, "TestServer"); // 構造一個TcpServer物件,傳入EventLoop物件、地址和服務名
// 設定回撥函式
server.setConnectionCallback(onConnection);
server.setMessageCallback(onMessage);
// 啟動TcpServer
server.start();
// 啟動事件迴圈
loop.loop();
}
測試結果:
1.啟動我們的測試服務
2.客戶端使用telnet發起連線
3.TcpServer接受新連線,處理連線到來事件
4.斷開客戶端連線,由於還未實現對連線斷開事件的處理,服務端會陷入busy loop狀態
相關文章
- muduo網路庫學習筆記(13):TcpConnection生命期的管理筆記TCP
- muduo網路庫學習之EventLoop(三):Socket、Acceptor、TcpServer、TcpConnection(連線建立,接收訊息)OOPTCPServer
- muduo網路庫學習筆記(1):Timestamp類筆記
- muduo網路庫學習筆記(3):Thread類筆記thread
- muduo網路庫學習筆記(8):高效日誌類的封裝筆記封裝
- muduo網路庫學習筆記(2):原子性操作筆記
- muduo網路庫學習之EventLoop(六):TcpConnection::send()、shutdown()、handleRead()、handleWrite()OOPTCP
- muduo網路庫學習筆記(6):單例類(執行緒安全的)筆記單例執行緒
- muduo網路庫學習筆記(14):chargen服務示例筆記
- muduo網路庫學習筆記(4):互斥量和條件變數筆記變數
- muduo網路庫學習之muduo_http 庫涉及到的類HTTP
- muduo網路庫學習筆記(11):有用的runInLoop()函式筆記OOP函式
- muduo網路庫學習之muduo_inspect 庫涉及到的類
- muduo網路庫學習之EventLoop(五):TcpConnection生存期管理(連線關閉)OOPTCP
- muduo網路庫學習筆記(10):定時器的實現筆記定時器
- muduo網路庫學習筆記(7):執行緒特定資料筆記執行緒
- muduo網路庫學習筆記(9):Reactor模式的關鍵結構筆記React模式
- muduo網路庫學習筆記(5):執行緒池的實現筆記執行緒
- muduo網路庫學習之EventLoop(四):EventLoopThread 類、EventLoopThreadPool 類OOPthread
- muduo網路庫學習筆記(15):關於使用stdio和iostream的討論筆記iOS
- muduo網路庫Timestamp類
- muduo網路庫學習之EventLoop(一):事件迴圈類圖簡介和muduo 定時器TimeQueueOOP事件定時器
- muduo網路庫Exception異常類Exception
- muduo網路庫學習之ThreadLocal 類、ThreadLocalSingleton類封裝知識點thread封裝
- muduo網路庫學習之EventLoop(七):TcpClient、ConnectorOOPTCPclient
- muduo網路庫學習之Timestamp類、AtomicIntegerT 類封裝中的知識點封裝
- muduo網路庫AtomicIntegerT原子整數類
- muduo網路庫學習之MutexLock類、MutexLockGuard類、Condition類、CountDownLatch類封裝中的知識點MutexCountDownLatch封裝
- muduo網路庫學習之BlockinngQueue類、ThreadPool 類、Singleton類封裝中的知識點BloCthread封裝
- 以太坊學習筆記————12、搭建測試網路和私有鏈筆記
- muduo網路庫學習之Logger類、LogStream類、LogFile類封裝中的知識點封裝
- 計網學習筆記(12)- HTTP筆記HTTP
- 【學習筆記】網路流筆記
- [網路]NIO學習筆記筆記
- 網路流學習筆記筆記
- muduo網路庫學習之EventLoop(二):程式(執行緒)wait/notify 和 EventLoop::runInLoopOOP執行緒AI
- muduo網路庫使用心得
- muduo網路庫學習筆記(0):物件導向程式設計風格和基於物件程式設計風格的比較筆記物件程式設計