using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace Server_Dashboard_Socket {
///
/// Generic Protocol class for Json and Xml serialization
///
/// JsonMessageProtocol or XmlMessageProtocol
public abstract class Protocol {
//Header size is always 4
private const int HeaderSize = 4;
///
/// Gets the message and checks with the header if the message is valid or not
/// important to defend against attacks with infinite long messages
///
/// A network stream
/// MessageType e.g. Json or Xml
public async Task ReceiveAsync(NetworkStream networkStream) {
//Gets the body length
int bodyLength = await ReadHeader(networkStream).ConfigureAwait(false);
//Validates the length
AssertValidMessageLength(bodyLength);
//Returns the body message type
return await ReadBody(networkStream, bodyLength).ConfigureAwait(false);
}
///
/// Sends data Async
///
/// Message type e.g. object or string
/// Network stream
/// The message
///
public async Task SendAsync(NetworkStream networkStream, T message) {
//encodes the message to a header and body
var (header, body) = Encode(message);
//Sends the header
await networkStream.WriteAsync(header, 0, header.Length).ConfigureAwait(false);
//Sends the body
await networkStream.WriteAsync(body, 0, body.Length).ConfigureAwait(false);
}
///
/// Reads the header and converts it to an integer
///
/// A network stream
/// Header as Integer
private async Task ReadHeader(NetworkStream networkStream) {
byte[] headerBytes = await ReadAsync(networkStream, HeaderSize).ConfigureAwait(false);
return IPAddress.HostToNetworkOrder(BitConverter.ToInt32(headerBytes));
}
///
/// Reads the body and decodes it to a human readable string
///
/// A network stream
/// Length of the body
/// Decoded body
private async Task ReadBody(NetworkStream networkStream, int bodyLength) {
//Reads the bytes from the stream into an array
byte[] bodyBytes = await ReadAsync(networkStream, bodyLength).ConfigureAwait(false);
//Decodes it and returns the string
return Decode(bodyBytes);
}
///
/// Reads the network stream as long as something is readable
///
/// A network stream
/// how many bytes there are to read
/// Every byte from the Stream
private async Task ReadAsync(NetworkStream networkStream, int bytesToRead) {
//new buffer that is as big as the content(watch out for buffer overflows)
byte[] buffer = new byte[bytesToRead];
//keep account of the bytes that are already read
int bytesRead = 0;
//White we still have something to read
while (bytesRead < bytesToRead) {
//Read it from the stream
var bytesReceived = await networkStream.ReadAsync(buffer, bytesRead, (bytesToRead - bytesRead)).ConfigureAwait(false);
//If it happens to be 0 close the socket
if (bytesReceived == 0)
throw new Exception("Socket Closed");
bytesRead += bytesReceived;
}
return buffer;
}
///
/// Encode the message from human readable to bytes for the stream
///
/// The message as anything e.g. object or string
/// The message to be send
/// The Header and Body as bytes[]
protected (byte[] header, byte[] body) Encode(T message) {
//Creates the body bytes
var bodyBytes = EncodeBody(message);
//Creates the header bytes
var headerBytes = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(bodyBytes.Length));
return (headerBytes, bodyBytes);
}
///
/// Decode the message with the given type, json or xml
///
/// The message to decode
/// Decoded message
protected abstract TMessageType Decode(byte[] message);
///
/// Validate the message length to combat attacks
///
/// The message length
protected virtual void AssertValidMessageLength(int messageLength) {
//If its not 0 throw an exception
if (messageLength < 1)
throw new ArgumentOutOfRangeException("Invalid message length");
}
///
/// Encode the message so it can be send via the network stream
///
/// Message type e.g. object or string
/// The message to be send
///
protected abstract byte[] EncodeBody(T message);
}
}