Socket理解。

?

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

?

可是來到通訊系統,一切變得困難復雜。原因實在太多了,如:

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

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

?

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

現有的Socket編程資源

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

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

?

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

?

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

?

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

Socket的選擇

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

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

?

1. 如果是短鏈接,使用同步socket。例如http服務器、轉接服務器等等。

?

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

?

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

?

4. 我聽說了java的NIO性能在5000/s所有,我們項目內部也進行了鏈接測試,在4000~5000比較穩定,當然如果代碼調優之后,能提高一點點。

?

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

TCP Socket協議定義

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

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

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

?

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

?

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

?

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

?

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

代碼
<!--

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環境如此惡劣,但是TCP的鏈接也至少保證了:

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

?

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

?

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

?

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

?

3. 包體就是自定義的一些協議內容,例如是對像序列化的內容(現有的系統已經很常見了,使用對象序列化、反序列化能夠極大簡化開發流程,等版本穩定后再轉入手工壓入byte操作)。

?

一個實際編寫的例子:比如我要傳輸2個整型 int = 1, int = 2,那么實際傳輸的數據包如下:

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

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

這個數據包就是4個整型,總長度 = 4*4? = 16。

?

說說我走的彎路:

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

?

?

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

Socket接收的邏輯概述

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

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

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

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

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

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

?

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

?

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

Socket服務器參數概述

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

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

因此,我們寫socket服務器,就要面對2個環境:

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

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

?

針對以上幾點,我們的服務器設計必須包含以下參數:

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

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

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

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

?

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

?

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

?

?

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

后記

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

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


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

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

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

  • 3.網絡Comet平臺再次成功對接上線.[2011-1-9]

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/254156.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/254156.shtml
英文地址,請注明出處:http://en.pswp.cn/news/254156.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

多元化時代敏捷軟件開發的崛起與傳統軟件工程的延續

多元化時代敏捷軟件開發的崛起與傳統軟件工程的延續 1.傳統軟件開發模式 1.1瀑布模型 1.1.1概念 瀑布模型&#xff0c;顧名思義&#xff0c;軟件開發的過程如同瀑布飛流一般&#xff0c;自上而下&#xff0c;逐級下落。瀑布模型的核心思想是將問題按照工序進行簡化&#xff0c;…

Linux中的cron計劃任務配置詳解

cron來源于希臘單詞chronos&#xff08;意為“時間”&#xff09;&#xff0c;指Linux系統下一個自動執行指定任務的程序&#xff08;計劃任務&#xff09; ####1. crontab命令選項代碼如下: #crontab -u <-l, -r, -e> -u指定一個用戶 -l列出某個用戶的任務計劃 -r刪除某…

new和delete

和 sizeof 類似&#xff0c;sizeof不是函數&#xff0c;它是一個操作符&#xff0c;它在編譯期就完成了計算&#xff0c;在函數運行期間它已經是一個常數值了。 int a;sizeof(int) 4;sizeof(a) 4;sizeof a ——也是4 不需要括號&#xff01;此時要注意&#xff1a;sizeof in…

char a[]和char *a的比較,數組名,數組首地址,a,a,a[0]

char a[]和char *a的比較 指針和數組存在著一些本質的區別。當然&#xff0c;在某種情況下&#xff0c;比如數組作為函數的參數進行傳遞時&#xff0c;由于該數組自動退化為同類型的指針&#xff0c;所以在函數內部&#xff0c;作為函數參數傳遞進來的指針與數組確實具有一定的…

Java中繼承thread類與實現Runnable接口的區別

Java中線程的創建有兩種方式&#xff1a; 1&#xff0e; 通過繼承Thread類&#xff0c;重寫Thread的run()方法&#xff0c;將線程運行的邏輯放在其中 2&#xff0e; 通過實現Runnable接口&#xff0c;實例化Thread類 在實際應用中&#xff0c;我們經常用到多線程&#xff0c;…

【VMware vSAN 6.6】6.2.啟用性能服務:vSAN硬件服務器解決方案

目錄 1. 簡介 1.1.適用于HCI的企業級存儲2. 體系結構 2.1.帶有本地存儲的服務器2.2.存儲控制器虛擬系統套裝的缺點2.3.vSAN在vSphere Hypervisor中自帶2.4.集群類型2.5.硬件部署選項3. 啟用vSAN 3.1.啟用vSAN3.2.輕松安裝3.3.主動測試4. 可用性 4.1.對象和組件安置4.2.重新構建…

Android eclipse導入項目后出現Unable to resolve target #39;android-17#39;解決方法

eclipse導入項目后出現Unable to resolve target android-17解決方法。在最后附帶還有一種編譯邏輯不成功情況解決方法。 一、問題情況 二、解決的方法 1、改動項目的目標版本號與當前Android sdk相相應的版本號 2、自己主動修復一下項目 三、這個問題不是上面的。是另外情況&a…

多個圓點,鼠標選取兩個,求兩個點的距離,用于計算像素尺寸(halcon實現)

read_image (Image, C:/Users/22967/Desktop/晶圓找位置/0.bmp) dev_close_window () dev_open_window_fit_image (Image, 0, 0, -1, -1, WindowHandle) dev_display (Image)binary_threshold (Image, Region1, max_separability, dark, UsedThreshold) connection (Region1, C…

修改UBOOT和LINUX調試串口(TI達芬奇芯片--DM6467)

Posted on 2011-10-31 10:53 jamiedu 閱讀(889) 評論(0) 編輯 收藏 1.1 概述 TI針對DM6467提供的UBOOT和內核默認都是串口0作為調試串口輸出的&#xff0c;但現在我需要使用DM6467的UART0的modem功能&#xff0c;所以修改代碼&#xff0c;改變調試串口為串口2。 需要修改的主要…

Java List與數組之間的轉換

http://blog.csdn.net/kingzone_2008/article/details/8444678轉載于:https://www.cnblogs.com/longshiyVip/p/5985981.html

受歡迎的五個開源可視化工具——你的選擇是?

摘要&#xff1a;大數據時代&#xff0c;數據為王&#xff0c;還在對一堆數據而發愁嗎&#xff1f;試試可視化工具吧&#xff0c;相信本文提到的五款工具有一款能夠幫助到你。人工智能時代&#xff0c;數據和算法以及硬件資源是非常重要的&#xff0c;相關行業的大公司也越來越…

halcon車刀崩邊檢測

list_files (新建文件夾, files, Files) read_image (Image, Files[0]) dev_close_window () get_image_size (Image, Width, Height) dev_open_window (0, 0, Width/1.5, Height/1.5, black, WindowHandle) dev_set_draw (margin) dev_set_colored (12) for Index:0 to |Files…

FFMPEG解碼264文件步驟

本文以H264視頻流為例&#xff0c;講解解碼流數據的步驟。 為突出重點&#xff0c;本文只專注于討論解碼視頻流數據&#xff0c;不涉及其它&#xff08;如開發環境的配置等&#xff09;。如果您需要這方面的信息&#xff0c;請和我聯系。 準備變量 定義AVCodecContext。如果…

Storm概念學習系列之storm的特性

不多說&#xff0c;直接上干貨&#xff01; storm的特性 Storm 是一個開源的分布式實時計算系統&#xff0c;可以簡單、可靠地處理大量的數據流。 Storm支持水平擴展&#xff0c;具有高容錯性&#xff0c;保證每個消息都會得到處理&#xff0c;而且處理速度很快&#xff08;在一…

Confluence 6 配置服務器基礎地址示例

2019獨角獸企業重金招聘Python工程師標準>>> 如果 Confluence 的安裝是沒有安裝在非根目錄路徑&#xff08;這個是上下文路徑&#xff09;&#xff0c;然后服務器基礎 URL 地址應該包括上下文地址。例如&#xff0c;你的 Confluence 正在運行在下面的地址&#xff1…

BootstrapValidator驗證

bootstrap&#xff1a;能夠增加兼容性的強大框架. 因為項目需要數據驗證&#xff0c;看bootstrapValidator 還不錯&#xff0c;就上手一直&#xff0c;完美兼容&#xff0c;話不多說。 需要引用css&#xff1a; bootstrap.min.css bootstrapValidator.min.css js: jquery-1.10.…

基于ARM9的視頻采集傳輸系統

http://www.ic37.com/htm_tech/2007-11/77189_618093.htm

halcon找矩形頂點的一種方法

主程序&#xff1a; read_image (Image11, 11)*畫仿射矩形 dev_set_color (green) draw_rectangle2 (3600, Row, Column, Phi, Length1, Length2)*生成仿射矩形xld gen_rectangle2_contour_xld (Rectangle, Row, Column, Phi, Length1, Length2) *找頂點工具&#xff08;基于卡…

老男孩linux運維50期

一、自我介紹&#xff1a;我是來自老男孩Linux運維脫產50期的楊國峰&#xff0c;我以前是學軟件編碼的&#xff0c;但在大學里基本沒怎么學&#xff0c;每一門課都一知半解的&#xff0c;甚至有些連軟件都不會裝&#xff0c;在校期間&#xff0c;我對JAVA、網頁設計等都不感興趣…

博客收藏

http://www.dreamfairy.cn/blog/category/unity3d/轉載于:https://www.cnblogs.com/wantnon/p/5989843.html