天天看點

關于Java的TCP程式設計中需要注意的一些坑

TCP連接配接就是傳說中的長連接配接,有所謂的3此握手來保證消息一定可達,在java中,TCP傳輸的方式屬于流資料傳輸,而流資料傳輸的特點就是資料到達的順序是固定的,比如說資料A寫入到TCP連接配接中,資料B接着再寫入到TCP連接配接中,資料C再寫入TCP連接配接,那麼在另一端,如果資料不丢失,那麼A到達後,B到達,C再到達;當B丢失了,那麼就是A到達,B丢失,C這時候也不會到達,因為TCP連接配接是可靠連接配接,一定會確定B到達了之後才會再去處理C資料,是以要麼隻收到A,B和C都丢失(TCP連接配接斷開),要麼A收到後過一段時間重新發送B和C,然後另一端再接收B,C,最後資料接收的順序還是A,B,C,而絕對不可能出現A,C或者B,C的情況; TCP資料傳輸還存在一個粘包問題,之是以會出現資料粘包就是因為TCP資料傳輸是流資料傳輸,假設資料包A是10個位元組,資料包B是20位元組,資料包C是30位元組,那麼發送順序是A,B,C,這時候TCP連接配接的流裡面就有10+20+30=60個位元組,但是有可能由于網絡原因這60個位元組并不會一次性都發出去,有可能先發了15個位元組,再發20個位元組,再發30個位元組,那麼在接收端就會連續收到三次資料,但是接收端必須對每次接收到的資料進行處理,第一次接收到了15個位元組,而A隻有10個位元組,那麼多出來的5個位元組就是B的,這時候就出現了粘包,就是說資料包B的部分資料粘在的資料包A後面了,是以在接收端要截取完整的資料包再進行處理,把多出來的5個位元組緩存起來等待接收B資料,以此類推UDP就不存在粘包的問題,UDP發送的資料包都是一個完整的包,是以接收端收到的包也會是一個完整的資料包,但是UDP每次發送的資料包大小是有限的,最大不超過【UDP中的總長度字段為2位元組 所能表示的最大數為65535 UDP協定頭本身占據了8位元組 是以所能發送的最大資料長度是65535-8=65527】,而且UDP在網絡環境不好的情況下容易丢失,丢失了也不會通知到發送方或者接收方,是以UDP是不可靠連接配接,但是UDP連接配接并不會一直維持一條從發送放到接收方的連接配接,發送方隻要把資料扔出去就結束了發送流程,而接收方隻是開一個指定的端口并監聽這個端口,有資料來就處理,沒資料就不管,是以UDP連接配接省資源,在不丢失資料的情況下發送的效率比較高,占用記憶體少,而TCP是時時刻刻維持一個長連接配接,比較耗資源,占用較多記憶體

在移動開發中,IM一般采用TCP長連接配接,TCP長連接配接會遇到一下幾個問題 :

耗資源,增加app的耗電量;占記憶體,增加app的運作記憶體,提升了在手機休眠情況下app被系統殺死的機率,導緻TCP連接配接斷開,消息無法即時收到

IM開發中一般有一個心跳包的概念,為什麼要心跳包呢?因為網絡營運商中有一個NAT逾時,大部分移動無線網絡營運商都在鍊路一段時間沒有資料通訊時,會淘汰NAT【(Network Address Translation,NAT)。簡單的說營運商的網關需要維護一個外網IP、端口到内網IP、端口的對應關系,以確定内網的手機可以跟Internet的伺服器通訊】表中的對應項,造成鍊路中斷。NAT逾時是影響TCP連接配接壽命的一個重要因素(尤其是國内),是以用戶端自動測算NAT逾時時間,來動态調整心跳間隔

android裝置的一個bug,從網上查到的,DHCP租期到了不會主動續約并且會繼續使用過期IP,這會導緻TCP連接配接程式設計無效連接配接,參考網上資料DHCP租期

最近碰到一個問題就是app開一個TCP連接配接成功連到伺服器,然後手機鎖屏休眠,過一段時間後(可能是幾個小時)會發現這條TCP連接配接變成無效連接配接了,我從背景伺服器檢視手機裝置的TCP連接配接确實是斷開了,但是手機app代碼檢測TCP連接配接中Socket連接配接卻顯示連接配接正常,并且連接配接也沒有任何中斷異常,然後手機發送資料也是正常write成功,但是實際上伺服器根本沒有收到手機發送過來的資料,也就是說手機app的連接配接判斷都是錯誤的,這種情況下TCP連接配接判斷都是正常的,但是這條TCP連接配接确實無效不可用的,隻有把這條TCP連接配接主動斷開重新連接配接才能正常收發資料,伺服器才能重新顯示裝置的TCP連接配接成功,我估計這個很有可能就是3造成的,有可能DHCP租期到了,但是沒有繼續續約,而采用過期IP,那麼手機到營運商的網關的網絡是通的,而由于目标伺服器的IP已經過去,導緻網關無法幫手機app路由消息

關于TCP連接配接狀态的判斷,在java裡面是提供了isClosed()和isConnected()判斷的是本地的狀态,想要判斷TCP連接配接是否真的有效: socket.sendUrgentData(0xff),這個是立即發送一個位元組資料,如果服務端的Socket沒有開啟setOOBInline(true)的話是會預設忽略這個收到的位元組的,是以如果位元組資料發送成功就說明連接配接可用,如果位元組資料發送不成功就會抛出異常,說明連接配接不可用

作者:pzhpengpeng

連結:https://www.jianshu.com/p/05cfbb60c620

來源:簡書

著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。