日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Socket理解。

發(fā)布時間:2023/12/10 编程问答 18 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Socket理解。 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

?

其他大部分系統(tǒng),例如CRM/CMS/權(quán)限框架/MIS之類的,無論怎么復雜,基本上都能夠本地代碼本地調(diào)試,性能也不太重要。(也許這個就是.net的企業(yè)級開發(fā)的戰(zhàn)略吧)

?

可是來到通訊系統(tǒng),一切變得困難復雜。原因?qū)嵲谔嗔?#xff0c;如:

  • 性能永遠是第一位:有時候一個if判斷都要考慮性能,畢竟要損耗一個CPU指令,而在通訊系統(tǒng)服務(wù)器,每秒鐘都產(chǎn)生上百萬級別的通訊量,這樣一個if就浪費了1個毫秒了。
  • 系統(tǒng)環(huán)境極其惡劣:所有我們可以想象的惡意攻擊、異常輸入等都要考慮;
  • 網(wǎng)絡(luò)說斷就斷:在socket環(huán)境下,客戶端可以以各種理由斷開鏈接,而且服務(wù)器根本不會知道,連一個流水作業(yè)的業(yè)務(wù)邏輯都無法保證正常執(zhí)行,因此需要設(shè)計各種輔助的協(xié)議、架構(gòu)去監(jiān)督。
  • 各種網(wǎng)絡(luò)鏈接問題:例如代理、防火墻等等。。。

經(jīng)過了1年的跌跌撞撞,我總算收獲了點有用的經(jīng)驗,本文先從設(shè)計角度介紹一些我在Socket編程中的經(jīng)驗,下一篇在放出源代碼。

?

------------------

現(xiàn)有的Socket編程資源

------------------

1. 首選推薦開源的XMPP框架,也就是Google的Gtalk的開源版本。里面的架構(gòu)寫的非常漂亮。特點就是:簡潔、清晰。

?

2. 其次推薦LumaQQ.net,這套框架本身寫的一般般,但是騰訊的服務(wù)器非常的猛,這樣必然導致客戶端也要比較猛。通過學習這套框架,能夠了解騰訊的IM傳輸協(xié)議設(shè)計,而且他們的協(xié)議是TCP/UDP結(jié)合,一舉兩得。

?

3. 最后就是DotMsn。這個寫的實在很一般般,而且也主要針對了MSN的協(xié)議特點。是能夠?qū)W習到一點點的框架知識的,不過要有所鑒別。

?

------------------

Socket的選擇

------------------

在Java,到了Java5終于出現(xiàn)了異步編程,NIO,于是各種所謂的框架冒了出來,例如MINA, xsocket等等;而在.NET,微軟一早就為我們準備好了完善的Socket模型。主要包括:同步Socket、異步Socket;我還聽說了.net 3.x之后,異步的Socket內(nèi)置了完成端口。綜合各種模型的性能,我總結(jié)如下:

?

1. 如果是短鏈接,使用同步socket。例如http服務(wù)器、轉(zhuǎn)接服務(wù)器等等。

?

2. 如果是長鏈接,使用異步socket。例如通訊系統(tǒng)(QQ / Fetion)、webgame等。

?

3. .net的異步socket的連接數(shù)性能在 7500/s(每秒并發(fā)7500個socket鏈接)。而聽說完成端口在1.5w所有。但是我到目前還沒有正式見過所謂的完成端口,不知道到底有多牛逼。

?

4. 我聽說了java的NIO性能在5000/s所有,我們項目內(nèi)部也進行了鏈接測試,在4000~5000比較穩(wěn)定,當然如果代碼調(diào)優(yōu)之后,能提高一點點。

?

------------------

TCP Socket協(xié)議定義

------------------

本文從這里開始,主要介紹TCP的socket編程。

新手們(例如當初的我),第一次寫socket,總是以為在發(fā)送方壓入一個"Helloworld",接收方收到了這個字符串,就“精通”了Socket編程了。而實際上,這種編程根本不可能用在現(xiàn)實項目,因為:

?

1. socket在傳輸過程中,helloworld有可能被拆分了,分段到達客戶端),例如 hello???+?? world,一個分段就是一個包(Package),這個就是分包問題

?

2. socket在傳輸過成功,不同時間發(fā)送的數(shù)據(jù)包有可能被合并,同時到達了客戶端,這個就是黏包問題。例如發(fā)送方發(fā)送了hello+world,而接收方可能一次就接受了helloworld.

?

3. socket會自動在每個包后面補n個 0x0 byte,分割包。具體怎么去補,這個我就沒有深入了解。

?

4. 不同的數(shù)據(jù)類型轉(zhuǎn)化為byte的長度是不同的,例如int轉(zhuǎn)為byte是4位(int32),這樣我們在制作socket協(xié)議的時候要特別小心了。具體可以使用以下代碼去測試:

代碼 <!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

-->????????public?void?test()
????????{
????????????
int?myInt?=?1;
????????????
byte[]?bytes?=?new?byte[1024];
????????????BinaryWriter?writer?
=?new?BinaryWriter(new?MemoryStream(bytes));
????????????writer.Write(myInt);
????????????writer.Write(
"j");
????????????writer.Close();
????????}

?

?

盡管socket環(huán)境如此惡劣,但是TCP的鏈接也至少保證了:

  • 包發(fā)送順序在傳輸過程中是不會改變的,例如發(fā)送方發(fā)送 H E L L,那么接收方一定也是順序收到H E L L,這個是TCP協(xié)議承諾的,因此這點成為我們解決分包、黏包問題的關(guān)鍵。
  • 如果發(fā)送方發(fā)送的是helloworld, 傳輸過程中分割成為hello+world,那么TCP保證了在hello與world之間沒有其他的byte。但是不能保證helloworld和下一個命令之間沒有其他的byte。

?

因此,如果我們要使用socket編程,就一定要編寫自己的協(xié)議。目前業(yè)界主要采取的協(xié)議定義方式是:包頭+包體長度+包體。具體如下:

?

1. 一般包頭使用一個int定義,例如int = 173173173;作用是區(qū)分每一個有效的數(shù)據(jù)包,因此我們的服務(wù)器可以通過這個int去切割、合并包,組裝出完整的傳輸協(xié)議。有人使用回車字符去分割包體,例如常見的SMTP/POP協(xié)議,這種做法在特定的協(xié)議是沒有問題的,可是如果我們傳輸?shù)男畔?nèi)容自帶了回車字符串,那么就糟糕了。所以在設(shè)計協(xié)議的時候要特別小心。

?

2. 包體長度使用一個int定義,這個長度表示包體所占的比特流長度,用于服務(wù)器正確讀取并分割出包。

?

3. 包體就是自定義的一些協(xié)議內(nèi)容,例如是對像序列化的內(nèi)容(現(xiàn)有的系統(tǒng)已經(jīng)很常見了,使用對象序列化、反序列化能夠極大簡化開發(fā)流程,等版本穩(wěn)定后再轉(zhuǎn)入手工壓入byte操作)。

?

一個實際編寫的例子:比如我要傳輸2個整型 int = 1, int = 2,那么實際傳輸?shù)臄?shù)據(jù)包如下:

?? 173173173?????????????? 8????????????????? 1?????????2

|------包頭------|----包體長度----|--------包體--------|

這個數(shù)據(jù)包就是4個整型,總長度 = 4*4? = 16。

?

說說我走的彎路:

我曾經(jīng)偷懶,使用特殊結(jié)束符去分割包體,這樣傳輸?shù)臄?shù)據(jù)包就不需要指名長度了。可是后來高人告訴我,如果使用特殊結(jié)束符去判斷包,性能會損失很大,因為我們每次讀取一個byte,都要做一次if判斷,這個性能損失是非常嚴重的。所以最終還是走主流,使用以上的結(jié)構(gòu)體。

?

?

------------------

Socket接收的邏輯概述

------------------

針對了我們的數(shù)據(jù)包設(shè)計+socket的傳輸特點,我們的接收邏輯主要是:

1. 尋找包頭。這個包頭就是一個int整型。但是寫代碼的時候要非常注意,一個int實際上占據(jù)了4個byte,而可悲的是這4個byte在傳輸過程中也可能被socket 分割了,因此讀取判斷的邏輯是:

  • 判斷剩余長度是否大于4
  • 讀取一個int,判斷是否包頭,如果是就跳出循環(huán)。
  • 如果不是包頭,則倒退3個byte,回到第一點。
  • 如果讀取完畢也沒有找到,則有可能包頭被分割了,因此當前已讀信息壓入接收緩存,等待下一個包到達后合并判斷。

2. 讀取包體長度。由于長度也是一個int,因此判斷的時候也要小心,同上。

3. 讀取包體,由于已知包體長度,因此讀取包體就變得非常簡單了,只要一直讀取到長度未知,剩余的又回到第一條尋找包頭。

?

這個邏輯不要小看,就這點東西忙了我1天時間。而非常奇怪的是,我發(fā)現(xiàn)c#寫的socket,似乎沒有我說的這么復雜邏輯。大家可以看看LumaQQ.net / DotMsn等,他們的socket接收代碼都非常簡單。我猜想:要么是.net的socket進行了優(yōu)化,不會對int之類的進行分割傳輸;要么就是作者偷懶,隨便寫點代碼開源糊弄一下。

?

------------------

Socket服務(wù)器參數(shù)概述

------------------

我在開篇也說了,Socket服務(wù)器的環(huán)境是非常糟糕了,最糟糕的就是客戶端斷線之后服務(wù)器沒有收到通知。 因為socket斷線這個也是個信息,也要從客戶端傳遞到我們socket服務(wù)器。有可能網(wǎng)絡(luò)阻塞了,導致服務(wù)器連斷開的通知都沒有收到。

因此,我們寫socket服務(wù)器,就要面對2個環(huán)境:

1. 服務(wù)器在處理業(yè)務(wù)邏輯中的任何時候都會收到Exception, 任何時候都會因為鏈接中斷而斷開。

2. 服務(wù)器接收到的客戶端請求可以是任意字符串,因此在處理業(yè)務(wù)邏輯的時候,必須對各種可能的輸入都判斷,防止惡意攻擊。

?

針對以上幾點,我們的服務(wù)器設(shè)計必須包含以下參數(shù):

1. 客戶端鏈接時間記錄:主要判斷客戶端空連接情況,防止連接數(shù)被惡意占用。

2. 客戶端請求頻率記錄:要防止客戶端頻繁發(fā)送請求導致服務(wù)器負荷過重。

3. 客戶端錯誤記錄:一次錯誤可能導致服務(wù)器產(chǎn)生一次exception,而這個性能損耗是非常嚴重的,因此要嚴格監(jiān)控客戶端的發(fā)送協(xié)議錯誤情況。

4. 客戶端發(fā)送信息長度記錄:有可能客戶端惡意發(fā)送非常長的信息,導致服務(wù)器處理內(nèi)存爆滿,直接導致宕機。

?

5. 客戶端短時間暴漲:有可能在短時間內(nèi),客戶端突然發(fā)送海量數(shù)據(jù),直接導致服務(wù)器宕機。因此我們必須有對服務(wù)器負荷進行監(jiān)控,一旦發(fā)現(xiàn)負荷過重,直接對請求的socket返回處理失敗,例如我們常見的“404”。

?

6. 服務(wù)器短時間發(fā)送信息激增:有可能在服務(wù)器內(nèi)部處理邏輯中,突然產(chǎn)生了海量的數(shù)據(jù)需要發(fā)送,例如游戲中的“群發(fā)”;因此必須對發(fā)送進行隊列緩存,然后進行合并發(fā)送,減輕socket的負荷。

?

?

------------------

后記

------------------

本文從架構(gòu)設(shè)計分析了一個socket服務(wù)器的設(shè)計要點。如果您有其他見解,歡迎留言與討論。


我們的最新動態(tài) (Bamboo@pixysoft.net)

  • 1.解決comet在多頁面中沖突的問題.[2011-1-23]

  • 2.優(yōu)化部分后臺邏輯代碼.提升首頁訪問性能[2011-1-20]

  • 3.網(wǎng)絡(luò)Comet平臺再次成功對接上線.[2011-1-9]

總結(jié)

以上是生活随笔為你收集整理的Socket理解。的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。