高性能、易于使用的网络库在所有提供的拓扑上支持 16k+ 并发客户端。
专为 LAN 或 Internet 上的分布式实时并发应用程序而设计。
提供高吞吐量消息传递、P2P、Nat Traversal、Reliable Udp 的基础设施。
该存储库由主要核心组件和几个特定于序列化的子组件组成。
请查看 Wiki 页面以获取详细文档。
网络库,包括与网络系统相关的所有逻辑,从原始字节到 P2P 大厅等抽象。它提供了可与任何类型的序列化一起使用的通用模板。
即插即用高性能模型,使用原始字节。也用作更高级别模型的基础。
Tcp Server/Client model
,字节可以像常规套接字一样分段。Tcp Byte Message Server/Client
,其中字节以 4 字节长度标头发送。它确保原子消息传递而没有碎片。Udp Server/Client
udp 系统,其中模拟服务器并优化性能。Reliable Udp Client/Server
,其中现代 TCP 协议是通过 Udp 实现的。涉及可以与任何序列化协议一起使用的通用模型。
Generic Message Server/Client
设计用于自动发送和接收序列化消息。Generic MessageProtocol Server/client
与上面类似,但添加了“MessageEnvelope”载体类,用作标头/元数据。P2P Relay Client/Server
,Peers(客户端)通过中继服务器发现彼此,可以使用 Udp/Rudp/Tcp 进行通信。支持Tcp&Udp打孔。P2P Room/Lobby Server/Client
Relay 模型的扩展,其中同行可以定义房间,类似于游戏配对服务器。库已使用操作系统 (Windows) 支持的尽可能多的客户端进行了测试(大约 16k 动态端口)。包括 RUDP 在内的数据可靠性通过互联网进行了广泛测试。 Nat Traversal Udp 打洞技术也在互联网上进行了测试并取得了成功。
注意:Libary 具有不安全的代码和堆栈内存分配。不安全部分经过充分测试,不会发生更改。
主装配中的通用模型是使用特定的序列化器实现的。这种划分的原因是为了避免不必要的依赖关系。所有方法签名和用法都是相同的。它包括:
Nuget 包可用:
核心网络库 | 原始缓冲区 | 消息包 | 网络序列化器 | 杰森 |
---|---|---|---|---|
Infinite Echo 基准测试是通过向服务器发送一组消息并获取回显响应来完成的。每个响应都会引发新的请求。每个服务器响应计为 1 个 Echo。
每条 1000 条种子消息(32 字节消息 + 4 个标头):
AMD 锐龙 7 5800H 笔记本电脑
客户数量 | 每秒 TCP 回显 | SSL 每秒回显 |
---|---|---|
100 | 53,400,000 | 41,600,000 |
1000 | 43,600,000 | 22,200,000 |
5000 | 43,400,000 | 21,800,000 |
10000 | 42,800,000 | 21,700,000 |
英特尔 i9 13980HX 笔记本电脑
客户数量 | 每秒 TCP 回显 | SSL 每秒回显 |
---|---|---|
100 | 128,800,000 | 79,400,000 |
1000 | 110,400,000 | 72,100,000 |
5000 | 102,100,000 | 67,500,000 |
10000 | 100,500,000 | 65,500,000 |
1000 个种子消息信封(32 字节有效负载,总共 48 字节):
AMD 锐龙 7 5800H 笔记本电脑
客户数量 | Protobuf 每秒回波 | 每秒安全的 Protobuf 回显 |
---|---|---|
100 | 9,440,000 | 8,050,000 |
1000 | 8,780,000 | 7,480,000 |
5000 | 8,360,000 | 7,390,000 |
10000 | 8,340,000 | 7,350,000 |
英特尔 i9 13980HX 笔记本电脑
客户数量 | Protobuf 每秒回波 | 每秒安全的 Protobuf 回显 |
---|---|---|
100 | 31,200,000 | 20,650,000 |
1000 | 30,500,000 | 19,500,000 |
5000 | 28,200,000 | 17,650,000 |
10000 | 26,400,000 | 16,000,000 |
该基准测试仅发送具有原始字节有效负载的消息信封。序列化具体性能请参考:SerializationBenchmarks
有关详细信息,请查看 AsyncTcpClient/Server 和 ByteMessageTcpClient/Server
字节数组或数组段的任何块都将到达目的地而不会产生碎片。
private static void ExampleByteMessage ( )
{
ByteMessageTcpServer server = new ByteMessageTcpServer ( 20008 ) ;
server . OnBytesReceived += ServerBytesReceived ;
server . StartServer ( ) ;
ByteMessageTcpClient client = new ByteMessageTcpClient ( ) ;
client . OnBytesReceived += ClientBytesReceived ;
client . Connect ( "127.0.0.1" , 20008 ) ;
client . SendAsync ( Encoding . UTF8 . GetBytes ( "Hello I'm a client!" ) ) ;
void ServerBytesReceived ( Guid clientId , byte [ ] bytes , int offset , int count )
{
Console . WriteLine ( Encoding . UTF8 . GetString ( bytes , offset , count ) ) ;
server . SendBytesToClient ( clientId , Encoding . UTF8 . GetBytes ( "Hello I'm the server" ) ) ;
}
void ClientBytesReceived ( byte [ ] bytes , int offset , int count )
{
Console . WriteLine ( Encoding . UTF8 . GetString ( bytes , offset , count ) ) ;
}
}
output:
Hello I'm a client!
Hello I'm the server
注意:这里重要的性能设置是使用缓冲区还是队列作为缓冲策略。如果消息主要是 byte[] 区域,例如(缓冲区、偏移量、计数),则使用 Buffer。如果消息是完整字节[](0 到结束),则使用队列。
client . GatherConfig = ScatterGatherConfig . UseQueue ;
server . GatherConfig = ScatterGatherConfig . UseBuffer ;
对于 SSL 变体,差异是:
var ccert = new X509Certificate2 ( "client.pfx" , "greenpass" ) ;
// null certificate or default constructor will generate self signed certificate
client = new SslByteMessageClient ( ccert ) ;
var scert = new X509Certificate2 ( "server.pfx" , "greenpass" ) ;
// null certificate or default constructor will generate self signed certificate
server = new SslByteMessageServer ( 8888 , scert ) ;
// You can override the SSL cerificate validation callback
server . RemoteCertificateValidationCallback += .. .
client . RemoteCertificateValidationCallback += .. .
有关更多信息,请查看 SSLClient/Server 和 SSLByteMessageClient/Server
传输原始字节的基本服务器/客户端。方法和回调签名与字节消息模型相同。没有通过基本服务器/客户端实现的协议,因此字节可能会根据您的 MTU 大小而分段。
AsyncTcpServer server = new AsyncTcpServer ( port : 20000 ) ;
AsyncTpcClient client = new AsyncTpcClient ( ) ;
// SSL variant
// null certificate or default constructor will generate self signed certificate
var ccert = new X509Certificate2 ( "client.pfx" , "greenpass" ) ;
var scert = new X509Certificate2 ( "server.pfx" , "greenpass" ) ;
SslServer server = new SslServer ( 2000 , scert ) ;
SslClient client = new SslClient ( ccert ) ;
序列化网络是核心库提供的通用类的实现,它适用于所有序列化协议。
欲了解更多信息:序列化网络Examples here is only given for Protobuf-net, but signature is identical for any other provided serialization protocol(MessagePack, Json etc)
。
实现服务器客户端模型,其中序列化消息以原子方式传输。声明你的类型:
[ ProtoContract ]
class SampleMessage
{
[ ProtoMember ( 1 ) ]
public string sample ;
}
PureProtoServer server = new PureProtoServer ( 11234 ) ;
server . StartServer ( ) ;
server . BytesReceived += ( clientId , bytes , offset , count ) =>
{
SampleMessage msg = server . Serializer . Deserialize < SampleMessage > ( bytes , offset , count ) ;
Console . WriteLine ( msg . sample ) ;
msg . sample = "Jesse Lets cook" ;
server . SendAsync ( clientId , msg ) ;
} ;
PureProtoClient client = new PureProtoClient ( ) ;
client . Connect ( "127.0.0.1" , 11234 ) ;
client . BytesReceived += ( bytes , offset , count ) =>
{
SampleMessage msg = client . Serializer . Deserialize < SampleMessage > ( bytes , offset , count ) ;
Console . WriteLine ( msg . sample ) ;
} ;
client . SendAsync ( new SampleMessage ( ) { sample = "Yo! Mr White" } ) ;
消息协议的实现是为了使用标准标头包装所有动态消息类型。详细解释请参考消息协议。
您可以声明有效负载类型,即任何可使用 protobuf 序列化的类型。
[ ProtoContract ]
class SamplePayload : IProtoMessage
{
[ ProtoMember ( 1 ) ]
public string sample ;
}
安全变体示例:
private static async Task ExampleProtoSecure ( )
{
// null certificate or default constructor will generate self signed certificate
var scert = new X509Certificate2 ( "server.pfx" , "greenpass" ) ;
var cert = new X509Certificate2 ( "client.pfx" , "greenpass" ) ;
SecureProtoMessageServer server = new SecureProtoMessageServer ( 20008 , scert ) ;
server . StartServer ( ) ;
server . OnMessageReceived += ServerMessageReceived ;
var client = new SecureProtoMessageClient ( cert ) ;
client . OnMessageReceived += ClientMessageReceived ;
client . Connect ( "127.0.0.1" , 20008 ) ;
var Payload = new SamplePayload ( ) { sample = "Hello" } ;
var messageEnvelope = new MessageEnvelope ( ) ;
messageEnvelope . Header = "PayloadTest" ;
// You can just send a message, get replies on ClientMessageReceived.
client . SendAsyncMessage ( messageEnvelope ) ;
client . SendAsyncMessage ( messageEnvelope , Payload ) ;
// Or you can wait for a reply async.
MessageEnvelope result = await client . SendMessageAndWaitResponse ( messageEnvelope , Payload ) ;
var payload = result . UnpackPayload < SamplePayload > ( ) ;
Console . WriteLine ( $ "Client Got Response { payload . sample } " ) ;
void ServerMessageReceived ( Guid clientId , MessageEnvelope message )
{
Console . WriteLine ( $ "Server Received message { message . Header } " ) ;
server . SendAsyncMessage ( clientId , message ) ;
}
void ClientMessageReceived ( MessageEnvelope message )
{
}
}
ProtoMessageServer
和ProtoMessageClient
具有相同的签名,只是构造函数不接受证书这个模型是我个人在其他项目(例如 P2PVideocall 和多人星际战斗机游戏)中使用的。基本上,您的网络中的某处有一个中继服务器,它可以充当 LAN 中的本地网络集线器和/或在启用端口转发的情况下开放来自互联网的连接。
中继客户端(对等点)连接到中继服务器并获取有关其他对等点存在的通知。对等点可以通过中继服务器相互发送消息,也可以直接相互发送消息(Udp 打孔)。
查看P2P Fore详细信息
服务器是完全被动的,允许其他对等点发现并向对方发送消息。此外,还提供了 UDP 打洞等 NAT 穿越方法,以允许通过 Internet 或 LAN 进行直接通信(到目前为止仅限 UDP,但我们有可靠的 udp)。
Relay Server Is Serialization Agnostic
这意味着任何序列化网络对等体(Protobuff、MessagePack 等)都可以使用相同的中继服务器。
要使用中继服务器,只需将您的服务器声明为:
var scert = new X509Certificate2 ( "server.pfx" , "greenpass" ) ;
var server = new SecureProtoRelayServer ( 20010 , scert ) ;
server . StartServer ( ) ;
中继服务器已预先配置。
中继客户端是实现应用程序逻辑的地方。您可以通过 Web 客户端应用程序来发现彼此并进行交谈。
要取消客户端:
// null certificate or default constructor will generate self signed certificate
var cert = new X509Certificate2 ( "client.pfx" , "greenpass" ) ;
var client = new RelayClient ( cert ) ;
client . OnPeerRegistered += ( Guid peerId ) => ..
client . OnPeerUnregistered += ( Guid peerId ) => ..
client . OnMessageReceived += ( MessageEnvelope message ) => ..
client . OnUdpMessageReceived += ( MessageEnvelope message ) => ..
client . OnDisconnected += ( ) => ..
client . Connect ( "127.0.0.1" , 20010 ) ;
方法签名和回调与原始客户端/服务器模型相同(也带有有效负载)。唯一的区别是您必须指定目标对等 Guid Id。每当新的对等点连接到中继服务器时,它来自 OnPeerRegistered 事件。中继服务器保证当前对等点集的同步以及所有对等点之间的最终一致性。因此,新的对等点将接收来自此事件的所有其他连接的对等点,而旧的对等点将收到更新。
client . SendAsyncMessage ( destinationPeerId , new MessageEnvelope ( ) { Header = "Hello" } ) ;
client . SendUdpMesssage ( destinationPeerId , new MessageEnvelope ( ) { Header = "Hello" } ) ;
// Or with an async reply
MessageEnvelope response = await client . SendRequestAndWaitResponse ( destinationPeerId ,
new MessageEnvelope ( ) { Header = "Who Are You?" } ) ;
Udp 消息可以超过 65,527 字节的数据报限制。系统将大型 udp 消息检测为巨型消息并以块的形式发送它们。接收端将尝试重建消息。如果所有部分未在超时内到达,则消息将被丢弃。 udp 的最大消息大小为 16,256,000 字节。
可靠的 udp 协议使用通过 UDP 实现的 TCP 算法。
client . SendRudpMessage ( peerId , envelope ) ;
client . SendRudpMessage ( peerId , envelope , innerMessage ) ;
client . SendRudpMessageAndWaitResponse ( peerId , envelope , innerMessage ) ;
client . SendRudpMessageAndWaitResponse ( peerId , envelope ) ;
Nat 遍历/打孔支持:
// Udp
bool result = await client . RequestHolePunchAsync ( destinationPeerId , timeOut : 10000 ) ;
// Tcp
bool result = await client . RequestTcpHolePunchAsync ( destinationPeerId , timeOut : 10000 ) ;
如果成功,它将允许您在当前和目标对等点之间发送直接 udp 消息,以获取双向的其余 udp 消息。
这是中继服务器/客户端的扩展。添加的是房间系统,同伴可以在其中创建或加入房间、查询可用房间、向房间发送消息(多播)。另外,保持相同的消息系统在对等点之间发送 1-1 消息。
您可以加入多个房间
Room Server Is Serialization Agnostic
这意味着任何序列化网络对等点(Protobuf、MessagePack 等)都可以使用相同的 Room 服务器。
服务器和客户端的解密
var server = new SecureProtoRoomServer ( 20010 , scert ) ;
server . StartServer ( ) ;
var client1 = new SecureProtoRoomClient ( cert ) ;
简单地创建/加入和离开房间:
client1 . CreateOrJoinRoom ( "Kitchen" ) ;
client1 . LeaveRoom ( "Kitchen" ) ;
房间回调如下。仅当您位于同一房间时才会触发此回调。
client1 . OnPeerJoinedRoom += ( roomName , peerId ) => ..
client1 . OnPeerLeftRoom += ( roomName , peerId ) => ..
client1 . OnPeerDisconnected += ( peerId ) => ..
除了标准的 1-1 消息回调之外,我们还有房间消息回调。
client1 . OnTcpRoomMesssageReceived += ( roomName , message ) => ..
client1 . OnUdpRoomMesssageReceived += ( roomName , message ) => ..
client1 . OnTcpMessageReceived += ( message ) => ..
client1 . OnUdpMessageReceived += ( message ) => ..
所有安全 TCP 变体都采用 TLS 身份验证/验证来实现标准 SSL 套接字。欲了解更多信息,请查看:安全