BeetleX
beetleX是基於dotnet core實現的輕量級高性能的TCP通訊組件,使用方便、性能高效和安全可靠是組件設計的出發點!開發人員可以在Beetlx組件的支持下快帶地構建高性能的TCP通訊服務程序,在安全通訊方面只需要簡單地設置一下SSL信息即可實現可靠安全的SSL服務。
項目地址:https://github.com/IKende/BeetleX
使用方便性
beetleX網絡流讀寫是基於Stream標準來構建,僅僅基於Stream的基礎讀寫對於應用者來說還是過於繁瑣;組件爲了更方便進行網絡數據處理在Stream的基礎之上擴展了一系列的讀寫規則:ReadLine、ReadInt、WriteLine、WriteInt等一系列簡便方法,在這些方法的支持下使用者就可以更輕鬆地處理數據;爲了在網絡通訊中更好的兼容其他平臺協議以上方法都兼容Big-Endian和Little-Endian不同方式。爲了更好地利用現有序列化組件,組件通過IPacket接口規範消息擴展,通過實現不同的Packet解釋器,即可以實現基於Protobuf,json和Msgpack等方式的對象數據傳輸。
高性能特性
beetleX的高性能是建立在內部一個數據流處理對象PipeStream,它是構建在Stream標準之上;它和.NET內置的NetworkStream最大的差別是PipeStream的讀寫基於SocketAsyncEventArgs實現,這正是在編寫高性能網絡數據處理所提倡的模式。PipeStream不僅在網絡數據處理模式上有着性能的優勢,在內存讀寫上和MemoryStream也有着很大的區別;由於PipeStream的內存塊是以一個基於鏈表的SocketAsyncEventArgs Buffer 組成,因此PipeStream在寫入大數據的情況並不存在內存擴容和複製的問題;因爲PipeStream基礎內存是SocketAsyncEventArgs Buffer,所以在數據和網絡緩存讀寫並不存在內存塊複製過程。如果在應用中中使用PipeStream相應的BinaryReader和IBinaryWriter讀寫規範,那大部分數據處理基本不存在內存複製過程,從而讓數據處理性能更高效。
性能
beetleX的性能到底怎樣呢,以下簡單和DotNetty進行一個網絡數據交換的性能測試,分別是1K,5K和10K連接數下數據請求併發測試
DotNetty測試代碼
public override void ChannelRead(IChannelHandlerContext context, object message)
{
var buffer = message as IByteBuffer;
context.WriteAsync(message);
}
Beetlex 測試代碼
public override void SessionReceive(IServer server, SessionReceiveEventArgs e)
{
server.Send(e.Stream.ToPipeStream().GetReadBuffers(), e.Session);
base.SessionReceive(server, e);
}
測試結果
1K connections
5K connections
10K connections
構建TCP Server
class Program : ServerHandlerBase
{
private static IServer server;
public static void Main(string[] args)
{
NetConfig config = new NetConfig();
//ssl
//config.SSL = true;
//config.CertificateFile = @"c:\ssltest.pfx";
//config.CertificatePassword = "123456";
server = SocketFactory.CreateTcpServer<Program>(config);
server.Open();
Console.Write(server);
Console.Read();
}
public override void SessionReceive(IServer server, SessionReceiveEventArgs e)
{
string name = e.Stream.ToPipeStream().ReadLine();
Console.WriteLine(name);
e.Session.Stream.ToPipeStream().WriteLine("hello " + name);
e.Session.Stream.Flush();
base.SessionReceive(server, e);
}
}
構建TCP Client
class Program
{
static void Main(string[] args)
{
TcpClient client = SocketFactory.CreateClient<TcpClient>("127.0.0.1", 9090);
//ssl
//TcpClient client = SocketFactory.CreateSslClient<TcpClient>("127.0.0.1", 9090, "localhost");
while (true)
{
Console.Write("Enter Name:");
var line = Console.ReadLine();
client.Stream.ToPipeStream().WriteLine(line);
client.Stream.Flush();
var reader = client.Read();
line = reader.ToPipeStream().ReadLine();
Console.WriteLine(line);
}
Console.WriteLine("Hello World!");
}
}
異步Client
class Program
{
static void Main(string[] args)
{
AsyncTcpClient client = SocketFactory.CreateClient<AsyncTcpClient>("127.0.0.1", 9090);
//SSL
//AsyncTcpClient client = SocketFactory.CreateSslClient<AsyncTcpClient>("127.0.0.1", 9090, "serviceName");
client.ClientError = (o, e) =>
{
Console.WriteLine("client error {0}@{1}", e.Message, e.Error);
};
client.Receive = (o, e) =>
{
Console.WriteLine(e.Stream.ToPipeStream().ReadLine());
};
var pipestream = client.Stream.ToPipeStream();
pipestream.WriteLine("hello henry");
client.Stream.Flush();
Console.Read();
}
}
實現一個Protobuf對象解釋器
public class Packet : FixedHeaderPacket
{
public Packet()
{
TypeHeader = new TypeHandler();
}
private PacketDecodeCompletedEventArgs mCompletedEventArgs = new PacketDecodeCompletedEventArgs();
public void Register(params Assembly[] assemblies)
{
TypeHeader.Register(assemblies);
}
public IMessageTypeHeader TypeHeader { get; set; }
public override IPacket Clone()
{
Packet result = new Packet();
result.TypeHeader = TypeHeader;
return result;
}
protected override object OnReader(ISession session, PipeStream reader)
{
Type type = TypeHeader.ReadType(reader);
int bodySize = reader.ReadInt32();
return reader.Stream.Deserialize(bodySize, type);
}
protected override void OnWrite(ISession session, object data, PipeStream writer)
{
TypeHeader.WriteType(data, writer);
MemoryBlockCollection bodysize = writer.Allocate(4);
int bodyStartlegnth = (int)writer.CacheLength;
ProtoBuf.Meta.RuntimeTypeModel.Default.Serialize(writer.Stream, data);
bodysize.Full((int)writer.CacheLength - bodyStartlegnth);
}
}