Added a socket client

This commit is contained in:
Rene Schwarz
2021-08-07 01:01:17 +02:00
parent 58524a9514
commit f8f28984a5
62 changed files with 1320 additions and 174 deletions

View File

@@ -0,0 +1,53 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace Server_Dashboard_Socket.Protocol {
/// <summary>
/// Json serializer class
/// </summary>
public class JsonMessageProtocol : Protocol<JObject> {
//The Json serializer and the settings
static readonly JsonSerializer serializer;
static readonly JsonSerializerSettings settings;
/// <summary>
/// Settings for the Json Serializer
/// </summary>
static JsonMessageProtocol() {
//Set the settings
settings = new JsonSerializerSettings {
Formatting = Formatting.Indented,
DateTimeZoneHandling = DateTimeZoneHandling.Utc,
ContractResolver = new DefaultContractResolver {
NamingStrategy = new CamelCaseNamingStrategy {
ProcessDictionaryKeys = false
}
}
};
settings.PreserveReferencesHandling = PreserveReferencesHandling.None;
//Creates the serializer with the settings
serializer = JsonSerializer.Create(settings);
}
//Decode the message, to Json
protected override JObject Decode(byte[] message) => JObject.Parse(Encoding.UTF8.GetString(message));
/// <summary>
/// Encode the body from Json to bytes
/// </summary>
/// <typeparam name="T">The message type e.g. object or string</typeparam>
/// <param name="message">The message to send</param>
/// <returns>message as byte[]</returns>
protected override byte[] EncodeBody<T>(T message) {
var sb = new StringBuilder();
var sw = new StringWriter(sb);
serializer.Serialize(sw, message);
return Encoding.UTF8.GetBytes(sb.ToString());
}
}
}

View File

@@ -0,0 +1,132 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace Server_Dashboard_Socket {
/// <summary>
/// Generic Protocol class for Json and Xml serialization
/// </summary>
/// <typeparam name="TMessageType">JsonMessageProtocol or XmlMessageProtocol</typeparam>
public abstract class Protocol<TMessageType> {
//Header size is always 4
const int HEADER_SIZE = 4;
/// <summary>
/// Gets the message and checks with the header if the message is valid or not
/// important to defend against attacks with infinite long messages
/// </summary>
/// <param name="networkStream">A network stream</param>
/// <returns>MessageType e.g. Json or Xml</returns>
public async Task<TMessageType> 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);
}
/// <summary>
/// Sends data Async
/// </summary>
/// <typeparam name="T">Message type e.g. object or string</typeparam>
/// <param name="networkStream">Network stream</param>
/// <param name="message">The message</param>
/// <returns></returns>
public async Task SendAsync<T>(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);
}
/// <summary>
/// Reads the header and converts it to an integer
/// </summary>
/// <param name="networkStream">A network stream</param>
/// <returns>Header as Integer</returns>
async Task<int> ReadHeader(NetworkStream networkStream) {
byte[] headerBytes = await ReadAsync(networkStream, HEADER_SIZE).ConfigureAwait(false);
return IPAddress.HostToNetworkOrder(BitConverter.ToInt32(headerBytes));
}
/// <summary>
/// Reads the body and decodes it to a human readable string
/// </summary>
/// <param name="networkStream">A network stream</param>
/// <param name="bodyLength">Length of the body</param>
/// <returns>Decoded body</returns>
async Task<TMessageType> 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);
}
/// <summary>
/// Reads the network stream as long as something is readable
/// </summary>
/// <param name="networkStream">A network stream</param>
/// <param name="bytesToRead">how many bytes there are to read</param>
/// <returns>Every byte from the Stream</returns>
async Task<byte[]> 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 acount 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;
}
/// <summary>
/// Encode the message from human readable to bytes for the stream
/// </summary>
/// <typeparam name="T">The message as anything e.g. object or strng</typeparam>
/// <param name="message">The message to be send</param>
/// <returns>The Header and Body as bytes[]</returns>
protected (byte[] header, byte[] body) Encode<T>(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);
}
/// <summary>
/// Decode the message with the given type, json or xml
/// </summary>
/// <param name="message">The message to decode</param>
/// <returns>Decoded message</returns>
protected abstract TMessageType Decode(byte[] message);
/// <summary>
/// Validate the message length to combat attacks
/// </summary>
/// <param name="messageLength">The message length</param>
protected virtual void AssertValidMessageLength(int messageLength) {
//If its not 0 throw an exception
if (messageLength < 1)
throw new ArgumentOutOfRangeException("Invalid message length");
}
/// <summary>
/// Encode the message so it can be send via the network stream
/// </summary>
/// <typeparam name="T">Message type e.g. object or string</typeparam>
/// <param name="message">The message to be send</param>
/// <returns></returns>
protected abstract byte[] EncodeBody<T> (T message);
}
}

View File

@@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Serialization;
namespace Server_Dashboard_Socket {
/// <summary>
/// Xml serialize class
/// </summary>
public class XmlMessageProtocol : Protocol<XDocument> {
/// <summary>
/// Decodes the message from byte[] to XDocument
/// </summary>
/// <param name="message">The message to decode</param>
/// <returns>Message as XDocument</returns>
protected override XDocument Decode(byte[] message) {
//Reads the data as utf8 string
var xmlData = Encoding.UTF8.GetString(message);
//Creates a new reader
var xmlReader = XmlReader.Create(new StringReader(xmlData), new XmlReaderSettings { DtdProcessing = DtdProcessing.Ignore });
//Decodes the data to XDocument format
return XDocument.Load(xmlReader);
}
/// <summary>
/// Encode the XDocument to byte[]
/// </summary>
/// <typeparam name="T">Message type e.g. object or string</typeparam>
/// <param name="message">The message to encode</param>
/// <returns>Message as byte[]</returns>
protected override byte[] EncodeBody<T>(T message) {
//new string builder
StringBuilder sb = new StringBuilder();
//New string writer with the string builder
StringWriter sw = new StringWriter(sb);
//new xml serializer with the same type as the message
XmlSerializer xs = new XmlSerializer(typeof(T));
//Serialize the message to a regular string
xs.Serialize(sw, message);
//Return as UTF8 encoded byte array
return Encoding.UTF8.GetBytes(sb.ToString());
}
}
}