A. 如何偵伺服器的tcp返回數據
在平時的開發中,經常會碰到一些需要檢測tcp連接是否正常的場景。比如一個分布式的應用,一個調度任務的節點管理一堆用來跑業務的節點。當調度節點進行調度的時候,需要把任務分發給它認為正常的業務節點去執行。業務節點是否正常,一個重要的參考依據就是調度節點和業務節點之間的tcp連接是否正常。這時候就需要調度節點主動地去檢測tcp連接。常見的檢測方法有以下幾種
方案一、通過TCP協議的返回值進行判斷
<1> 利用select,把socket設置為非阻塞。然後使用select等待該socket的可讀事件。如果socket可讀,但是recv的返回值是0,則說明socket已經被對端斷開,這時候就可以調用close關閉socket。這里還要注意一點,recv還可能返回負數,這個代表socket操作出錯。但是仍然應該判斷一下errno是否為EINTR。如果errno是EINTR,則說明recv函數是被信號中斷返回的,這時候不能判斷socket的連接是否正常,也不應該調用close關閉socket。
<2> 利用poll的事件。poll本身提供了POLLHUP,POLLERR, POLLNVAL三個事件。如果文件描述符是socket,則POLLHUP代表socket已經斷開了連接,在TCP底層就是已經收到了FIN報文。POLLERR表示socket出現了錯誤,一般情況下是收到了rst報文,或者已經發送了rst報文。這兩種情況都應該調用close關閉socket。POLLNVAL代表socket沒有打開,這時不能使用close關閉它,而應該根據自己的業務做一些其他的操作。因為關閉一個未打開的socket會出錯。
這兩種方法都可以很精確地判斷tcp連接是否正常,但是仍然有很明顯的缺陷。就是它只可以根據TCP操作的返回值來進行判斷。如果TCP四次握手沒有正常被執行呢?比如連接對端機器直接掛了,那麼就不會發送FIN報文給這一端,select不會返回socket可讀,poll不會返回socket異常。那麼這個死鏈接將會永遠檢測不到。直到寫這個socket的時候,對端直接返回一個ret報文,這時才知道這個連接已經斷掉了。這就意味著tcp連接異常可能永遠檢測不到,或者檢測到的延遲非常大。這對於一些資源寶貴而且要求高性能的伺服器是不能接受的,比如游戲伺服器,比如搜索伺服器。方案二、在第一種方案的基礎上設置socket的 keep alive 機制
方案一的主要缺陷在於檢測不及時,或者根本檢測不到。TCP協議提供了keep alive機制。如果開啟了這個特性(暫時稱開啟了keep alive的一端為開啟端),在默認情況下,開啟的著一端的socket相關結構中會維護一個定時器,默認是2小時。如果在2小時內兩端沒有數據往來,那麼開啟端就會給另一端發送一個ack空報文。這時候分幾種情況:
<1> 對端機器可達,而且TCP相關組件運行正常。那麼對端就會給開啟端發送一個ack空報文。這時開啟端就知道對端是正常的,意味著tcp連接也沒有問題。開啟端會重新初始化定時器,等待下一個超時的到來。需要注意的是,如果兩端之間有數據往來,定時器也會被重新初始化為2個小時。
<2> 對端掛了,或者正在重啟,還沒有完全起來。或者對端伺服器不可達。 這種狀態的對端是不會響應這個ack的。開啟端的 keep alive 機制會把這種情況當探測超時來處理,並且重新發送ack到對端。當超時次數超過一定限制,keep alive 就認為這個tcp連接有問題。典型值是每次75秒,超時9次。
<3> 對端掛過,但是已經重啟完成。這時候發送這個ack和寫已經關閉的socket是一種情況,對端會返回一個rst報文,這樣開啟端就知道tcp連接出問題了。
可以看出 keep alive 機制彌補了方案一種不能判斷沒有進行正常四次揮手連接出現問題的缺陷。默認的發送超時和發送間隔都是可以調整的。
tcp_keepalive_time: KeepAlive的空閑時長,默認是2小時
tcp_keepalive_intvl: KeepAlive探測包的發送間隔,默認是75s
tcp_keepalive_probes: 在tcp_keepalive_time之後,沒有接收到對方確認,繼續發送保活探測包次數,默認是9次
這3個參數使用 setsockopt函數都是可以配置的。
方案二看似已經完美了,能夠比較精確而且及時地發現有問題的連接。但是還有2個缺點。第一個是 keep alive 機制看似牛逼,但是很多人不建議使用。因為上面說的3個參數很難根據業務場景給出合適的值,設置不好很容易對tcp連接狀態發生誤判,關閉了一個本來正常的連接。而且沒有一個主動通知應用層的方式。比如socket連接出錯了,TCP協議接到了rst,fin,或者keep alive判斷出socket有問題了,但是並不會主動去通知應用層,必須我們自己 recv socket或者等待錯誤事件才能得到這個錯誤。第二個是很多場景下,keep alive 檢測仍然不夠及時,比如對端掛了,最長需要等待 tcp_keepalive_intvl * tcp_keepalive_probes時間才可以檢測出來,而且這兩個值還不能設置得太小,太小了容易誤判。
方案三、應用層的心跳
這種形式的心跳設計就比較多樣化了,而且靈活,可以很好地適應業務場景。唯一的缺點就是要自己寫代碼。我目前接觸到的就是定期進行RPC調用。看RPC調用是否正常,如果返回錯誤或者拋出異常,就說明連接有問題。
B. Ajax向伺服器發送請求和接收返回的信息
Ajax向伺服器發送請求
Ajax對象創建完成後,下面就要講解一下Ajax如何使用。首先詳細講解一下Ajax向伺服器發送請求所需的兩個方法,具體如下:
(1)open()方法
open()方法用於創建一個新的HTTP請求,並指定此請求的類型(如GET、POST等)、URL以及驗證信息,其聲明方式如下所示:
在上述聲明中,method用於指定請求的類型,其值可為POST、GET、PUT及PROPFIND,大小寫不敏感;URL表示請求的地址,可以為絕對地址也可以為相對地址,並且可以傳遞查詢字元串。其餘參數為可選參數,其中,asyncFlagy用於指定請求方式,同步請求為false,默認為非同步請求true;userName用於指定用戶名,password用於指定密碼。
(2)send()方法
send()方法用於發送請求到HTTP伺服器並接收回應。其聲明方式如下所示:
在上述聲明中,content用於指定要發送的數據,其值可為DOM對象的實例、輸入流或字元串,一般與POST請求類型配合使用,需要注意的是,如果請求聲明為同步,該方法將會等待請求完成或者超時才會返回,否則此方法將立即返回。
需要注意的是,在使用GET方式傳遞特殊字元或中文參數時,要使用JavaScript中的encodeURIComponent()函數將其轉換成「%十六進制數」的形式,防止在某些瀏覽器(如IE瀏覽器)中中文亂碼的問題。
Ajax接收伺服器返回的信息
了解Ajax向伺服器發送請求後,下面將對Ajax如何接收伺服器返回的信息(例如,HTML標簽、CSS樣式、字元串、XML、JSON等),進行詳細講解。具體如下:
(1)readyState屬性
readyState屬性用於返回Ajax的當前狀態,狀態值有5種形式,具體如表所示。
(2)onreadystatechange屬性
onreadystatechange事件屬性用於感知readyState屬性狀態的改變。為了大家更好的理解這兩個屬性的使用,下面創建一個伺服器端的文件index.php,用於輸出字元串,然後在瀏覽器端index.html中向伺服器端發送請求,並在控制台輸出狀態值。具體示例如下所示:
創建伺服器端文件:index.php
創建瀏覽器端文件:index.html
在瀏覽器中訪問客戶端文件,按「F12」鍵,切換到控制台,查看輸出結果,具體如下圖所示。
從圖中可以看出,通過onreadystatechange事件屬性可以清晰的感知Ajax狀態的改變,同時使用readyState獲取轉變後的狀態值。例如Ajax從0(未初始化)狀態變成1(初始化)狀態值時,Ajax此時的狀態值為1。
(3)status屬性
status屬性用於返回當前請求的HTTP狀態碼,常見的狀態碼如表所示。
值得一提的是,在感知當前Ajax對象狀態時,為了追求程序的嚴謹性,需要同時判斷當前HTTP狀態status是否等於200(請求成功)。
需要注意的是,Ajax中的statusText屬性,僅當數據發送並接收完畢後,才可以獲取當前請求的響應狀態。
(4)獲取響應信息的相關屬性
當數據接收完畢且請求伺服器的請求成功時,即可以使用Ajax中提供的相關屬性獲取伺服器的響應信息。具體的屬性及相關說明如下表所示。
在上表中,responseText屬性用於返迴文本格式的響應數據;屬性responseBody表示直接從伺服器返回並未經解碼的二進制數據;responseXML屬性用於接收XML數據格式的響應數據。
C. 數據怎麼返回給原頁面(PHP)
你大概要先搞清楚 頁面(前端) 和 PHP(後端) 的關系;
第一個頁面 由 A.php完成, 提交後 輸入的內容 交給B.php 由B.php 生成第二個頁面。
這個時候 A.php已經不起作用了。 A.PHP 提交的內容在伺服器端的 全局變數$_POST['name']裡面(name 就是A.PHP中TEXTAREA 標簽的name 屬性值)。
注意 : 第一個頁面不存在了,要想顯示第一個頁面的內容,就在B.php.中把第一個頁面復制一遍。把重新建立一個<textarea > 標簽 默認值是你要顯示的值,