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
PM> Install-Package Arci.Networking
Build Status
Copyright
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