Skip to content

Arcidev/Arci.Networking

Repository files navigation

Arci.Networking

Simple library for client-server network communication with as less dependencies as possible. Use packets to encapsulate your data which can be encrypted using either RSA or AES. For RSA you can choose to use padding scheme OAEP or not. For AES you can choose to generate a key of length 16, 24 or 32 bytes and use several padding modes like for example PKCS7. Samples included within solution.

Nuget

NuGet NuGet downloads

PM> Install-Package Arci.Networking

Build Status

Build status Build status Test status Codacy

Copyright

License

Documentation

Documentation

Usage

Basic usage of the library. You can find some basic example of a client and a server in the solution. For more advanced example you can check my other project.

Using serializer

Serializer allows to directly serialize/deserialize object into/from Packet object.

// Marks class as serializable to packet with 1 as packet identifier
[PacketClass(1)]
public class MyObject
{
    // Marks property to be serialized in specified order
    [PacketProperty(1)]
    public SByte SByte { get; set; }

    [PacketProperty(2)]
    public UInt16 UInt16 { get; set; }

    [PacketProperty(3)]
    public DateTime DateTime { get; set; }
}

var obj = new MyObject();
// Object will be serialized into packet
var packet = obj.ToPacket();
// Deserializes object back to object of type MyObject
obj = packet.FromPacket<MyObject>();

Without using serializer

You can choose not to use serializer and serialize/deserialize your objects manualy. Manual serialization is slightly faster (no requirements for reflection) and more flexible as the serializer does not support dynamic objects nor single types.

Writing byte based values

var buffer = new ByteBuffer()
buffer.Write(uint32Value);
buffer.Write(int16Value);
buffer.Write(byteValue);
buffer.Write(stringValue);
buffer.Write(byteArrayValue);
buffer.Write(dateTimeValue);

Writing bit based values

Network stream can only work with byte values therefore we need to inform buffer that he needs to write values into stream as a whole byte when we're finished with bit writes (this is only necessary when bits does not form byte already e.g. number_of_bits_written % 8 != 0)

var buffer = new ByteBuffer()
buffer.WriteBit(byteValue & 0x1);
buffer.WriteBit(byteValue & 0x10);
buffer.WriteBit(byteValue & 0x80);
buffer.WriteBit(true);
// Write bits into stream if they do not already form byte
buffer.FlushBits();

Writing values with specified number of bits

var buffer = new ByteBuffer();
// Same as buffer.Write((UInt16)value)
// Flush not required because 16 % 8 == 0
buffer.WriteBits(uint32value, 16);

buffer.WriteBits(uint32Value, 2);
buffer.WriteBits(uint32Value, 4);
// Flush required because we did 2 + 4 bit writes and 6 % 8 != 0
buffer.FlushBits();

PacketGuid

UInt64 value represented as 8 byte values. Only bytes that are not 0 will be written to stream. In byte stream before writing guid first you should write bit values to be able to determine which bytes are not 0.

var guid = new PacketGuid(uint64value);
// Order is up to you
buffer.WriteGuidBitStreamInOrder(guid, 0, 1, 2, 3, 4);
// Value can be added in between
buffer.WriteBit(false);
buffer.WriteGuidBitStreamInOrder(guid, 7, 6, 5);
// Flush required because we wrote 9 bits instead of 8
buffer.FlushBits();
buffer.WriteGuidByteStreamInOrder(guid, 0, 1, 2, 3, 4, 5, 6, 7);
// If you just want to save some space and do not care about the order
buffer.Write(guid);

Encapsulating with Packet

Extends ByteBuffer by adding an identifier to a stream

var packet = new Packet(identifier);
packet.Write(uint32Value);
packet.WriteBit(byteValue & 0x10);
packet.WriteBit(true);
packet.FlushBits();
packet.Write(byteBuffer);
packet.Write(stringValue);

Builder

Only usable for Packet class

var packet = new Packet(identifier).Builder()
.Write(uint32Value)
.WriteBit(byteValue & 0x10)
.WriteBit(true)
.FlushBits()
.Write(byteBuffer)
.Write(stringValue).Build();

Reading data

var packet = new Packet(byteStream);
var uint32value = packet.ReadUInt32();
var guid = new PacketGuid();
packet.ReadGuidBitStreamInOrder(guid, 0, 1, 2, 3, 4, 7, 6, 5);
packet.ReadGuidByteStreamInOrder(guid, 0, 1, 2, 3, 4, 5, 6, 7);
guid = packet.ReadGuid();
var uint64value = (UInt64)guid;
var boolean = packet.ReadBit();
// Reading bit values reads the whole byte into memory and bits are being read from there.
// Necessary if you wish to start bit reading from a new byte.
packet.ClearUnflushedBits();

Using Client

var client = await Client.CreateAsync("localhost", 10751);
// Send data to server
client.SendPacket(packet);
// Await collection of packets from server
var packets = await tcpClient.ReceiveDataAsync(false);

Using Server

var server = new Server(IPAddress.Parse("127.0.0.1"), 10751);
// Await new connection
var tcpClient = await server.AcceptClientAsync();
// Await collection of packets from this client
var packets = tcpClient.ReceiveData(false);
// Send data back to client
tcpClient.SendPacket(packet); 

Aes

var aes = new AesEncryptor() { PaddingMode = PaddingMode.PKCS7 };
var encryptedData = aes.Encrypt(packet.Data);
var decryptedData = aes.Decrypt(encryptedData);
// client can handle encryption if you set an encryptor
client.Encryptor = aes;

RSA

var rsa = new RsaEncryptor(RSAKey.RsaParams);
var rsaEncryptedValue = rsa.Encrypt(packet.Data);
var rsaDecryptedValue = rsa.Decrypt(rsaEncryptedValue);

Changes

See Wiki for a list of changes between versions

Supported frameworks

  • .NET Standard 2.0