第三章 伺服器多工型態
3.1 I TERATIVE S ERVER
3.1.2 Single Process Iterative Server
下圖 3.2 為 Single Process Iterative Server 的流程圖。在 Iterative Server 中,
Listening socket 是專門負責監聽 client 的連線要求,而 Connected socket 則是已 經建立好固定的連線,透過 Select 函數與巨集(詳細請參考 3.1.1 章節),使得 Iterative Server 可以不斷地或是定期地去查詢是否有新的連線請求(新的用戶連
線),或是舊用戶的控制信令(RTSP 信令)。
圖 3. 2 Single Process Iterative Server 流程圖
如圖 3.2 所示,Live 的 Iterative Server 原則上是一直在 DoEventLoop 的迴 圈中執行,新用戶連線(new socket)會把建立好的 connected socket 加入 select 所 監控的 set(集合)中;接下來當舊用戶的 connected socket 有 RTSP 的信令傳送時,
kernel 變會喚醒 select,行程便繼續執行下去(old socket)。原則上就分為下列兩種 的情況:
1. 尚未連線的 client 所提出的要求(如上圖 3. 3 new socket)。
2. 已連線的 client 所提出的服務請求(如上圖 3. 4 old socket)。
如果是尚未連線的要求,Single Process Iterative Server 會呼叫 Accept()來接 受連線的要求外,還必須把 connected socket 加入欲監控的 set 中(圖 3.2 的
FD_Set(connfd)),使 connected socket 成為 select 的集合。而 Iterative Server 會 在 old socket 流程中,藉由呼叫 BSD Socket API 的 Read/ReciveFrom、Write/SendTo 函數來進行與 client 間的封包收送的工作。
3.1.2.1 fd_set
在 Select 的引數中我們會看到 Set(fd_set),實際上這個 set 是一個 bit array,
其主要的功能是用來標示那些 sockets 是程式所想要去處理的對象,藉由不同的
set 來區分 event 的種類,並且根據 event 的種類去服務它的要求。從 Select 函數 中的引數我們可以知道,它包含三個 set,用途參考下表 3.3 所示 :
表 3. 3 根據 event 的種類區分 fd_set
3.1.2.2 Select 函數虛擬碼範例
fd_set fReadSet; //宣告 fReadSet 的 data type 為 fd_set int max_fd; //用來記錄所有 sockets 當中,其值最大者 socket_fd = socket(…); //Construct sockets
bind(socket_fd ,…);
listen(socket_fd, …);
FD_ZERO(&fReadSet); //對 fReadSet 執行清除的工作
FD_SET(socket_fd , &fReadSet);//監聽 client 連線請求的 socket 加入 fReadSet 中 /*目前程式中只有 listening state 的 socket,所以 max_fd 的值就是這個 listening socket 的值*/
max_fd = socket_fd;
while(1){
fd_set readset = fReadSet;//就是這個 listening socket 的值。
/*select 函數中所呼叫的是 read 的事件,故其它*writefds, *exceptfds 都為 NULL,而 tv_timeToDelay 預設值為一百萬秒,為 struct *timeval 型態所宣告變 數的最大允許值。
故如果沒有新用戶連線的話,程式便會 block 在此點;倘若必須持續對舊用戶傳 送封包的話,在圖 3.2 的 old socket 中,程式必須設定 tv_timeToDelay 為 0,如 此程式的流程才得以繼續向下執行。
等到不需要對舊用戶傳送封包時,程式便恢復 tv_timeToDelay 為一百萬秒的設 定*/
int selectResult = select(max_fd + 1,&readfds , NULL , NULL ,
&tv_timeToDelay);
/* FD_ISSET 由 fd 0 開始去 scan 一直到 max_fd 為止,目的是先確認 listening socket 是否因為有新用戶連線要建立 connected fd;否則便由之前的 connected fd 逐一檢視,找出欲進行 RTSP 信令或是 RTP 封包傳送的用戶,其流程請參考圖 3. 5*/
//Below pseudo codes are under while-loop for(int i=0;i < max_fd;i ++)
{
if(FD_ISSET(i , &readSet)= = 1) {
//TODO:RTSP negotiation and creates UDP socket for RTP (………. ………. ………. ……….)
}
//TODO :send RTP packet to the exactly fd (………. ………. ………. ………. ……….) }//end if FD_ISSET
} //end loop on all fds
以下為一個 Client 連接後,進入 Select 函數所產生的程序示意圖:
圖 3. 6 Step1 : Select() in Client - Server
圖 3. 7 Step2 :Select() in Client-Server