程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> FastSocket學習筆記~制定自已的傳輸協議,fastsocket學習筆記

FastSocket學習筆記~制定自已的傳輸協議,fastsocket學習筆記

編輯:C#入門知識

FastSocket學習筆記~制定自已的傳輸協議,fastsocket學習筆記


對於TCP或者UDP來說,它們作於傳輸層的協議,有著自己的標准,或者叫格式,在我們看TCP格式之前先了解一下計算機的基礎知識,字節,它是計算機世界的一個小單位,也是我們可以理會到的,如一個utf-8英文字母表示一個字節(byte),而一個漢字或者一個unicode的英文字母則表示為兩個字節,或者說在計算機中它將占兩個字節的存儲空間。而在編程世界裡,通常所說的Int32,Int16,Int64這些是類型的後綴是指它占用的位數(bit,8bit=1byte),即一個int32類型的數字,占用4個字節的存儲空間,其實這在之前的教課書上大家都見過,也都背過,但可能就是沒有用過,學以不至用,不是一件好事,下面就讓大家學以至用一下。

TCP協議格式

TCP各字段的含義

Source Port & Destination Port
如果我們將IP比喻成地址那麽Port可以說是門口了試想一下一座大樓有前門後門側門送貨的門出貨的門倒垃圾的門扔死屍的門等等亂七八糟的門...... 那麽一個IP地址也有著好多個各種功能的port而每一個port都被不同的服務傾聽著就好比看門人一樣。下面是一些常用port和其對應的服務有興趣的 朋友可以在Linux的/etc/services這個檔案找到它們
ftp-data 20/tcp
ftp 21/tcp
telnet 23/tcp
smtp 25/tcp mail
www 80/tcp http # WorldWideWeb HTTP
www 80/udp # HyperText Transfer Protocol
pop-3 110/tcp # POP version 3
pop-3 110/udp
其實port號碼可以隨您喜歡任意指定給哪些服務使用但為了避免“找錯門口”的情形出現(除非您故意想躲起來)人們將一些比較常用的服務(Well known services)的port號碼固定下來了。但是在TCP資料傳送過程中可能同時要處理一個以上的封包程式也會建立多個port來避免突。在兩台主機進 行資料傳送的時候來源地的port和目的地的port都必須讓TCP知道才行。
Sequence Number
發送序號。當資料要從一台主機傳送去另一台主機的時候發送端會為封包建立起一個初始號碼然後按照所傳送的位元組數依次的遞增上去那麽下一個封包的序號就會使用遞增之後的值來作為它的序號了。這樣接收端就可以根據序號來檢測資料是否接收完整了。
Acknowledgement Number
回應序號。當接收端接收到TCP封包之後通過檢驗確認之後然後會依照發送序號產生一個回應序號發出一個回應封包給發送端這樣接收端就知道剛才的封包已經被成功接收到了。
可是如果由於網路狀況或其它原因當封包的TTL值達到期限時接收端還沒接收到回應序號就會重發該個被以為丟失了的封包。但如果剛好重發封包之後才接收到回應呢這時候接收端就會根據序號來判斷該封包是否被重發送如果是的話很簡單將之丟棄不做任何處理就是了。
Data Offset
這是用來記錄標頭固定長度用的,和IP封包的IHL差不多。如果options沒設定的話,其長度就是20byte,用十六進位表示就是 0x14了。
Reserved
這是保留區間暫時還沒被使用。
Contral Flag
控制標記。一共有六個它們分別是: Urgent data
如果URG為1,表示這是一個攜有緊急資料的封包。
Acknowledgment field significant
如果ACK為1,表示此封包屬於一個要回應的封包。一般都會為1。
Push function
如果PSH為1,此封包所攜帶的數據會直接上傳給上層應用程序而無需經過TCP處理。
Reset
如果RST為1,要求重傳。表示要求重新設定封包再重新傳遞。
Synchronize sequence number
如果SYN為1,表示要求雙方進行同步溝通。
No more data for sender (Finish)
如果FIN為1,表示傳送結束,然後雙方發出結束回應進而正式終止一個TCP傳送過程。
Window
我們都知道MS Windows是什麼東西,但這裡的Window卻非操作系統的“視窗”哦,這裡一般稱為“滑動視窗(Sliding Window)”。為什麼我們需要使用視窗呢?
正如您剛才看到的TCP封包會通過SQN和ACK序號來確保傳送的正確性,但如果每一個封包都要等上一個封包的回應才被發送出去的話實在是太慢和難以接受 的。這樣我們可以利用Sliding Window在傳送兩端劃分出一個范圍,規定出可以一次性發送的最大封包數目。
當TCP傳送建立起來之後兩端都會將window的設定值還原到初始值比方說每次傳送3個封包。然後發送端就一次發送三個封包出去,然後視窗則會往後移動 三個封包填補發送出去之封包的空缺。如果接收端夠順利也能一次處理接收下來的三個封包的話,就會告訴發送端的window值為3,但如果接收端太忙或是其 它因素影響暫時只能處理兩個封包,那麼在視窗裡面就剩下一個封包,然後就會告訴發送端window值為2。這個時候發送端就只送出兩個封包而視窗就會往後 移動兩個封包填補發送出去的空缺。您明白為什麽這個視窗會“滑動”了吧。
其實,Window值是以字節數計算的。
Chechsum
當資料要傳送出去的時候發送端會計算好封包資料大小然後得出這個檢驗值封包一起發送當接收端收到封包之後會再對資料大小進行計算看看是否和檢驗值一致如果結果不相稱則被視為殘缺封包會要求對方重發該個封包。
Urgent Pointer
還記得剛才講到Control Flag的時候我們提到一個URG的標記嗎如果URG被設定為一的時候這裡就會指示出緊急資料所在位置。不過這種情形非常少見例如當資料流量超出頻寬的時候系統要求網路主機暫緩發送資料所有主機收到這樣的信息都需要優先處理。
Option
這個選項也比較少用。當那些需要使用同步動作的程式如Telnet要處理好終端的交互模式就會使用到option來指定資料封包的大小因為telnet使用的資料封包都很少但又需要即時回應。
Option的長度為0,或32bit的整倍數,如果不足則填充到滿。

自己動手,搞自己的協議格式

[bodyLength int32][id int32][type int16][name char(20)][body byte[]]

下面我們來測試一個協議,為它賦值,模擬一下client和server的發送與接受的過程

            string name = "zzl";
            var message = SerializeHelper.SerializeToBinary(new UserDTO { ID = 1, Name = "repositoryUncle" });
            int bodyLength = 4 + 4 + 2 + name.Length + message.Length;
            byte[] buffer = new byte[bodyLength];
            byte[] body = new byte[bodyLength];

            #region (發送端)寫入字節流
            //write message length
            Buffer.BlockCopy(BitConverter.GetBytes(bodyLength), 0, buffer, 0, 4);
            //write id.
            Buffer.BlockCopy(BitConverter.GetBytes(101), 0, buffer, 4, 4);
            //write response type.
            Buffer.BlockCopy(BitConverter.GetBytes((short)4), 0, buffer, 8, 2);
            //write response name.
            Buffer.BlockCopy(Encoding.ASCII.GetBytes(name), 0, buffer, 10, name.Length);
            //write response message.
            Buffer.BlockCopy(message, 0, buffer, 10 + name.Length, message.Length);

            #endregion

            #region (接收端)讀字節流
            var messageLength = BitConverter.ToInt32(buffer, 0);
            var id = BitConverter.ToInt32(buffer, 4);
            var type = BitConverter.ToInt16(buffer, 8);
            var nameVal = Encoding.ASCII.GetString(buffer, 10, name.Length);
            var dataLength = messageLength - 10 - name.Length;
            Buffer.BlockCopy(buffer, 10 + name.Length, body, 0, dataLength);
            var obj = (UserDTO)SerializeHelper.DeserializeFromBinary(body);
            #endregion

我們在測試時,可以設置斷點去觀察一下messageLength,id,type,nameVal,obj等元素的值,是否正確解析了,呵呵。

最後一點要記住,協議並不難,字節流也並不神秘,關鍵在於堅持,不斷的去探索、研究,有了這幾點,再麻煩的事贊也能搞定!

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved