在羣裏有個羣友在羣裏一直在問使用FixedHeaderReceiveFilter與msgpack結合的實現問題,搞了幾天都沒有搞定,因而就寫了這個文章來講明。但願能幫助到他。 SuperSocket的內置的經常使用協議實現模版文檔連接 msgpack的官網git
SuperSocket 簡稱爲SSc#
首先要定義一下協議數組
/// +-------+---+-------------------------------+ /// |request| l | | /// | name | e | request body | /// | (4) | n | | /// | |(4)| | /// +-------+---+-------------------------------+
如上說明,request name 佔4個字節,這個是用來尋找SS裏面的命令對象,根據是根據命令類名稱來查找的。 len是表示 request body序列化成byte後的長度。 request body 是表示咱們用msgpack序列化後的內容。緩存
*注意:*request name 和len佔用的字節是能夠本身定義的session
協議搞懂後,咱們就須要來編寫代碼了,須要編寫以下類:數據結構
下面來看下代碼的實現app
using System; using System.Collections.Generic; using System.Linq; using System.Text; using SuperSocket.Common; using SuperSocket.Facility.Protocol; using SuperSocket.SocketBase.Protocol; namespace FixedHeader { /// +-------+---+-------------------------------+ /// |request| l | | /// | name | e | request body | /// | (4) | n | | /// | |(4)| | /// +-------+---+-------------------------------+ public class MsgPackReceiveFilter : FixedHeaderReceiveFilter<BinaryRequestInfo> { public MsgPackReceiveFilter() : base(8) { } protected override int GetBodyLengthFromHeader(byte[] header, int offset, int length) { var headerData = new byte[4]; Array.Copy(header,offset+4,headerData,0,4); return BitConverter.ToInt32(headerData, 0); } protected override BinaryRequestInfo ResolveRequestInfo(ArraySegment<byte> header, byte[] bodyBuffer, int offset, int length) { return new BinaryRequestInfo(Encoding.UTF8.GetString(header.Array, header.Offset, 4), bodyBuffer.CloneRange(offset, length)); } } }
首先咱們看到構造函數base(8)裏面的輸入了一個8,這個8是協議頭的長度,也就是request name 加 len的長度。 而後再看實現了方法GetBodyLengthFromHeader,從名字上看,就能夠知道是根據協議頭的數據來獲取打包的數據的長度。 這個方法有三個參數socket
在這裏咱們能夠取獲得協議頭的數據,就是在header從偏移量offset開始截取長度爲length的部分數組,就是咱們的協議頭了。可是咱們的協議頭是8位,要取打包數據的長度那麼就須要從偏移offset上再加4位,代碼就是ide
var headerData = new byte[4]; Array.Copy(header,offset+4,headerData,0,4);
而後再把取到的數據轉換成爲int類型也就是函數
BitConverter.ToInt32(headerData, 0);
ss就能夠根據這個長度來幫助咱們獲取到打包的數據。而後傳給方法ResolveRequestInfo。咱們須要實現這個方法。這個方法有四個參數:
ResolveRequestInfo 返回的是 FixedHeaderReceiveFilter的一個泛型,這個對象是用於注入實現命令的。咱們這裏使用的是BinaryRequestInfo。
Encoding.UTF8.GetString(header.Array, header.Offset, 4)
這個代碼是把協議頭的前四位轉換成爲字符串,這字符串是用於查找要執行的命令的。
bodyBuffer.CloneRange(offset, length)
這個代碼是截取咱們須要的數據,這個數據也將是會傳給執行命令的。
到這裏咱們的MsgPackReceiveFilter協議解析已經完成了,而後再實現一個工廠來使得server可以加載到MsgPackReceiveFilter來解析咱們的協議
using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Text; using SuperSocket.SocketBase; using SuperSocket.SocketBase.Protocol; namespace FixedHeader { public class MsgPackReceiveFilterFactory : IReceiveFilterFactory<BinaryRequestInfo> { public IReceiveFilter<BinaryRequestInfo> CreateFilter(IAppServer appServer, IAppSession appSession, IPEndPoint remoteEndPoint) { return new MsgPackReceiveFilter(); } } }
MsgPackReceiveFilterFactory的實現相對簡單,就是實現接口IReceiveFilterFactory的方法返回一個MsgPackReceiveFilter對象,這個就用多作解釋
而後要使得server可以加載到,還須要再實現一個server
using System; using System.Collections.Generic; using System.Linq; using System.Text; using SuperSocket.SocketBase; using SuperSocket.SocketBase.Protocol; namespace FixedHeader { public class MsgPackServer : AppServer<MsgPackSession, BinaryRequestInfo> { public MsgPackServer() : base(new MsgPackReceiveFilterFactory()) { } } }
只要實例化工廠MsgPackReceiveFilterFactory而後傳給構造函數就能夠了。
而後再實現一個MsgPackSession這個只要繼承AppSession就能夠了,不用作任何的實現。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using SuperSocket.SocketBase; using SuperSocket.SocketBase.Protocol; namespace FixedHeader { public class MsgPackSession : AppSession<MsgPackSession, BinaryRequestInfo> { } }
而後再實現一個MsgPackCommand。這個主要是爲了把打包發送過來的數據統一反序列列化,這樣只要繼承MsgPackCommand的類,均可以直接獲得想要的對象。
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using MsgPack.Serialization; using SuperSocket.SocketBase.Command; using SuperSocket.SocketBase.Protocol; namespace FixedHeader { public abstract class MsgPackCommand<TMsgPack> : CommandBase<MsgPackSession, BinaryRequestInfo> where TMsgPack : class { public override void ExecuteCommand(MsgPackSession session, BinaryRequestInfo requestInfo) { var serializer = SerializationContext.Default.GetSerializer<TMsgPack>(); using (var stream = new MemoryStream(requestInfo.Body)) { var unpackedObject = serializer.Unpack(stream) as TMsgPack; ExecuteCommand(session, unpackedObject); } } public abstract void ExecuteCommand(MsgPackSession session, TMsgPack pack); } }
最後再作一個測試的命令和一個數據結構,
using System; using System.Collections.Generic; using System.Linq; using System.Text; using SuperSocket.SocketBase.Command; using SuperSocket.SocketBase.Protocol; namespace FixedHeader { public class Test : MsgPackCommand<MyData> { public override void ExecuteCommand(MsgPackSession session, MyData pack) { Console.WriteLine(pack.Name+":"+ pack.Other); } } }
namespace FixedHeader { public class MyData { public string Name { get; set; } public string Other { get; set; } } }
到這裏就服務端就能夠了。接下來還須要實現一個客戶端來作簡單的測試,這個沒有什麼好說的,直接上代碼:
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net.Sockets; using System.Text; using MsgPack.Serialization; namespace FixedHeaderClient { class Program { static void Main(string[] args) { var socket = new Socket(AddressFamily.InterNetwork,SocketType.Stream, ProtocolType.Tcp); socket.Connect("127.0.0.1",2012); using (var stream = new MemoryStream()) { var serializer = SerializationContext.Default.GetSerializer<MyData>(); var myData = new MyData() { Name = "Test", Other = "abcd" }; serializer.Pack(stream, myData); //var commandData = new byte[4];//協議命令只佔4位 var commandData = Encoding.UTF8.GetBytes("Test");//協議命令只佔4位,若是佔的位數長過協議,那麼協議解析確定會出錯的 var dataBody = stream.ToArray(); var dataLen = BitConverter.GetBytes(dataBody.Length);//int類型佔4位,根據協議這裏也只能4位,不然會出錯 var sendData = new byte[8+dataBody.Length];//命令加內容長度爲8 // +-------+---+-------------------------------+ // |request| l | | // | name | e | request body | // | (4) | n | | // | |(4)| | // +-------+---+-------------------------------+ Array.ConstrainedCopy(commandData, 0, sendData, 0, 4); Array.ConstrainedCopy(dataLen, 0, sendData, 4,4); Array.ConstrainedCopy(dataBody, 0, sendData, 8, dataBody.Length); for (int i = 0; i < 1000; i++) { socket.Send(sendData); } } Console.Read(); } } }