本文內容是遊戲伺服器之被動連線線程池。顧名思義,被動連線線程池處理的是被動連線的會話的狀態管理和資料收發。
設計上:
每個伺服器物件有個接收連線線程池, 執行緒型別包括:
驗證執行緒、同步執行緒、網路接收傳送執行緒、**執行緒。
會話正常流程步驟(正常狀態切換流程):
(1)程式主線程接收連線(監聽連線)
(2)驗證執行緒(檢查驗證超時、驗證發來訊息的伺服器id和伺服器ip(伺服器之間,依靠中心伺服器發來的依賴伺服器列表)、驗證賬號和臨時id和賬號(客戶端登陸和重登陸))
(3)同步執行緒(閘道器發訊息刪除中心伺服器登陸會話,中心伺服器讀資料庫檢查其他伺服器連線的合法性、傳送測試訊息到對端)
(4)網路接收傳送執行緒
(5)**執行緒(**連線)
1、被動連線線程池初始化
2、被動連線池的會話管理
(1)未使用會話到驗證執行緒
(2)驗證會話到同步執行緒
(3)會話到**執行緒
3、網路資料收發
(1)處理網路讀
(1-1)建立讀epoll描述符
(1-2)遍歷所有連線
(1-3)檢查讀事件
(1-4)**需要斷開的連線
(1-5)新增需要讀的連線到epoll描述符
(1-6)處理epoll讀事件(非阻塞檢查)
(1-7)接收有讀事件的連線的資料
(2)處理網路寫
(2-1)成功接收到資料才檢查寫快取(連線的寫事件一直存在)
(2-2)每隔一段時間才檢查寫快取
(2-3)檢查epoll描述符(kdpfd)有事件的連線(寫事件一直存在)
(2-4)再次是否有讀事件(epoll描述符kdpfd)
(2-5)連線的接收資料
(2-6)檢查連線的寫事件(連線的寫事件一直存在,目的檢查的是寫快取)
(2-6)連線寫資料
(3)程序關閉前**所有的會話
4、伺服器關閉銷毀資源
1、被動連線線程池初始化
初始化是在程序主線程的伺服器物件(具體伺服器類物件)的初始化函式裡。
執行緒型別包括:**執行緒、驗證執行緒、同步執行緒、網路接收傳送執行緒(網路接收傳送執行緒也是根據配置有多個)。
執行緒池初始化
bool tcp_session_pool::init()
//建立初始化驗證執行緒
if(!verifythreads.init(1,8,"verify_thread",this))//檢查驗證超時、中心伺服器的伺服器配置驗證(讀資料庫)
//建立初始化同步執行緒
if(!syncthreads.init(1,1,"sync_thread",this))
//建立初始網路接收傳送執行緒(這裡的網路接收傳送執行緒只是處理網路接收和傳送的執行緒)
int maxthreadcount = (maxconns + main_service_thread::getmaxsize() - 1)/main_service_thread::getmaxsize();
g_log->debug("執行緒最大連線數%d,每執行緒連線數%d,執行緒個數%d",maxconns,main_service_thread::getmaxsize(),maxthreadcount);
if(!okaythreads.init(1,maxthreadcount,"main_service_thread",this))
return true;
}2、被動連線池的會話管理
連線的狀態處理是由執行緒池的那些狀態管理執行緒來處理的,連線的狀態會進行轉換,把連線物件交給其他執行緒來管理。
(1)未使用會話到驗證執行緒
bool tcp_session_pool::addverify(tcp_session *task)
else
return true;
}(2)驗證會話到同步執行緒
void tcp_session_pool::addsync(tcp_session *task)
else
}(2)同步會話到網路收發執行緒
bool tcp_session_pool::addokay(tcp_session *task)
g_log->fatal("沒有找到主線程新增任務");
//沒有找到執行緒來處理這個連線,需要**關閉連線
return false;
}(3)會話到**執行緒
void tcp_session_pool::addrecycle(tcp_session *task)
else
}3、網路資料收發
網路收發執行緒的迴圈部分,處理網路資料的接收和傳送。
網路的處理是讀優先的,有讀才檢查寫,寫的檢查是一段時間檢查一次。
有個讀epoll描述符來處理網路資料的接收。
void main_service_thread::run()
task->delepoll(kdpfd, epollin | epollout | epollerr | epollpri);
task->msocket.changesocket(0);
if (task->isfdsradd())
task->addepoll(kdpfd, epollin | epollout | epollerr | epollpri, (void *)task);//kdpfd 裡面一直有所有的連線
}//檢查測試訊號指令(檢查是否需要傳送網路測試訊號(只有簡單包頭的沒有內容的訊息)到客戶端)
task->checksignal(currenttime);
(1-4)**需要斷開的連線
if (task->isterminatewait())//檢查任務是否被中斷,是的話就要設定中斷任務標識
if (task->isterminate())//如果連線被中斷就從epoll中刪除(從epoll描述符中刪除掉對該套接字的監聽),並**該連線
remove(it);
task->getnextstate();//先設定狀態再新增容器
pool->addrecycle(task);
}else
}++it;}}
}check=false;
}thread_base::msleep(2);
(1-6)處理epoll讀事件(非阻塞檢查)
if(fds_count_r)//處理epoll讀事件
else
task->delepoll(kdpfd, epollin | epollout | epollerr | epollpri);}}
else}}
epfds_r[i].events=0;}}
}(2)處理網路寫
(2-1)成功接收到資料才檢查寫快取(連線的寫事件一直存在)
if(check)//套接字出錯或者有讀事件但還沒到讀緩衝區則繼續讀下乙個,否則開始檢查寫快取(連線的寫事件一直存在)
(2-2)每隔一段時間才檢查寫快取
//每隔一段時間才檢視檢查是否有寫事件需要處理
if (currenttime.msec() - _write_time.msec() >= (unsigned long)(pool->usleep_time/1000))
else
task->delepoll(kdpfd, epollin | epollout | epollerr | epollpri);}}
else
}(2-6)檢查連線的寫事件(連線的寫事件一直存在,目的檢查的是寫快取)
if (epfds[i].events & epollout)//處理寫事件}}
epfds[i].events=0;//該事件處理完後去掉該標識}}
}check=true;}}
(3)程序關閉前**所有的會話
**所有的會話:
//執行緒關閉後就先要加入**佇列先把所有的任務**掉
//把所有任務佇列中的連線加入到**佇列中,**這些連線
for(it = tasks.begin(); it != tasks.end();)
temp_failure_retry(::close(kdpfd_r));//關閉epoll描述符
}4、伺服器關閉銷毀資源
server_base::~server_base()
//關閉epoll控制代碼
temp_failure_retry(::close(epoll_handler));
if (-1 != sock_handler)
}連線池刪除時需要關閉所有這些執行緒
void tcp_session_pool::final()
**:
遊戲伺服器設計之任務系統
任務系統是遊戲中最重要的系統之一,本文旨在設計乙個輕量清晰的任務系統。通用易擴充套件是本系統關注的重點。任務系統中當角色的條件滿足時,自動觸發每一型別的任務,每個任務有其所需的完成條件,當角色完成了指定的操作後,則會觸發任務自動完成,任務完成後一般玩家會領取對應的獎勵,結束任務,此任務的生命週期結束...
遊戲伺服器設計之NPC系統
npc系統是遊戲中非常重要的系統,設計的好壞很大程度上影響遊戲的體驗。npc在遊戲中有如下作用 引導玩家體驗遊戲內容,一般遊戲內有很多主線 支線任務,而任務的介紹 接取 領取獎勵等操作都是通過npc的操作,一般會有幾個核心npc,再不停的任務引導中,玩家會對核心npc印象深刻,強化了遊戲代入感。核心...
遊戲伺服器設計之屬性管理器
遊戲中角色擁有的屬性值很多,運營多年的遊戲,往往會有很多個成長線,每個屬性都有可能被n個成長線模組增減數值。舉例當角色戴上 時候hp 100點,卸下 時hp 100點,這樣加減邏輯只有一處還比較好控制,如果某天有個特殊功能當被某技能攻擊時,角色 會被擊落,這樣就會出現減數值的操作不止一處。如果邏輯處...