由於工作需要,最近開發了這麼樣的一個伺服器端程式。嚴格意義上面來講,這個程式並不是平常聊天的伺服器程式,它是一個遊戲系統中的廣播伺服器處理常式。不過只需要稍微做一些修改的話,他就可以成為一個聊天的伺服器。
首先簡單地介紹一下功能(希望我們的程式員不要覺得麻煩),遊戲已經上線了,根據設想,我們還想做一個廣播系統,即遊戲裡面的使用者發生了一些事件,我們在php的代碼中,將這些事件經過後端的伺服器發到前端的flash頁面上,讓所有的使用者都知道這樣一件事情。
這個就是簡單的程式運行,linux伺服器主要處理的就是從php程式中接受資料,讓後將這些資料推送到前台的頁面上去。
這樣的一個系統,我採用的是socket來實現它,今天我們不介紹php是如何處理socket的,前面已經有相關的檔案介紹,另外,對於資料轉送使用的協議,這裡也不做介紹,這個東西只要我們對伺服器和php程式做統一的規定就好了。我們的重點是總結一下這個廣播背景實現過程:
我們簡單地看看使用socket伺服器需要做那些處理:
1。載入socket 協議,
2。建立一個socket對象,
3。將該socket bind到一個連接埠,
4。監聽該連接埠的相應(使用listener),
5。接受用戶端的socket,
6。建立資料庫傳輸。
大部分socket編程都會使用上面的結構,我們這裡也是,使用的tcp協議。對於我們的程式,這裡有個特殊的要求,我們不是面對一對一的資料轉送,我們只是從一個地方接受資料,但是我們要將這個資料推送到前面的很多用戶端上面去,所以我們的用戶端串連講是一個數組。在我的程式裡面我使用這樣的結構
typedef struct CLIENT {
int fd;
struct sockaddr_in addr;
}CLIENT;
CLIENT client[FD_SETSIZE]; //後面的FD_SETSIZE設定為1024
while (1)
{
confd =accept(slisten, (struct sockaddr*)&addr,&len)) == -1;
if(confd)
{
for (i=0; i<FD_SETSIZE; i++)
{
if(client[i].fd<0)
{
client[i].fd = confd;
client[i].addr = addr;
}
}
}
}
上面的代碼就是將這樣的一個client填滿,如果有些用戶端斷掉了,我們再使用新來的串連將其補上。
上面說了,這個程式還要接受php程式的資料,那麼就必須使用到recv 和send函數,但是我們知道,這些函數是阻塞I/O,如果沒有資料進來的話,程式就會停留在這個地方,這樣需要推送的資料自然發送不了(大家可以自己動手實現一下,)。要解決這樣一個問題,那就必須使用非阻塞式編程啦。
在這裡我使用了select 函數,具體的使用詳細地介紹,大家可以上網去查查:
fd_set 是select 的一個基本結構,這裡我們定義
fd_set allset, rset;
FD_ZERO(&allset); //初始化
FD_SET(slisten, &allset); //將伺服器的socket放到select中
maxfd = slisten;
nready = select(maxfd + 1, &rset, NULL, NULL, &tv); //注意第一個參數一定要加一, 這個函數很重要,在使用的使用查清楚
我們會根據這個nready的值來做不同的處理:
if(nready==0) //逾時
else if(nready < 0) //出錯
else
{
//說明有資料轉送
//使用FD_ISSET來判斷是哪一個有反映,例如有新的介面,接受資料等等
}
這裡再提一個問題,剛才講了一個程式只能處理1024個的串連,那如果使用者的數目大於1024, 需要怎麼處理了?