• 沒有找到結果。

Single-Process ( Event-Based ),Concurrent Servers…. 80

在文檔中 串流伺服器特性剖析 (頁 93-98)

四、 串流伺服器實作細節

4.1 串流伺服器的多工

4.1.2 Single-Process ( Event-Based ),Concurrent Servers…. 80

如下圖 4.2 所示,Single-Process 的意思是主行程(master process)需同時擔任接待以 及服務的工作,因為我們只有一個行程(Single process)。Live 的 RTSPServer 便是屬於此

種串流多工架構,這種串流伺服器會由這個單一的行程來控制多個 sockets 的 I/O。如下 圖 4.2 所示,listening socket 接收來自 client 端的連線並以 accept()建立了 connected socket 後,負責連線的後續工作。

通常這種 Non-blocking socket 會搭配 select()函數來使用,select()目的是事先詢問各 個 socket 的狀態,判斷目前 socket 是否已經可以讀取,確定有之後我們再去讀取,避免 類似 polling I/O 浪費 CPU 資源。

圖 4.2(a) Single-Process ( Event-Triggered ),Concurrent Servers

(註)欲讓原本預設為 blocking 的 socket 變成 Non-blocking socket 的設定如下所示:

4.1.2.1 Select()說明

select 這個 system call 可以告知 kernel 它想監控的 socketfd,當 socketfd 所 index 的 socket 有事件發生時,kernel 會叫醒該程式來處理之,可以監控的事件有三種,分別是

『讀取』、『寫入』以及『例外』,select 的敘述以及 Single-Process ( Event-Triggered )流程 圖如下所示:

圖 4.2(b) Single-Process with select flowchart

(使用 select 來達成單一行程的多工步驟如下)

1.針對欲監控的 fd_set 宣告結構變數,三種情形:讀、寫、例外的集合視需要宣告之。

2. 宣告 readSet 以及 freadSet,並使用 FZ_ ZERO 把結構的 bit array 內容清為 0。

3.使用 FD_SET 把我們的 Listen socketfd 加入 fReadSet 的集合中,當進入迴圈之後,把 fReadSet 的值設定給 readSet。

4.呼叫 select(),使行程 block 住直到所監控的 socketfd 有動靜(由 kernel 主動喚醒程式),

程式碼如下所示。

Live RTSPServer 只把先前宣告好的 readSet 放到 select 的第二個參數中,表示我們 只等待『讀取』的事件,因此我們把參數三及參數四 *writeSet、*exceptSet 都設為 NULL;

第五個參數在 Live RTSPServer 的程式中是設為一百萬秒(11.5 天),原因是 select 所 block 的時間如果超過一百萬秒(此段時間沒有用戶連線),則 select 函數會失效。

故最佳的設定便是設成一百萬秒,如果這段時間都沒有用戶連線,Select()便會回傳 一次結果,如此 Timer 便可以重新計數而不會導致 Select()失效;同時 Live555 也替每一 個 connected socket 設定了一個『通關』的函數 timeToNextAlarm(),目的是使伺服器可 以同時接收新的用戶連線並且對舊有的用戶連線進行封包的傳送,此地方的設計需要特 別注意。

5.直到所監控的 socketfd 有動靜,行程就會從 select loop 中跳出,因 select 回傳的是符合 監控狀況的 socketfd 數目,因此尚須由 FZ_ISSET 巨集來判斷是哪個 socketfd 有動靜。

6. 使用迴圈並配合 FZ_ISSET 來檢查是 readSet 中的那個 connected socketfd 有反應,這

表示 kernel 所監控的 readSet 有讀取事件發生,也就是有封包抵達 NIC(Network InterfaceCard,網路介面卡)。

7.當完成 TCP 3WHS 建立完 RTSP 的 connected socket 後,我們便可以使用 BSD socket API 的 recv()來接收送到 Server socket 上的 TCP/RTSP 封包;當 RTSP connected socket 收到 RTSP PLAY 信令後,Server 便會建立 UDP socket 以作為 RTP 封包傳送之用。

4.1.2.2 Select()虛擬碼範例 int socketfd = socket(…);

bind(socketfd, …);

int curFlags = fcntl(listen_fd, F_GETFL, 0); //(make Socket Non-Blocking ) fcntl(newSocket, F_SETFL, curFlags|O_NONBLOCK);

listen(socketfd, …);

fd_set fReadSet;

FD_ZERO(&fReadSet);

FD_SET(socketfd, & fReadSet);

int max_fd =socketfd;

while(1) {

fd_set readSet=fReadSet;

/*只等待 read 的事件,故*writefds,*exceptfds 都設為 NULL*/

int selectResult = select(max_fd + 1, &readfds, NULL, NULL, &tv_timeToDelay);

/*Do loop on all fds to look for RTSP connected socket or accept a new connection request.*/

for (int i = 0; i <= max_fd; i++) { if (FD_ISSET(i, &readSet)= =1) {

/*ex: recvfrom(connfd, buf, sizeof(buf)); */

<TODO:RTSP negotiation and creates UDP socket for RTP > } }//end if FD_ISSET

}//end loop on all fds //For each UDP socket

<TODO:send RTP packet by calling BSD socket API sendto() >

……….………

在文檔中 串流伺服器特性剖析 (頁 93-98)

相關文件