O Carambolas é uma biblioteca de suporte de uso geral em constante evolução, composto por múltiplos conjuntos .NET Standard 2.0 . Em particular, Possui uma implementação de protocolo UDP de múltiplos canais personalizados, destinada a baixas aplicações de rede de produtos de largura de banda com baixa latência/baixa largura de banda com restrições de tempo real em tempo real .
A primeira versão é um núcleo mínimo com um módulo de rede totalmente funcional. Este repositório é estruturado em torno de uma única solução porque planejo expandir adicionando novos módulos no futuro.
A cobertura do teste ainda é mínima, portanto, nenhum nível de correção deve estar implícito sem uma inspeção estreita do código -fonte. Este é um projeto em andamento em seus estágios iniciais.
Em breve, os binários estarão disponíveis na seção de arquivo ou na forma de pacotes Nuget.
O host local é representado por uma instância de carambolas.net.host . Ele deve ser usado para se conectar a um host remoto ou aceitar conexões de entrada.
Todo host remoto é representado por uma instância de carambolas.net.peer .
Eventos como conexão, desconexão e dados são recebidos através do objeto host, enquanto os objetos de pares podem ser usados para enviar dados ou desconectar ativamente.
Neste ponto, conectar, desconectar, enviar e receber operações não bloqueando; Aberto e perto estão bloqueando (por razões óbvias).
Observe que o mesmo objeto de host pode ser usado para solicitar ativamente conexões e aceitar conexões de entrada, o mesmo que o torna utilizável nas topologias P2P. As funções de cliente/servidor não são aplicadas e surgem simplesmente pela forma como um host está configurado. Um host pode até enviar solicitações de conexão para vários hosts remotos simultaneamente.
Exemplos :
Objeto do host instanciado para se conectar a um par remoto. O loop interno é responsável por garantir que os eventos não acumulem a próxima iteração.
using ( var host = new Host ( "MyHost" )
{
host . Open ( IPEndPoint . Any , new Host . Settings ( 0 ) ) ;
host . Connect ( new IPEndPoint ( IPAddress . Loopback , 1313 ) , out Peer peer ) ;
.. .
while ( true )
{
while ( host . TryGetEvent ( out Event e ) )
{
if ( e . EventType == EventType . Data )
Console . WriteLine ( $ "DATA: { e . Peer } { e . Data } " ) ;
else if ( e . EventType == EventType . Connection )
Console . WriteLine ( $ "CONNECTED: { e . Peer } " ) ;
else if ( e . EventType == EventType . Disconnection )
{
Console . WriteLine ( $ "DISCONNECTED: { e . Peer } { e . Reason } " ) ;
return ;
}
}
Thread . Sleep ( 33 ) ;
}
}Objeto de host instanciado para aguardar até 10 conexões recebidas. O loop interno é responsável por garantir que os eventos não acumulem a próxima iteração.
using ( var host = new Host ( "MyHost" )
{
host . Open ( new IPEndPoint ( IPAddress . Loopback , 1313 ) , new Host . Settings ( 10 ) ) ;
.. .
while ( true )
{
while ( host . TryGetEvent ( out Event e ) )
{
if ( e . EventType == EventType . Data )
Console . WriteLine ( $ "DATA: { e . Peer } { e . Data } " ) ;
else if ( e . EventType == EventType . Connection )
Console . WriteLine ( $ "CONNECTED: { e . Peer } " ) ;
else if ( e . EventType == EventType . Disconnection )
{
Console . WriteLine ( $ "DISCONNECTED: { e . Peer } { e . Reason } " ) ;
return ;
}
}
Thread . Sleep ( 33 ) ;
}
} Este projeto remonta a 2015, quando cheguei ao Canadá para estudar design e desenvolvimento de videogames na Toronto Film School. A motivação original era criar uma compilação de classes acessórias que pudessem ser reutilizadas em vários projetos do Unity3D. Depois de um tempo, comecei a pesquisar soluções de rede para um jogo multiplayer em potencial e o foco mudou para projetar um módulo de rede reutilizável. Inicialmente, abordei o problema como uma questão simples de integrar a UNET ou qualquer outra biblioteca de terceiros adequada que eu pudesse encontrar na época. Logo depois, comecei a encontrar todos os tipos de problemas, desde suposições quebradas até compensações ocultas de implementação. Não era incomum encontrar listas de recursos infladas (quase enganosas), projetar incompatibilidades ou implementações quebradas. Em particular, o que mais me incomodou foi que muitos aspectos das soluções pareciam aleatoriamente arbitrários, com pouca ou nenhuma explicação de por que essa abordagem era preferida ou um certo limite imposto. Eu passava horas inspecionando a fonte de um projeto fazendo anotações para descobrir por que algo era a maneira como era apenas para perceber mais tarde que outra parte do código estava em contradição direta.
Tudo isso me levou a mais trabalhos e, eventualmente, decidi construir uma biblioteca de rede leve com uma lista de recursos razoável que eu poderia implementar e verificar. Sem pressa, sem prazos. Apenas uma tentativa genuína de implementar a melhor solução técnica que eu poderia criar.
Enquanto isso, me formei, voltei a um emprego em período integral e tive que deixar esse projeto de lado. Há um ano, depois de encontrar algumas anotações antigas, restaurei meu arquivo de protótipos e decidi montar uma construção abrangente com todas as informações que reuni para que não apenas outras pessoas pudessem experimentar com ela, mas também entender a maneira como funcionou e por quê.
Os assemblies gerenciados podem ser incorporados em qualquer plataforma com um compilador que suporta C# 7.3 ou superior. Testes e aplicativos de acessórios requerem o NetCore 2.2.
As bibliotecas nativas podem ser construídas usando CMake com GCC ou Visual Studio.
Versões do sistema operacional suportadas:
Para qualquer outra plataforma, ou na ausência de uma biblioteca nativa necessária, existe o código de fallback que, embora em geral, possa ser menos eficiente, deve ser totalmente funcional e transparente.
Todos os projetos C# e scripts de construção são configurados para armazenar arquivos e binários intermediários em uma pasta de construção localizada na raiz do projeto, para que as construções possam ser facilmente inspecionadas, verificadas e limpas.
O código usa o DLLIMPORT para vincular bibliotecas nativas. O DLLIMPORT pode sempre usar nomes de bibliotecas do Windows e adicionará automaticamente os prefixos/sufixos de outras plataformas, conforme necessário. Por exemplo, carambolas.net.native.dll, o nome da biblioteca nativa da rede no Windows, torna -se libcarambolas.net.native.dll.so em linux e libcarambolas.net.native.dll.dynlib no macOS. Construir scripts já criam as bibliotecas sob os nomes adequados.
Uma solução do Visual Studio está incluída para conveniência, portanto, não devem ser necessárias etapas adicionais para o Windows. Certifique -se de selecionar apenas a plataforma correspondente ao seu sistema operacional host (x86 ou x64). Isso é necessário para criar os aplicativos de teste e para testes de unidade. Todos os conjuntos .NET são construídos para AnyCPU , independentemente da plataforma de solução selecionada, mas o Visual Studio deve saber quais bibliotecas nativas construirem para testes, pois espera-se que sejam implantados lado a lado com seus conjuntos associados.
Use o nugetpack.bat para compilar a biblioteca nativa e os conjuntos portáteis e criar pacotes Nuget, tudo em uma única ação.
Use Build.bat para criar todos os projetos para lançamento sem usar o Visual Studio.
O Visual Studio para Mac não foi testado e não é suportado; portanto, não espere que funcione.
Certifique -se de ter cmake (> = 2,8) e GCC para poder compilar a biblioteca nativa. O DOTNET CORE SDK 2.1 é necessário para compilar os conjuntos e gerar pacotes NUGET.
Use o nugetpack.sh para compilar a biblioteca nativa e os conjuntos portáteis e criar pacotes Nuget, tudo em uma única ação.
Use Build.sh para construir todos os projetos para lançamento sem usar o Visual Studio.
Certifique-se de ter cmake (> = 2,8), essencial de construção e GCC-Multilib instalados para poder compilar a biblioteca nativa para x86 e x64.
No Ubuntu Run: $ sudo apt-get Instale Build-Bequit-Essencial GCC-Multilib G ++-Multilib CMake
O DOTNET CORE SDK 2.1 é necessário para compilar conjuntos e gerar pacotes NUGET.
No Ubuntu Run:
$ wget https://packages.microsoft.com/config/ubuntu/20.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
$ sudo dpkg -i packages-microsoft-prod.deb
$ sudo apt-get update;
sudo apt-get install -y apt-transport-https &&
sudo apt-get update &&
sudo apt-get install -y dotnet-sdk-2.1
Use o nugetpack.sh para compilar a biblioteca nativa e os conjuntos portáteis e criar pacotes Nuget, tudo em uma única ação.
Use Build.sh para construir todos os projetos para lançamento sem usar o Visual Studio.
Um conjunto mínimo de testes de unidade é implementado em torno dos principais recursos usando projetos Xunit.
Carambolas.net.tests.host é um aplicativo de console simples usado para verificar manualmente a funcionalidade básica da rede. É particular útil quando lado com Wireshark e desajeitado
Carambolas.net.tests.integração é um conjunto de testes de integração para carambolas.net também implementados com o XUnit. Os testes são executados sequencialmente, cada um com dois threads separados (um servidor e um cliente), que se comunicam pela interface de loopback por uma quantidade específica de tempo. O loopback representa uma rede ideal em que o tempo de ida e volta é mínimo, os pacotes nunca chegam fora de ordem e nunca são perdidos, a menos que haja um transbordamento de bufffer. Essas características são úteis para validar os caminhos normais de execução.
O Wireshark é uma ferramenta de depuração inestimável que pode ser usada para monitorar a atividade da rede e inspecionar pacotes. Além disso, o Wireshark suporta uma classe especial de plugins chamados dissectores que podem ser usados para analisar protocolos personalizados.
Este projeto inclui um dissector básico de Wireshark para Carambolas. Para usá -lo, verifique se o Wireshark já está instalado.
Clumsy é um programa de captura de pacotes de rede que é executado no modo de usuário e é capaz de interceptar pacotes para simular condições de rede degradadas em tempo real.
Adicione uma linha de filtro predefinida como a seguinte no arquivo config.txt para afetar os hosts conectados pela interface loopback na mesma porta usada nos testes de integração (1313):
carambolas: udp and outbound and loopback and (udp.DstPort == 1313 or udp.SrcPort == 1313)
Observe que existem algumas advertências ao usar desajeitadas com a interface loopback. Do manual do usuário desajeitado:
- Os pacotes de entrada de loopback não podem ser capturados ou reinjetados. Quando você pensa sobre isso, é realmente difícil dizer que é um pacote de entrada ou saída quando você está enviando pacotes do computador para si. De fato, a plataforma de filtragem do Windows subjacente parece classificar todos os pacotes de loopback como saída. A coisa a lembrar é que, quando você está processando em pacotes de loopback, não pode ter "entrada" no seu filtro. É importante saber que seu computador pode ter IPs além de 127.0.0.1, como um IP da intranet alocado pelo seu roteador. Estes também são considerados pacotes de loopback.
- Os pacotes de loopback são capturados duas vezes. Como não temos pacotes de loopback de entrada, todos os pacotes de loopback são considerados como saída. Então, desajeitado, processará -os duas vezes: a primeira vez é o envio e a segunda vez ao receber. Um exemplo simples é que, quando o filtro é simplesmente "saindo" e aplique um atraso de 500ms. Quando você pingia o host, seria um atraso de 1000ms. Você pode contornar isso especificar a porta de destino e coisas assim. Mas seria mais fácil ter isso em mente e ter cuidado ao definir os parâmetros.
- A captura de pacotes de entrada não está funcionando o tempo todo. Como observado anteriormente, os pacotes de entrada de loopback não podem ser reinjetados. O problema é que, em ocasiões, alguns pacotes podem ser classificados como pacotes de entrada, mesmo que o IP de destino não seja do seu computador. Isso afeta apenas pacotes não loopback. Se você está trabalhando apenas na localhost, vai ficar bem. O objetivo do lançamento futuro é diagnosticar o que causou isso e fornecer uma solução.
- Não é possível filtrar com base na captura de redes do sistema de processos em ampla rede está listada como um recurso. Mas, na verdade, isso é, pois não há maneira fácil de fornecer uma solução robusta.
Estou sempre aberto a contribuições, na forma de relatórios de bugs, correções de bugs (ainda melhores!) Ou cobertura de teste aprimorada.
As solicitações de recursos são bem -vindas, mas podem ser mantidas em um backlog, dependendo de quão extenso, viável ou desejável são. Se uma solicitação de recurso for muito complexa, pode depender do patrocínio, pois tenho recursos limitados (tempo e dinheiro) para dedicar.
Se você gostaria de apoiar este projeto, posso estar interessado em ouvir de você, então entre em contato!
Em português, Carambolas é a forma plural de Carambola (= starfruit). O termo também é usado coloquialmente em certas regiões do Brasil para expressar espanto ou impaciência.
Antes de Carambolas, fiz pelo menos meia dúzia de tentativas de organizar minhas idéias em um projeto utilizável. Com o Carambolas, decidi construir uma série de protótipos para aprender sobre problemas de design e testar diferentes abordagens. Cada protótipo tinha um nome de código formado por uma carta e um número a partir de A1. O código -fonte que foi inicialmente importado neste repositório foi a 75ª iteração do 9º protótipo, daí A9.
As bibliotecas nativas são fornecidas principalmente por razões de desempenho, portanto, elas são totalmente opcionais. Teria sido irracional tentar fornecer uma implementação nativa para todas as plataformas possíveis (pense em todos os desktops, móveis, console, incorporado ...) e confiar exclusivamente em bibliotecas nativas reduziria as plataformas de destino para apenas um punhado, possivelmente apenas para desktop (Windows, Linux e MacOS). Portanto, como regra geral, sempre deve haver uma implementação de fallback no código gerenciado para qualquer funcionalidade implementada por uma biblioteca nativa.
Como as bibliotecas nativas são opcionais, o programa não sabe dizer se um arquivo ausente deveria estar lá ou não, por isso não há erro ou registrado para uma biblioteca nativa ausente. Por definição, uma biblioteca nativa ausente nunca é um erro.
Em geral, você não pode. E você não deveria, pelo menos não da perspectiva da API. Não deve importar para o usuário (ou programador de aplicativos) qual a estratégia de implementação subjacente é empregada por uma dependência, neste caso Carambolas. No entanto, essas informações podem ser relevantes para a implantação, portanto, toda vez que um objeto de interop é criado que também possui um fallback automático, o código produz uma informação indicativa de log. Por exemplo, o carambolas.net.socket produzirá uma informação de log semelhante a "usando carambolas.net.sockets.native.socket" quando uma biblioteca nativa é encontrada para a implementação do soquete subjacente. Dessa forma, se você estiver implantando com bibliotecas nativas em mente, pode determinar se eles estão realmente sendo usados.
Isso significa que você está implantando uma biblioteca nativa que é corrupta ou compilada para a arquitetura errada da CPU.
As bibliotecas nativas devem ir lado a lado com seus conjuntos de interop correspondentes e, embora os conjuntos possam ser compilados uma vez para qualquer arquitetura da CPU, as bibliotecas nativas não podem. Eles devem corresponder à arquitetura da CPU do sistema operacional em execução, caso contrário, são tratados como arquivos corruptos e .Net lança um sistema.badImageFormatexception. Observe que isso não é o mesmo que tentar carregar uma biblioteca que não é encontrada, que, por definição, não é um erro.
O produto de largura de banda (BDP) é o produto da capacidade de transmissão de um link de rede (em bits por segundo) e seu tempo de atraso de ida e volta (em segundos). Representa a quantidade máxima de dados que uma rede pode reter antes que qualquer reconhecimento possa chegar.
O BDP pode ser usado para classificar redes de acordo com se está acima ou abaixo de um determinado limite. As redes com um grande BDP são chamadas de redes de gordura longa (LFNs). Os LFNs podem ser redes com um tempo médio de ida e volta muito grande (sem regadre de largura de banda, como nos links de satélite) ou uma ampla rede (alta largura de banda) que exibe tempos de ida e volta consideravelmente pequenos (como nos links Gigabit Ethernet).
Verifique a Wikipedia para obter mais informações sobre isso.
Um objeto carambolas.net.socket serve como uma fachada para uma implementação de soquete nativo ou uma implementação de fallback que se baseia no System.net.sockets.socket. Ajuda a dissociar e reduzir a complexidade dos objetos de host e colegas. Consulte o doc/readme-carambolas.net para obter mais informações.
System.net.ipaddress e system.net.ipendpoint são objetos mutáveis que promovem uma série de alocações desconhecidas em todas as implementações atuais do núcleo .NET e a estrutura .NET. Carambolas.net.ipaddress e carambolas.net.ipendpoint são tipos de valor imutáveis que contribuem para reduzir a pressão do GC. Consulte o doc/readme-carambolas.net para obter mais informações.
AEAD com Chacha20 e Poly1305 é suportado para fora da caixa. As estratégias personalizadas podem ser implementadas fornecendo ao host implementações de carambolas.net.icipher e carambolas.net.iciPherFactory Interfaces. Os únicos requisitos são:
Um aplicativo de usuário é gratuito para comprimir seus dados antes de enviar, mas atualmente não há mecanismo para fornecer compactação/descompressão automática de mensagens individuais ou pacotes completos.
Todo o código -fonte e quaisquer binários produzidos para serem implantados ao lado de um aplicativo de usuário são licenciados sob uma licença do MIT.
Carambolas.CommandLinearguments foi baseado em um artigo de Griffonrl com código -fonte publicado sob uma licença do MIT com idéias adicionais de outro artigo de Jake Ginnivan que se expandiu na fonte original.
Carambolas.security.criptography.crc32c foi baseado no CRC32.NET por força sob uma licença do MIT.
Carambolas.Security.criptography.nacl foi baseado e expandido no NACL.CORE por David de Smet sob uma licença do MIT.
O dissetor do protocolo escrito em Lua para o Wireshark está disponível sob uma licença GPLV3. Ele só deve ser usado como um arquivo de entrada para o Wireshark, a fim de estender seus recursos e permitir que ele exiba mais informações sobre os pacotes UDP formatados de acordo com o Protocolo de rede Carambolas. Portanto, é completamente separado e não interage, depende ou contribui de forma alguma para arquivos de origem, montagens ou bibliotecas nativas.