TCP是一個流式的協(xié)議,客戶端向服務(wù)器發(fā)送一段數(shù)據(jù)后,可能并不會被服務(wù)器一次就完整的接收到??蛻舳讼蚍?wù)器發(fā)送多段數(shù)據(jù),可能服務(wù)器一次就接收到了全部。 (推薦學(xué)習(xí): swoole視頻教程)
在實際應(yīng)用中,希望在服務(wù)器上能夠一次接收一段完整的數(shù)據(jù),不多也不少。
傳統(tǒng)的TCP服務(wù)器中,往往需要由程序員維護(hù)一個緩存區(qū),先將讀取到數(shù)據(jù)寫入緩存區(qū),然后再通過預(yù)先設(shè)定好的協(xié)議內(nèi)容來區(qū)分一段完整數(shù)據(jù)的開頭、結(jié)尾和長度,并將一段完整的數(shù)據(jù)交給邏輯部分處理,這就是自定義協(xié)議的功能。
在Swoole中已經(jīng)在底層實現(xiàn)了一個數(shù)據(jù)緩存區(qū),并內(nèi)置了幾種常用的協(xié)議類型,并直接在底層做好了數(shù)據(jù)的拆分,以保證在onReceive回調(diào)函數(shù)中,一定能夠收到一個或數(shù)個完整的數(shù)據(jù)段。
數(shù)據(jù)緩存區(qū)的大小可以通過配置pakcage_max_length來控制。
$configs = []; $configs["package_max_length"] = 8192; $server->set($configs);
swoole目前支持兩種通訊協(xié)議:EOF結(jié)束符協(xié)議、固定包頭加包體協(xié)議
package_max_length
package_max_length用于設(shè)置最大數(shù)據(jù)包尺寸,當(dāng)開啟open_length_check或open_eof_check或open_http_protocol等協(xié)議解析后,Swoole底層會進(jìn)程數(shù)據(jù)包拼接,此時在數(shù)據(jù)包未收取完整時,所有數(shù)據(jù)都將保存在內(nèi)存中。
所以需要設(shè)置package_max_length一個數(shù)據(jù)包最大允許占用的內(nèi)存尺寸。
如果同時有1萬個TCP連接在發(fā)送數(shù)據(jù),每個數(shù)據(jù)包2MB,在最極端的情況下會占用20GB的內(nèi)存空間。所以此參數(shù)不宜設(shè)置過大,否則會占用很大的內(nèi)存。
相關(guān)配置選項
open_length_check
當(dāng)發(fā)現(xiàn)數(shù)據(jù)包長度超過package_max_length時會直接丟棄此數(shù)據(jù)并關(guān)閉連接,因此不會占用任何內(nèi)存,適用于websocket、mqtt、http2協(xié)議。
open_eof_check
由于無法事先得知數(shù)據(jù)包的長度,所以接收到的數(shù)據(jù)還是會保存在內(nèi)存中持續(xù)增長。當(dāng)發(fā)現(xiàn)內(nèi)存占用已經(jīng)超過package_max_length時,將直接丟地此數(shù)據(jù)包并關(guān)閉連接。
open_http_protocol
HTTP的GET請求最大允許8KB數(shù)據(jù)且無法修改此配置,POST請求會檢測Content-Type,如果發(fā)現(xiàn)超過package_max_length則直接丟地此數(shù)據(jù),并發(fā)送HTTP 400錯誤并關(guān)閉連接。
EOF協(xié)議
使用一組固定的、不會在正常數(shù)據(jù)內(nèi)出現(xiàn)的字符串/r/n作為分割協(xié)議的標(biāo)記,稱之為EOF協(xié)議。
什么是EOF協(xié)議呢?
EOF全稱 End of File,使用rn作為結(jié)束標(biāo)記。
在逐個讀取數(shù)據(jù)流中的數(shù)據(jù)時,如果發(fā)現(xiàn)讀到EOF標(biāo)記,就表示已經(jīng)讀到數(shù)據(jù)末尾。
在TCP的數(shù)據(jù)流中,使用EOF協(xié)議的數(shù)據(jù)流的特征是|數(shù)據(jù)|EOF|數(shù)據(jù)|EOF|。
EOF協(xié)議處理的原理是在每串正常數(shù)據(jù)的末尾會添加一個預(yù)先規(guī)定的且絕對不會再數(shù)據(jù)中出現(xiàn)的字符串作為結(jié)束標(biāo)記,這樣接收到的數(shù)據(jù)就可以根據(jù)EOF標(biāo)記來切分?jǐn)?shù)據(jù)。
典型的memcached、ftp、stmp都是使用/r/n作為結(jié)束符。當(dāng)發(fā)送數(shù)據(jù)時只要在數(shù)據(jù)包的末尾添加/r/n即可。
使用EOF協(xié)議處理一定要確保數(shù)據(jù)包中間不會出現(xiàn)EOF,否則將會造成分包錯誤。