Biblioteca de rede de alto desempenho e fácil de usar, com suporte para mais de 16 mil clientes simultâneos em todas as topologias fornecidas.
Projetado para aplicações simultâneas distribuídas em tempo real através de LAN ou Internet.
Fornece infraestrutura para passagem de mensagens de alto rendimento, P2P, Nat Traversal, Udp confiável.
Este repositório consiste em um conjunto principal principal e vários subconjuntos específicos de serialização.
Por favor, verifique a página Wiki para documentação detalhada.
Biblioteca de Rede, Inclui toda a lógica associada aos sistemas de rede, desde bytes brutos até abstrações como lobbies P2P. Ele fornece modelos genéricos para serem usados com qualquer tipo de serialização.
Modelos Plug&Play de alto desempenho, trabalhando com bytes brutos. Também utilizado como base para modelos de nível superior.
Tcp Server/Client model
com buffer dinâmico e subsistemas de enfileiramento, os bytes podem vir fragmentados como soquetes normais.Tcp Byte Message Server/Client
onde os bytes são enviados com cabeçalho de 4 bytes. Ele garante a entrega atômica de mensagens sem fragmentação.Udp Server/Client
Sistema udp onde um servidor é emulado com desempenho otimizado.Reliable Udp Client/Server
onde o protocolo TCP moderno é implementado sobre Udp.Envolve modelos genéricos que podem funcionar com qualquer protocolo de serialização.
Generic Message Server/Client
projetado para enviar e receber mensagens serializadas atomicamente.Generic MessageProtocol Server/client
semelhante ao acima, mas com adição da classe de operadora "MessageEnvelope", usada como cabeçalho/metadados.P2P Relay Client/Server
onde os pares (clientes) se descobrem por meio do servidor de retransmissão, podem usar Udp/Rudp/Tcp para se comunicar. Suporta TCP e Udp Holepunch.P2P Room/Lobby Server/Client
do modelo Relay onde os pares podem definir uma sala, semelhante aos servidores de matchmaking de jogos.A biblioteca é testada com tantos clientes quanto o sistema operacional (Windows) suporta (cerca de 16 mil portas dinâmicas). A confiabilidade dos dados, incluindo RUDP, é testada extensivamente na Internet. O holepunching Nat Traversal Udp também foi testado com sucesso na Internet.
Nota: Libary possui código inseguro e alocações de memória de pilha. As seções inseguras são bem testadas e não estão sujeitas a alterações.
Modelos genéricos da montagem principal são implementados com o serializador específico. A razão para esta divisão é evitar dependências desnecessárias. Todas as assinaturas e usos de métodos são idênticos. Inclui:
Pacotes Nuget estão disponíveis:
Biblioteca de rede principal | Protobuf | Pacote de mensagens | NetSeralizer | JSON |
---|---|---|---|---|
Os benchmarks de eco infinito são feitos enviando um conjunto de mensagens ao servidor e obtendo resposta de eco. Cada resposta causa uma nova solicitação. Cada resposta do servidor é contada como 1 eco.
1000 mensagens iniciais (mensagem de 32 bytes + 4 cabeçalhos) cada:
Portátil AMD Ryzen 7 5800H
Número de clientes | Eco TCP por segundo | Eco SSL por segundo |
---|---|---|
100 | 53.400.000 | 41.600.000 |
1000 | 43.600.000 | 22.200.000 |
5.000 | 43.400.000 | 21.800.000 |
10.000 | 42.800.000 | 21.700.000 |
Notebook Intel i9 13980HX
Número de clientes | Eco TCP por segundo | Eco SSL por segundo |
---|---|---|
100 | 128.800.000 | 79.400.000 |
1000 | 110.400.000 | 72.100.000 |
5.000 | 102.100.000 | 67.500.000 |
10.000 | 100.500.000 | 65.500.000 |
1.000 envelopes de mensagens iniciais (carga útil de 32 bytes, total de 48 bytes):
Portátil AMD Ryzen 7 5800H
Número de clientes | Eco do protobuf por segundo | Eco Protobuf Seguro por Segundo |
---|---|---|
100 | 9.440.000 | 8.050.000 |
1000 | 8.780.000 | 7.480.000 |
5.000 | 8.360.000 | 7.390.000 |
10.000 | 8.340.000 | 7.350.000 |
Notebook Intel i9 13980HX
Número de clientes | Eco do protobuf por segundo | Eco Protobuf Seguro por Segundo |
---|---|---|
100 | 31.200.000 | 20.650.000 |
1000 | 30.500.000 | 19.500.000 |
5.000 | 28.200.000 | 17.650.000 |
10.000 | 26.400.000 | 16.000.000 |
Este benchmark envia apenas envelope de mensagem com carga útil de bytes brutos. Para desempenho específico de serialização, consulte: SerializationBenchmarks
Para informações detalhadas, verifique AsyncTcpClient/Server e ByteMessageTcpClient/Server
Qualquer pedaço da matriz de bytes ou segmento da matriz chegará ao destino sem fragmentação.
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
Nota: A configuração de desempenho importante aqui é usar um buffer ou uma fila como política de buffer. Use Buffer se as mensagens forem principalmente regiões de byte[] como (buffer, offset,count). Use Queue se as mensagens tiverem byte completo[] (0 ao final).
client . GatherConfig = ScatterGatherConfig . UseQueue ;
server . GatherConfig = ScatterGatherConfig . UseBuffer ;
Para variantes SSL, a diferença é:
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 += .. .
Para obter mais informações, consulte SSLClient/Server e SSLByteMessageClient/Server
Servidor/Cliente Base onde os bytes brutos são transferidos. As assinaturas de método e retorno de chamada são idênticas aos modelos de mensagem de byte. Não há protocolo implementado no Servidor/Cliente base, portanto, os bytes podem vir fragmentados dependendo do tamanho do seu 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 ) ;
Redes serializadas são implementações de classes genéricas fornecidas pela Core Library. É aplicável a todos os protocolos de serialização.
Para mais informações: Rede serializada
Examples here is only given for Protobuf-net, but signature is identical for any other provided serialization protocol(MessagePack, Json etc)
.
Implementa um modelo de cliente servidor onde mensagens serializadas são transferidas atomicamente. Declare seu tipo:
[ 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" } ) ;
O protocolo de mensagens é implementado para agrupar todos os tipos de mensagens dinâmicas com um cabeçalho padrão. Consulte o Protocolo de Mensagens para obter explicações detalhadas.
Você pode declarar seus tipos de carga útil, qualquer tipo serializável com protobuf.
[ ProtoContract ]
class SamplePayload : IProtoMessage
{
[ ProtoMember ( 1 ) ]
public string sample ;
}
Exemplo para a variante segura:
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
e ProtoMessageClient
possuem assinaturas idênticas, exceto que os construtores não recebem um certificado Este modelo é o que eu pessoalmente uso em meus outros projetos, como P2PVideocall e Multiplayer Starfighter Game. Basicamente, você tem um servidor Relay em algum lugar da sua rede, que pode atuar como um hub de rede local na LAN e/ou aberto a conexões da Internet se o encaminhamento de porta estiver ativado.
Os clientes de retransmissão (Peers) conectam-se ao servidor de retransmissão e recebem notificações sobre a existência de outros pares. Os pares podem enviar mensagens entre si por meio do Relay Server ou diretamente entre si (Udp holepunch).
Confira informações detalhadas do P2P Fore
O servidor é completamente passivo, permitindo que outros pares descubram e enviem mensagens entre si. Além disso, métodos de passagem NAT, como holepunching UDP, são fornecidos para permitir a comunicação direta via Internet ou LAN (UDP apenas até o momento, mas temos um udp confiável).
Relay Server Is Serialization Agnostic
o que significa que qualquer peer de rede serializado (Protobuff, MessagePack etc.) pode usar o mesmo servidor de retransmissão.
Para usar o servidor Relay, simplesmente declere seu servidor como:
var scert = new X509Certificate2 ( "server.pfx" , "greenpass" ) ;
var server = new SecureProtoRelayServer ( 20010 , scert ) ;
server . StartServer ( ) ;
O servidor de retransmissão já está pré-configurado.
O cliente de retransmissão é onde a lógica do seu aplicativo é implementada. Você pode usar seus aplicativos clientes na Web para descobrir e conversar entre si.
Para cancelar um cliente:
// 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 ) ;
Assinaturas de método e retornos de chamada são idênticos ao modelo proto cliente/servidor (também com Payloads). A única diferença é que você precisa especificar o ID do Guid do peer de destino. Ele vem do evento OnPeerRegistered, sempre que um novo peer é conectado ao servidor de retransmissão. O Relay Server garante a sincronização do conjunto de pares atual com eventual consistência entre todos os pares. Assim, os novos peers receberão todos os outros peers conectados deste evento e os peers antigos receberão uma atualização.
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?" } ) ;
As mensagens UDP podem ultrapassar o limite do datagrama de 65.527 bytes. O sistema detecta mensagens udp grandes como mensagens Jumbo e as envia em pedaços. Receber end with tentará reconstruir a mensagem. se todas as peças não chegarem dentro de um tempo limite, a mensagem será descartada. O tamanho máximo da mensagem para UDP é 16.256.000 bytes.
O protocolo UDP confiável é usado como algoritmo TCP implementado sobre UDP.
client . SendRudpMessage ( peerId , envelope ) ;
client . SendRudpMessage ( peerId , envelope , innerMessage ) ;
client . SendRudpMessageAndWaitResponse ( peerId , envelope , innerMessage ) ;
client . SendRudpMessageAndWaitResponse ( peerId , envelope ) ;
Suporte Nat Traversal/Holepunch:
// Udp
bool result = await client . RequestHolePunchAsync ( destinationPeerId , timeOut : 10000 ) ;
// Tcp
bool result = await client . RequestTcpHolePunchAsync ( destinationPeerId , timeOut : 10000 ) ;
se for bem-sucedido, permitirá que você envie mensagens udp diretas entre os pares atuais e de destino para o restante das mensagens udp em ambas as direções.
Esta é uma extensão do Relay Server/Client. A adição é o sistema de salas onde os pares podem criar ou ingressar em salas, consultar salas disponíveis, enviar mensagens para salas (multicast). Além disso, mantendo o mesmo sistema de mensagens para enviar mensagens 1-1 entre pares.
Você pode ingressar em várias salas
Room Server Is Serialization Agnostic
, o que significa que qualquer peer de rede serializado (Protobuf, MessagePack etc.) pode usar o mesmo servidor Room.
Declaração de servidor e cliente
var server = new SecureProtoRoomServer ( 20010 , scert ) ;
server . StartServer ( ) ;
var client1 = new SecureProtoRoomClient ( cert ) ;
Para criar/entrar e sair de salas simplesmente:
client1 . CreateOrJoinRoom ( "Kitchen" ) ;
client1 . LeaveRoom ( "Kitchen" ) ;
Os retornos de chamada da sala são os seguintes. Esses retornos de chamada só são acionados se você estiver na mesma sala.
client1 . OnPeerJoinedRoom += ( roomName , peerId ) => ..
client1 . OnPeerLeftRoom += ( roomName , peerId ) => ..
client1 . OnPeerDisconnected += ( peerId ) => ..
Além do retorno de chamada de mensagem 1-1 padrão, temos retornos de chamada de mensagem de sala.
client1 . OnTcpRoomMesssageReceived += ( roomName , message ) => ..
client1 . OnUdpRoomMesssageReceived += ( roomName , message ) => ..
client1 . OnTcpMessageReceived += ( message ) => ..
client1 . OnUdpMessageReceived += ( message ) => ..
Todas as variantes TCP seguras estão implementando soquete SSL padrão com autenticação/validação TLS. Para mais informações confira: Segurança