socket多使用者串連求助

來源:互聯網
上載者:User
小弟正在做一個flash為用戶端,php為服務端的socket小程式

現在出現一個問題flash成功串連服務端後!一切都能正常通訊

但是不能多使用者串連服務端(比如我開啟兩個flash,第二就會串連不上)

求助php socket是否能解決多使用者串連的問題


望高手相助


回複討論(解決方案)

沒有人開發過類似的東西嗎?

你這個 PHP 程式,是普通的處理響應 HTTP 要求嗎?還是真的是個 socket server 呢?

如果是後者的話,你的 PHP 程式是如何被啟動的呢?

你要先把這些說清楚,下面才好討論。

是要真的socket響應
flash一直跟php保持著串連狀態

php
可以用
set_time_limit(0);
ignore_user_abort();
進行永遠執行

但是還是用java寫的好
可我不會java

你只回答了我兩個問題中的第一個。第二個問題,你的 PHP 是如何啟動的呢?

如果是手工預先發一個 HTTP 要求來啟動的,那麼就需要在 PHP 裡用一個固定連接埠接收並處理多個 socket 串連請求,這個需要一定的技巧。

如果是由 FLASH 發送這個 HTTP 要求來啟動的,那就可以在 PHP 裡使用不同的連接埠來接收 socket 串連。

無論如何,這種方式啟動並執行 PHP 程式,不適合做 socket server。

好建議

用flash來啟動socket服務端

但是我不好掌握啟動後已耗用時間(如果永久運行,伺服器的開銷會很大,直至最後down掉)
還有當有新使用者進入的時候,我需要主動發送資訊給其他之前登入的使用者,卻不太好實現了

回答你之前的問題
php啟動是在進入遊戲之前先用php串連一下服務端如果不能串連就請求一下(我之前是這樣想的)

的確,每個 FLASH 自己請求啟動一個 PHP 來做 socket server,就只能為這個 FLASH 單獨服務,難以實現多個會話的互連。

不瞭解你的實際應用背景,所以不清楚你為什麼一定要用 socket,如果你的通訊方式能迴歸到普通的 HTTP 的話,應該是最簡單的。

一定要用 socket 的話,建議你還是考慮換一種方式吧。不是說用 java 就好,即使用 java,如果是 web 方式部署的話,問題是一樣的。PHP 也可以用 CLI 的方式來做。

通過HTTP串連, 就是通過IIS,APACHE, 他門有很好的處理多使用者的本事,

所以在可能的情況下都是用WEBSERVICE,也即是用用IIS,APACHE,

所以網上的教材說到FLASH都是說用WEBSERVICE,

否則自己搞個SERVER處理得不好就會有倒塞,

主要就是做一個flash遊戲,基於web,能夠幾個人在一個房間內玩

要能夠即時通知各個使用者其他使用者的情況,所以必須用socket才能達到即時通訊的目的

CLI還沒有用過,能給我一個例子嗎?
我想知道他是如何工作的,能幹什麼

你這個應用情境,關鍵就是要“即時通知”,是吧?

用 socket 當然能達成目的,但要實現起來並不容易,至少在 web 容器裡會比較費勁。

其實用 HTTP 長串連也能達到同樣的效果,比如你訪問開心網,總會有一個未完成的 HTTP 要求,最多 30 秒它就會結束,但馬上會再發起一個請求,這就可以實現“即時通知”。相關資料可以搜尋“comet 長串連”。

(請注意,上面說的 30 秒,不是說每個通知要等 30 秒。如果沒有任何通知,則超過 30 秒會改用一個新的串連;但如果有通知的話,會立刻返回)

CLI 只是 PHP 程式的另外一種啟動運行方式,相當於在伺服器上直接運行一個 exe,只不過這個 exe 是用 PHP 編寫的。因為沒有 web 容器的限制,對於多線程、網路通訊等功能使用起來比較自由。

php
可以用
set_time_limit(0);
ignore_user_abort();
進行永遠執行

但是還是用java寫的好
可我不會java
怎麼還有ignore_user_abort(),php socket server要在命令列下執行,也就是上面說的cli,而不是設為為一個http響應端.
http://devzone.zend.com/article/1086

謝謝maquan

php編寫.exe尾碼的可執行檔嗎?

還是說檔案也是.php只是啟動髮式變了而已?

http://devzone.zend.com/article/1086上面的寫法和手冊的socket_select差不多

我再嘗試下

謝謝大家的協助!

小弟學到了很多以前不懂的知識和一些解決方案!

再次感謝

CLI 只是 PHP 程式的另外一種啟動運行方式,相當於在伺服器上直接運行一個 exe,只不過這個 exe 是用 PHP 編寫的。因為沒有 web 容器的限制,對於多線程、網路通訊等功能使用起來比較自由。


我用cli方式運行就不會有線程限制了?
是不是等於我以前寫的代碼直接運行就可以了?

非常感謝大家,已經可以達到多人線上了

感覺即時性也非常的好

謝謝maquan和BooJS

結貼後還能繼續聊天嗎?

還想跟你們聊聊呢?

結貼不著急,只要是跟這個話題有關的,儘管繼續聊,hehe

不過如果是其它問題,最好另外開貼,這樣也方便大家閱讀。CSDN 是公用場所嘛 :)

昨天上傳到空間發現用cli運行有限制30秒便逾時了!

那麼我們就必須要寫一個守護他的程式當發現逾時的時候重新用cli方式運行一次

那麼用try catch是否能監控到逾時的時候運行catch裡面的東西呢?
try{
我的socket代碼
}
catch(){
再次重新請求
}

catch括弧裡面我該如何寫呢?

對空間不瞭解,不知道他們有什麼管理限制。PHP 自己的已耗用時間限制可以通過 set_time_limit() 來設定,預設就是 30s。

用精靈重新載入的方式,未必能很好地解決你的問題,它會讓你的服務端表現很不穩定,用戶端開發難度加大。

如果你是租用空間的話,超常規的程式一定會有諸多限制,強烈建議你仔細瞭解一下長串連。

好的謝謝

空間確實是設定了30秒逾時
我先瞭解一下長串連!

這個對環境的要求會小很多

通過一天的瞭解大概瞭解了什麼是comet長串連了!

主要就在php端寫上
flush();
重新整理緩衝區,讓前台的ajax接收到資訊

同時php端並沒有停止工作

當30秒逾時的時候,用戶端再重新串連一次

不知道我的理解是否正確?

你理解得沒錯,大體上就是這個意思。

不過你可能太關注實現的細節了。概念上講,所謂長串連,就是伺服器端收到 HTTP 要求後,並不立刻完成它,而是保持一段時間(比如 30 秒),這期間如果有需要即時送達的通知,則立刻輸出響應內容並關閉串連。如果平安無事,到時間也要返回(最好不要等到被容器殺掉),這樣可以給用戶端一個完整的響應,便於用戶端繼續下面的工作。

實現長串連的一個痛點在於等待過程中如何獲知“需要即時送達的通知”,尤其是在租用空間的 web 容器中,實現起來會很受限制,簡單的輪詢和延時肯定效率非常差,最好是某種阻塞方式的 IO。


????????????????????????????????
基於CSDN論壇提供的外掛程式擴充功能,自己做了個簽名檔工具, 分享給大家,歡迎 技術交流 :)

確實在等待過程中,如何知道有新內容需要返回是一個比較麻煩的事情
做的不好會影響即時性

我是這樣想的,如果有內容需要返回的時候是讀取由另一個頁面建立的xml或者php數組之類的(即讀取寫檔案),當串連發起時,記錄一下檔案當前的修改時間,然後當修改時間發生變化的時候就返迴文件裡的內容。
不知道這樣會不會出現一些問題,比如修改和串連同時進行(我串連的前一秒,檔案被修改了,這樣就只有等下次檔案修改的時候才會返回資訊,出現這樣情況的幾率不知道有多大)

你指的IO操作我不太明白



我寫過一個socket的小例子。在我的部落格裡面,你可以去看看。
http://blog.csdn.net/a82168506/article/details/6307461

謝謝樓上這位兄弟,socket的我也寫了代碼和你的差不多

問題就是空間強制設定了30秒逾時(set_time_limit(0)沒有用),所以沒有辦法才想看看長串連能不能解決!

學習了一下IO
發現我的這種長串連貌似是阻塞IO吧!

如果想做到是非阻塞IO,技巧在哪呢?

經過測試發現ajax發起的長串連是非阻塞IO

只是瀏覽器對長串連有限制,當相同瀏覽起發送兩個長串連的時候,有一個是無法串連的!

如果實現有新訊息立即通知客戶

我想了如下方法
還是用讀檔案的方法
首先頁面開啟的時候,由js發起串連同時傳送所要讀的檔案當前的修改時間

到達php頁面後當發現修改時間和傳送過來的時間不一致的時候通知用戶端有新訊息
php頁面運行到25秒的時候直接返回一個檔案的最後修改時間給js,此次長串連結束

js接收到php傳回的檔案修改時間後重新發起串連並傳送php傳回的檔案修改時間

我相信這樣就不會漏掉需要通知用戶端的資訊了吧

以上是我個人的一個想法

請maquan和大家再幫幫我,分享一下你們的處理方法

謝謝

……
到達php頁面後當發現修改時間和傳送過來的時間不一致的時候通知用戶端有新訊息
php頁面運行到25秒的時候直接返回一個檔案的最後修改時間給js,此次長串連結束
……
在紅、藍兩個動作之間的時間裡,程式在幹什麼呢?如果在第 10 秒的時候“事件發生了”,能及時通知嗎?

兩個動作之間php頁面sleep(1)(返回新訊息後串連並沒有結束),然後接著檢查檔案修改時間,直至已耗用時間大於25秒才結束串連,再由js發起新串連

這樣應該沒有問題吧

如果是你,你怎麼做呢?如何判斷有新的訊息需要返回?

等偶有時間了,也來弄下flash

首先,“返回新訊息後串連並沒有結束”這個恐怕是有問題的,只有結束掉串連,用戶端才會認為“已經完整接受到了一個 HTTP Response”,才會進一步處理。如果不結束串連的話,即使你做了 flush 等等,最多也只是把資料流傳到用戶端,但用戶端並沒有完成接收、開始處理,也就是說你這個通知還是沒有“即時”。

至於“檢查是否有通知”的方法,你所用的應該屬於“輪詢+延遲”的方法,一般來說可以用,但“即時性”和“運行效率”稍差一點點。我總覺得“阻塞 IO”的方式也許會好一點,你可以參考一下 socket_recvfrom(),缺點是資源佔用多了一點。

租用的空間嘛,沒辦法,受制於 web 容器。如果是自己的獨立伺服器,搞個 deamon,用多路非同步 IO,想咋玩就咋玩,hehe


????????????????????????????????
基於CSDN論壇提供的外掛程式擴充功能,自己做了個簽名檔工具, 分享給大家,歡迎 技術交流 :)

http://www.zeitoun.net/articles/comet_and_php/start
我尋找到這個例子

他使用了一個js庫就可以實現了長串連且內容能正常返回,串連不終止!

大家看看

這個庫我不太熟悉,有熟悉的朋友嗎?

這就是真正意義上的comet嗎?

多方尋找資料
發現能參考的很少

我現有的水平可能只能這樣寫了

js發起長串連

php頁面如果有新資訊立即返回給js並且結束該串連,JS收到新資訊後(進行處理),馬上發起新的串連(資料處理之前)

如果超過25秒都沒有新資訊,php結束該串連,js同樣再次發起新串連!

用socket_recvfrom()我又必須用cli去運行一個服務端了吧,這樣的就是阻塞IO了?
貌似JS也是可以當作socket用戶端串連服務端的,如果多個用戶端同時去串連,出現了阻塞,不知道用戶端的發起的串連會逾時嗎?會不會在socket服務端斷開前一串連時自動連接上呢?

理論上講如果用戶端多的情況下,即時性恐怕也無法保證了

上面我好像理解錯誤了,找到了一個最通俗的解釋

比如進程A要從網路收一個包,如果沒有包就等待,這就是阻塞。
不等待,去做別的事情,等包來了再處理就是不阻塞。

要是按這樣來看

js發起長串連
php頁面如果有新資訊立即返回給js並且結束該串連,JS收到新資訊後(進行處理),馬上發起新的串連(資料處理之前)
如果超過25秒都沒有新資訊,php結束該串連,js同樣再次發起新串連!
其實是一個阻塞IO,當沒有新資訊的時候js是一直處理等待狀態,直到有新的資訊為止

在一個通訊的兩段,讀操作和寫操作,它們分別使用的是“阻塞”還是“非阻塞”,是各說各的,互不影響。

比如你的服務端代碼:等待通知如果採用 socket_recvfrom(),那就是“阻塞”的,而輸出 HTTP Response 一般來說都是“阻塞”的。而在 js 這邊接收 HTTP Response,既然你用了 AJAX,那就一定是“非阻塞”的,這正是 AJAX 的價值所在,否則頁面就卡死變白屏了。
用socket_recvfrom()我又必須用cli去運行一個服務端了吧,這樣的就是阻塞IO了?
不是這樣的。負責長串連的那個 PHP 用 socket_recvfrom() 等待通知數據,處於“阻塞狀態”;而產生通知數據的一定是你另外一個常規的 PHP 程式,比如“某使用者登入”,由它負責 socket_sendto()。

理論上講如果用戶端多的情況下,即時性恐怕也無法保證了
需要擔心的不是“即時性”,前面描述的通訊模型如果能成功的話,即時性總是能保證的(當然還取決於網路速度)。問題在於當用戶端數量多的情況下,資源佔用會比較大,包括:

·每個用戶端會持續佔用一個 PHP 長串連進程資源;
·每個長串連進程會佔用一個 socket 資源;

總的說來,局限在 web 容器裡,這種模式不適合大規模應用。比如空間服務商限定了你網站的 HTTP 並發串連數,那長串連有可能將你的整個網站拖垮。那還不如迴歸到“用戶端周期性輪詢”的模式呢,損失一點“即時性”,換來的是“線上使用者數”。

我運用了你的方法嘗試,發現一個問題

每個連接埠只能建立一個通訊端
如果我使用隨機連接埠的話socket_sendto就不會知道目前有哪些連接埠需要發送了

我是否需要維護一個當前線上的連接埠表?是儲存到資料庫比較好,還是存為檔案模式呢?

用 UDP 廣播,具體用法你查一下資料吧。

好的太感謝你的耐心解答了
我先查查資料

maquan
廣播我已經測試成功

但是廣播還是需要設定連接埠

我貼上My Code

js請求的php

header("Cache-Control: no-cache, must-revalidate");header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");include('config.php');//這裡是$add(IP)和$port$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);socket_bind($socket, $add, $port);//我嘗試將$port寫為0但是如果這樣建立會失敗$port = 0;socket_recvfrom($socket, $buf, 10240, 0, $from, $port);echo "Received $buf from remote address $from and remote port $port" . PHP_EOL;socket_close($socket);


廣播的php
include('config.php');$sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);socket_set_option($sock, SOL_SOCKET, SO_BROADCAST, 1); $msg = rand(1000,9999);$len = strlen($msg);socket_sendto($sock, $msg, $len, 0,'255.255.255.255', $port);//這裡的$port要和上面那檔案的$port相同上面的檔案才能收到廣播,但是不同客戶串連後連接埠必須不一樣才能成功建立socket串連,所以要怎麼寫才能通知該IP下所有連接埠的使用者呢?這種是通知了連接埠為$port的所有使用者吧socket_close($sock);


我該如何寫才能是通知所有連接埠呢?

把socket.accept寫在while(1)中就行

accept是監聽有沒有使用者串連吧

我這個是不需要使用者串連用sendto根據ip和連接埠發送給線上的使用者

再試試 socket_set_option($sock, SOL_SOCKET, SO_REUSEADDR, 1);

還是不行
socket_sendto函數的連接埠參數不可為空的話接收端就收不到資訊,也就是說不能一次性send資訊給所有線上的連接埠

socket_set_option($sock, SOL_SOCKET, SO_REUSEADDR, 1); 應該是寫在接收端吧,但是寫了也不行

你能寫個例子給我嗎?

昨天晚上心血來潮,寫了一段 UDP 廣播通訊的程式,正好可以供你參考 :)

recvfrom.php

  

sendto.php
  

以上兩個 PHP 程式,recvfrom.php 可以用多個瀏覽器視窗分別發送請求,都會處在等待狀態,然後再用一個瀏覽器視窗發送 sendto.php 的請求,前面的幾個瀏覽器視窗就同時得到結果了。


????????????????????????????????
基於CSDN論壇提供的外掛程式擴充功能,自己做了個簽名檔工具, 分享給大家,歡迎 技術交流 :)

非常感謝
已經測試成功
現在返回去看SO_REUSEADDR的解釋Reports whether local addresses can be reused.
才恍然大悟,“是否可重複使用本地地址”
設定了這個option過後就不會再出現bind時提醒一個IP一個連接埠只能被建立一個串連了

剛測試發現
recvfrom.phpl裡面不用加這句也成功
socket_set_option( $socket, SOL_SOCKET, SO_BROADCAST, 1 );

我認為可以刪除,因為廣播的頁面是sendto.php,recvfrom.php只需要可重複使用本地地址和連接埠就行了

是這樣嗎?

這個我也不是很清楚。

UDP 廣播包跟普通的 UDP 包一個很明顯的區別就是“目標 IP 位址不同”。作為“監聽 UDP 包”的 socket,從物理上講,它肯定能“感知”到廣播和非廣播的 UDP 包,但它會不會“接收”廣播包呢?如果你已經把這個 socket 設成“廣播”型了,那肯定就沒問題了,但如果不設定呢?它會不會拒收呢?

我覺得這個不好說,可能跟具體的系統實現有關吧。雖然實際使用時不寫也可以,但我總覺得寫上更安全。

有道理
計算一下外網的廣播位址測試一下

廣播位址要有子網路遮罩才能算啊

還得問問服務商,只有IP貌似算不了

不知道我理解得對不對,廣播(broadcast)僅限於區域網路內,即一個網段。要出外網的話,就是多播(multicast)了,需要路由器配合。

我做的這個其實就是對一個ip的不同連接埠進行廣播呀

應該是可以辦到的吧

如果無法辦到就真的只能維護一個使用者使用的連接埠表了

嘗試了一下不行
幫我看看廣播位址是不是算錯了


IP 位址: 123.183.215.45
子網路遮罩: 255.255.255.128
預設閘道: 123.183.215.1

我算出來是
123.183.215.127

  • 聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.