1. 程式人生 > >muduo網路庫原始碼解析 四

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成員函式:

void setNewConnectionCallback(const newConnectionCallback &cb)
{
	newConnectionCallback_ = cb;
}

bool listening() { return listening_; }
void listen();
第一個用於設定接受新連線的回撥函式,第二個返回狀態,第三個設定socket開始監聽。
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類