用多路複用I/O模型實現支持多個客戶端的通信服務


引言

  多路複用I/O模型是UNIX/LINUX用得的最多的一種I/O模型。這種I/O模型在技術上的實現是包括select()以及FD_XXX的幾個宏及常量。在單個進程中支持的客戶端數量由FD_SETSIZE決定。Solaris 10和Linux 9.0默認爲1024個,Windows 2000是64個。本文用代碼給出該I/O模型處理多Client的一種實現。

用select開發一個通信服務器

  這個服務器包括接收模塊、發送模塊和套接口管理模塊。我們只演示與select實現有關的部分,即接收模塊和套接口管理模塊。下面服務器類關係圖:



  tcp_listen是tcp實現監聽功能,tcp_comm實現tcp的發送和接收,下在我們重點在於recv和sock_list這兩個類。sock_list定義如下:

class sock_list  
{
public:
	// 功能:將一個值插入到list的尾部
	// 參數:要插入的tcp_comm對象
	void insert(tcp_comm* t);

	// 功能:刪除一個指定的元素
	// 參數:指向要刪除的tcp_comm對象的指針
	void remove(tcp_comm* t);

	// 功能:從隊列取得所有的元素
	// 參數:存放指向tcp_comm對象指針的向量
	void get_all(std::vector<tcp_comm*>& vec);
private:
	std::list<tcp_comm*> _sock_list;
};

sock_list實現tcp_comm對象的管理。下面我們來看recv類的定義:

class recv
{
public:
	// 功能: 啓動通信服務
	bool start();
private:	
	// 功能: 接收核心函數,通過多路複用支持多個客戶端
	// 返回值: 正常, 0; 失敗,-1
	int do_select();
   
	// 初始化套接口集合
	void init_readfds(const vector<tcp*>& vec, fd_set* set);
private:	
	sock_list* _list;
	tcp_listen* _listen;  // 監聽套接口
};

  start()是給外部調用的:創建監聽套接口對象;監聽;執行do_select()。init_readfds將監聽套接口對象和socket_list中的通信套接口對象加入到套接口讀套接口集合中。do_select()比較重要,流程:
用init_readfds初始化讀套接口集合;
執行select(),返回進行到下一步;
先檢測監聽套接口是否有連接到來,並將新的連接加入到sock_list中;
再檢測sock_list中的套接口是否有數據可接收。
實現代碼:

int recv::do_select()
{
	fd_set readfds;
	int listen = _listen->get_handle();

	while (true)
	{
		vector<tcp_comm*> vec;
	    _list->get_all(vec);    // 獲得sock_list中的所有套接口句柄
		init_readfds(vec, &readfds);

		select(...);
		// 檢查監聽套接字句柄,有新的連接到,建立新的連接
		if (FD_ISSET(listen, &readfds))   
		{
			tcp_comm* client = new tcp_comm;
			_listen->accept(*client);
			_list->insert(client);// 將已連接的套接字對象放到sock_list中
		}

		// 檢查所有的客戶端套接字句柄
		vector<tcp_comm*>::iterator it;
		for (it = vec.begin(); it != vec.end(); it++) 
		{
			if (FD_ISSET((*it)->get_handle(), &readfds))
				// 這裏是*it作爲參數進行數據接收
		}
	}
}

爲減少代碼量,我省去了出錯處理、細節;本來類都在某個名稱空間中的,也省去了。

多進程(線程)和select合作

  上面是在一個進程中的情況,可以考慮多線程中每一個線程實現select。以solaris 10爲例,開100個線程,一個線程支持1024個套接口,那就是100 * 1024,10萬個。集羣服務中的前端均衡器的實現可以考慮一下這個技術,不過效率將是一個極大的考驗。

結論

  本文重點講述在單進程的環境使用select支持多個客戶端。經常看見一些服務程序,用到了select也是來一個連接,fork一個子進程處理。在單進程中處理多個客戶端的目的是使開發的程序更易理解、維護;多進程往往較單進程複雜些。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章