Created
May 10, 2017 21:10
-
-
Save bjorkstromm/888aebcaeb275ffd8e73dcc92da345f7 to your computer and use it in GitHub Desktop.
Simple BinaryReader/Writer serialization of object and sent over NetworkStream
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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