問題 看下面一段代碼 $word = 'HELLO'; $conf = array( array('ip'=>'10.1.146.133', 'port'=>2001), array('ip'=>'10.1.146.133', 'port'=>2002) ); function udpGet($word, $ip, $port) { $sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP); socket_set_option($sock, SOL_SOCKET, SO_SNDTIMEO, array('sec'=>2, 'usec'=>0)); socket_set_option($sock, SOL_SOCKET, SO_RCVTIMEO, array('sec'=>2, 'usec'=>0)); socket_sendto($sock, $word, strlen($word), 0x100, $ip, $port); socket_recvfrom($sock, $result, 8192, 0, $host, $port); socket_close($sock); return $result; } for ($i=0; $i<2; $i++) { $res = udpGet($word, $conf[$i]['ip'], $conf[$i]['port']); var_dump($res); } 就是連續用UPD向兩個server收發數據(為說明問題,這裡的server使用了最簡單的回射邏輯), 如果一切流程正常,客戶端會收到兩次‘HELLO’。但是,如果服務端出了問題呢? 目前,客戶端的超時時間是2秒,假設2001端口過了3秒發數據,而2002端口無法服務, 猜下結果會是什麼呢?“兩個NULL!”,直覺上應該是這個答案。如果你也這麼認為, 那麼恭喜你,答錯了。 實際的答案是: NULL string(5) "HELLO" 分析 使用tcpdump抓包,得到如下結果 (133為服務端,163為客戶端,客戶端php版本5.3.1,Linux內核2.6.16) 12:01:39.014658 IP 10.1.146.163.40678 > 10.1.146.133.2001: UDP, length 5 12:01:41.015121 IP 10.1.146.163.40678 > 10.1.146.133.2002: UDP, length 5 12:01:42.016103 IP 10.1.146.133.2001 > 10.1.146.163.40678: UDP, length 5 按我的想法,兩個請求應該使用不同的臨時端口收發,但從抓包結果看, 客戶端雖然進行了兩次socket_create,但實際中卻使用了同一臨時端口(40678) 收發數據!這就使得第二個請求收到了第一個請求的回包。 感覺上這應該算是個系統的BUG,從實驗中發現,此問題只在部分系統中存在, 比如Linux內核2.6.32+php5.2.3就沒有此問題。 解決 每次指定socket端口,進行收發。如下面的紅色代碼所示。 $sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP); $sendPort = rand(10240, 60000); socket_bind($sock, ’10.1.146.163′, $sendPort); socket_set_option($sock, SOL_SOCKET, SO_SNDTIMEO, array(‘sec’=>2, ‘usec’=>0)); socket_set_option($sock, SOL_SOCKET, SO_RCVTIMEO, array(‘sec’=>2, ‘usec’=>0)); 當然,rand的端口也有可能出現對撞,但畢竟這種機率不大,可以從很大程度上解決問題。 如果有更好的解決辦法,歡迎大家交流~ �0�2 �0�2