天上午很不幸碰到一個select的問題,幸好以前對網絡I/O處理的模塊進行了封裝,今天下午很快就完成了一個基於poll的I/O處理封裝,
只需要進行1行代碼的替換,就可以輕鬆從select架構轉換到poll,同理也可以轉換到epoll。
自己可以隨心所欲的切換網絡I/O處理架構!!!
這裏是接口,使用的是模板,這裏沒有使用C++的繼承,有如下幾點考慮:
(1) 效率, 模板實現的多態是發生在編譯的時候,而繼承重載虛函數是通過程序運行的時候,根據vtable指針對函數的實際地址進行尋址,
當然比模板慢, 而且還可能會增加4字節的vptr指針
(2) 面向接口編程: 繼承一般是有實際的邏輯含義的對象的使用,比如人,黑人之間。
而我們的網絡處理模型,只是不同的處理方法,所以用模板更好一些
下面的代碼請放到一個頭文件中,不然存在編譯問題,詳情請見<<C++templates>>第6章
這裏對fd的訪問採用的是迭代模式,必須先啓動開始迭代,讓後才能依次迭代。
具體的網絡模型對fd存放形式對於調用者是不可見的, 只能獲取迭代指針,訪問網絡模型的內部fd列表。
各個實現類內存的數據結構自己是可以優化的,目前是全部採用數組
template <class T>
class InterfaceIOQuery
{
private:
T member;
public:
//添加一個關心的fd
void remove(int* pFd, int iOption);
//測試該fd是否可讀
bool isCanRead(int* pIterator);
//測試該fd是否可寫
bool isCanWrite(int* pIterator);
//測試該fd是否異常
bool isExcept(int* pIterator);
//設置關心的事件
void setQueryOption(int iOption);
//註冊一個fd
void registerFd(int pFd, int iOption);
//輪詢操作, -1 永久等待, 0輪詢, >0, 按時間輪詢
int selectWithTime(int iTime);
//開始遍歷fd列表
int* getSelectFdBegin();
//取得下一個fd的指針
int* getSelectFdNext();
};
具體的select和poll, 接口的實現請見附件,
使用的範例代碼如下:
//設置關心的事件
m_selector.setQueryOption(OP_READ);
//註冊fd和該fd關心的事件
m_selector.registerFd(m_workPipeFd[0], OP_READ);
int iReady;
for ( ; ; )
{
//進行select操作
iReady = m_selector.selectWithTime(-1);
if (iReady < 0)
{
throw CException(errno);
}
else if (iReady == 0)
{
m_log.writeLog(LOG_INFO, "doSelect()", "work%d do select with 1 second time out", m_iThreadID);
}
else
{
//開始迭代訪問
m_selector.getSelectFdBegin();
int* iterator;
//爲NULL標識訪問完畢
while (NULL != (iterator = m_selector.getSelectFdNext()))
{
if (m_selector.isCanRead(iterator))
{
if (*iterator == m_workPipeFd[0])
{
int iRecive;
int iRead = r_read(*iterator, (void*)(&iRecive), sizeof(iRecive));
if (iRead <= 0)
{
m_log.writeLog(LOG_ERR, "doSelect()", "work%d read pipe %d fail ", m_iThreadID, *iterator);
}
else
{
m_log.writeLog(LOG_INFO, "doSelect()", "work%d recevie fd %d success ", m_iThreadID, iRecive);
m_selector.registerFd(iRecive, OP_READ);
}
}
else
{
replayToClient(iterator);
}
--iReady;
if (0 == iReady)
{
break;
}
}
}
}
}