Skip to content

Instantly share code, notes, and snippets.

@bjorkstromm
Created May 10, 2017 21:10
Show Gist options
  • Save bjorkstromm/888aebcaeb275ffd8e73dcc92da345f7 to your computer and use it in GitHub Desktop.
Save bjorkstromm/888aebcaeb275ffd8e73dcc92da345f7 to your computer and use it in GitHub Desktop.
Simple BinaryReader/Writer serialization of object and sent over NetworkStream
using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
var port = 8888;
var cancellationTokenSource = new CancellationTokenSource();
var tasks = new[]
{
StartServer(port, cancellationTokenSource.Token),
StartClient(port, cancellationTokenSource.Token)
};
while (true)
{
if (Console.ReadKey().Key == ConsoleKey.Q)
{
cancellationTokenSource.Cancel();
break;
}
}
try
{
Task.WaitAll(tasks);
}
catch (Exception ae)
{
// noop
}
}
private static async Task StartServer(int port, CancellationToken cancellationToken)
{
var listener = new TcpListener(IPAddress.Loopback, port);
listener.Start();
Console.WriteLine("[SERVER] Waiting for a connection...");
using (var client = await listener.AcceptTcpClientAsync())
{
Console.WriteLine("[SERVER] Client Connected...");
var reader = new BinaryReader(client.GetStream());
var writer = new BinaryWriter(client.GetStream());
while (!cancellationToken.IsCancellationRequested)
{
// Request
var length = reader.ReadInt32();
var requestBytes = reader.ReadBytes(length);
var request = Deserialize<Request>(requestBytes);
Console.WriteLine($"[SERVER] Received {request.Bar} {request.Foo}...");
// Response
var response = new Response
{
Bar = unchecked((int) DateTime.Now.Ticks),
Boing = DateTime.Now.Ticks,
Baz = Guid.NewGuid().ToString()
};
Console.WriteLine($"[SERVER] Sending {response.Bar} {response.BarAsString} {response.Baz} {response.Boing}...");
var responseBytes = Serialize(response);
writer.Write(responseBytes.Length);
writer.Write(responseBytes);
writer.Flush();
}
}
listener.Stop();
}
private static async Task StartClient(int port, CancellationToken cancellationToken)
{
// Wait a second so the server is up
await Task.Delay(1000, cancellationToken);
using (var client = new TcpClient())
{
Console.WriteLine("[CLIENT] Connecting...");
await client.ConnectAsync(IPAddress.Loopback, port);
Console.WriteLine("[CLIENT] Connected...");
var reader = new BinaryReader(client.GetStream());
var writer = new BinaryWriter(client.GetStream());
// ReSharper disable once InconsistentNaming
var SendAndReceiveMessage = new Func<Request, Response>((request) =>
{
var requestBytes = Serialize(request);
writer.Write(requestBytes.Length);
writer.Write(requestBytes);
writer.Flush();
var length = reader.ReadInt32();
var responseBytes = reader.ReadBytes(length);
return Deserialize<Response>(responseBytes);
});
while (!cancellationToken.IsCancellationRequested)
{
var request = new Request
{
Bar = Guid.NewGuid().ToString(),
Foo = unchecked((int)DateTime.Now.Ticks)
};
Console.WriteLine($"[CLIENT] Sending {request.Bar} {request.Foo}...");
var response = SendAndReceiveMessage(request);
Console.WriteLine($"[CLIENT] Received {response.Bar} {response.BarAsString} {response.Baz} {response.Boing}...");
await Task.Delay(TimeSpan.FromSeconds(5), cancellationToken);
}
}
}
private static byte[] Serialize<T>(T obj) where T : class
{
var properties = obj.GetType()
.GetProperties().Where(p => p.IsDefined(typeof(DataMemberAttribute)))
.OrderBy(p => p.GetCustomAttribute<DataMemberAttribute>().Order);
using (var ms = new MemoryStream())
using (var writer = new BinaryWriter(ms))
{
foreach (var property in properties)
{
var order = property.GetCustomAttribute<DataMemberAttribute>().Order;
var type = property.PropertyType;
var value = property.GetValue(obj);
// Write order
writer.Write(order);
// Write value
if (type == typeof(Int32))
{
writer.Write((int)value);
}
else if(type == typeof(Double))
{
writer.Write((double)value);
}
else if (type == typeof(String))
{
writer.Write((string)value);
}
else
{
throw new SerializationException($"Invalid type {type.Name} for property {property.Name}");
}
}
writer.Flush();
return ms.ToArray();
}
}
private static T Deserialize<T>(byte[] bytes) where T : class
{
var obj = Activator.CreateInstance<T>();
var properties = typeof(T)
.GetProperties().Where(p => p.IsDefined(typeof(DataMemberAttribute)))
.OrderBy(p => p.GetCustomAttribute<DataMemberAttribute>().Order);
using (var ms = new MemoryStream(bytes))
using (var reader = new BinaryReader(ms))
{
foreach (var property in properties)
{
var order = property.GetCustomAttribute<DataMemberAttribute>().Order;
var type = property.PropertyType;
// Verify order
if (order != reader.ReadInt32())
{
throw new SerializationException();
}
// Write value
if (type == typeof(int))
{
property.SetValue(obj, reader.ReadInt32());
}
else if (type == typeof(double))
{
property.SetValue(obj, reader.ReadDouble());
}
else if (type == typeof(string))
{
property.SetValue(obj, reader.ReadString());
}
}
}
return obj;
}
[DataContract]
private class Request
{
[DataMember(Order = 0)]
public int Foo { get; set; }
[DataMember(Order = 1)]
public string Bar { get; set; }
}
[DataContract]
private class Response
{
[DataMember(Order = 0)]
public int Bar { get; set; }
[IgnoreDataMember]
public string BarAsString => Bar.ToString();
[DataMember(Order = 1)]
public string Baz { get; set; }
[DataMember(Order = 2)]
public double Boing { get; set; }
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment