muduo網路庫原始碼解析 四
這一章節我們首先來解析用到的socket的基礎函式:
1、位元組序轉換函式封裝為HostToNetwork16、HostToNetwork32類似命名,原函式為htons、htonl,封裝後方便記憶與書寫,標頭檔案<netinet/in.h>
2、地址轉換函式toHostPort和fromHostPort,用的是<arpa/inet.h>中的inet_pton和inet_ntop,p和n表示表示式和數值
3、設定檔案描述符為non-blocking和close-on-exec函式:setNonBlockAndCloseOnExec,createNonblockingOrDie
4、地址型別轉換函式,利用C++的型別轉換函式:
const SA* sockaddr_cast(const struct sockaddr_in *addr)
{
return reinterpret_cast<const SA*>(addr);
}
SA* sockaddr_cast(struct sockaddr_in *addr)
{
return reinterpret_cast<SA*>(addr);
}
5、bind、listen都進行了簡單的封裝,加上了處理失敗的情況
6、accept進行了處理,如果不支援accept4函式,則呼叫setNonBlockAndCloseOnExec,將connfd設定為non-blocking和close-on-exec,否則直接呼叫accept4,該函式可以直接將connfd設定為non-blocking和close-on-exec
另外,muduo對socket也進行了封裝,封裝為Socket類,將socket的相關操作進行封裝,比如bind、listen、accept,也提供了返回fd的介面。
下面我們來解析一下TCP建立的過程:
有了以上基礎,我們可以來解析Acceptor類,該類是個內部類,供TcpServer使用,用於接受新連線,並通過回撥通知使用者。該類比較簡單,提供三個public成員函式:
第一個用於設定接受新連線的回撥函式,第二個返回狀態,第三個設定socket開始監聽。void setNewConnectionCallback(const newConnectionCallback &cb) { newConnectionCallback_ = cb; } bool listening() { return listening_; } void listen();
void Acceptor::listen()
{
loop_->assertInLoopThread();
listening_ = true;
acceptSocket_.listen();
acceptChannel_.enableReading();
}
利用Channel,將其加入Poller的監聽佇列裡。
該類含有一個處理連線到來的函式,即handleRead:
void Acceptor::handleRead()
{
loop_->assertInLoopThread();
InetAddress peerAddr(0);
int connfd = acceptSocket_.accept(&peerAddr);
if (connfd > 0)
{
newConnectionCallback_(connfd, peerAddr);
}
}
該函式是個私有型別,在建構函式裡,會將此函式bind到Channel的readCallback_上,當對應的fd可讀時,說明有新連線到來,即執行此回撥函式
接著來解析TcpServer類,這個類是對使用者開放,用於處理新建的TcpConnection。TcpServer類的核心函式有兩個,都是私有:
void newConnection(int sockfd, const InetAddress& peerAddr);
void removeConnection(const TcpConnectionPtr &conn);
一個用來處理新建的連線,一個用來斷開連線(被動關閉),相關的資料結構為:
typedef std::map<std::string, TcpConnectionPtr> ConnectionMap;
std::unique_ptr<Acceptor> acceptor_;
int nextConnId_;
ConnectionMap connections_;
connections_用來儲存已經建立的連線,key為nextConnId_,value為TcpConnection的指標。具體操作為:
acceptor_->setNewConnectionCallback(
std::bind(&TcpServer::newConnection, this,
std::placeholders::_1, std::placeholders::_2)
);
在建構函式裡,將TcpServer的newConnection成員函式bind到Acceptor的newConnectionCallback_上,用於回撥。newConnection函式新建一個TcpConnection物件,並將其加入connections_,並設定相應回撥:連線到來回調,訊息到來回調,連線關閉回撥。這裡調來調去,讀者可能會頭暈,下一小節講完TcpConnection後,會進行專門梳理。void TcpServer::newConnection(int sockfd, const InetAddress &peerAddr)
{
loop_->assertInLoopThread();
char buff[32];
snprintf(buff, sizeof buff, "#%d", nextConnId_);
++nextConnId_;
std::string connName = name_ + buff;
std::cout << "TcpServer::newConnection [" << name_ << "] - new connection ["
<< connName << "] from " << peerAddr.toHostPort() << "\n";
InetAddress localAddr(basic::getLocalAddr(sockfd));
TcpConnectionPtr conn(
new TcpConnection(loop_, connName, sockfd, localAddr, peerAddr)
);
connections_[connName] = conn;
conn->setConnectionCallback(connectionCallback_);
conn->setMessageCallback(messageCallback_);
conn->setCloseCallback(
std::bind(&TcpServer::removeConnection,this,std::placeholders::_1)
);
conn->connectEstablished();
}
OK ,,,,over,下一小節講解最難最多的TcpConnection類