Calling .Net Core code from .Net Framework 4.0

I had a need to call some modern code from an old .net framework 4.0 application.
What I came up with is communicating with a console application using named pipes and a JSON based protocol.

Server Code:

using System.IO.Pipes;

namespace core_connector
{
    internal class Program
    {
        static int Main(string[] args)
        {
            string pipename = "testpipe";

            if (args.Length > 0)
            {
                pipename = args[0];
            }

            using NamedPipeServerStream pipeServer =
                new NamedPipeServerStream(pipename, PipeDirection.InOut, 1);

            int threadId = Thread.CurrentThread.ManagedThreadId;

            Console.WriteLine("Server waiting for client connection on pipe '{0}'", pipename);
            pipeServer.WaitForConnection();

            Console.WriteLine("Client connected on thread[{0}].", threadId);
            try
            {
                var ss = new PipeProtocol(pipeServer);
                ss.WriteObject(new HelloResponse($"Welcome to #{pipename}"));

                bool QuitRequest = false;
                while (pipeServer.IsConnected && !QuitRequest)
                {
                    Console.WriteLine("Waiting for client...");
                    var response = ss.ReadPayload();

                    switch (response.payloadtype)
                    {
                        case MessageTypes.Greet:
                            Thread.Sleep(3 * 1000);
                            ss.WriteObject(new GreetResponse("Well Met Client"));
                            break;
                        case MessageTypes.SumTheseNums:
                            var req = response.ReadAs<SumTheseNumsRequest>();
                            var result = req.Number1 + req.Number2;
                            Thread.Sleep(2 * 1000);
                            ss.WriteObject(new SumTheseNumsResponse(result));
                            break;
                        case MessageTypes.Quit:
                            Console.WriteLine($"Client Quit");
                            QuitRequest = true;
                            break;
                        default:
                            break;
                    }

                }

                return 0;
            }
            catch (IOException e)
            {
                // Catch the IOException that is raised if the pipe is broken
                // or disconnected.
                Console.WriteLine("ERROR: {0}", e.Message);
                return -1;
            }
            finally
            {
                pipeServer.Close();
            }

        }
    }
}

Protocol, that is shared between both.

using Newtonsoft.Json;
using System;
using System.IO;
using System.Text;

namespace core_connector
{
    public class PipeProtocol
    {
        private Stream ioStream;
        private Encoding streamEncoding;
        private BinaryWriter binwriter;
        private BinaryReader binreader;

        public PipeProtocol(Stream ioStream)
        {
            this.ioStream = ioStream;
            this.streamEncoding = UnicodeEncoding.Unicode;
            this.binwriter = new BinaryWriter(ioStream, streamEncoding);
            this.binreader = new BinaryReader(ioStream, streamEncoding);
        }

        private void WritePayload(MessageTypes payloadtype, byte[] outBuffer)
        {
            this.binwriter.Write((int)payloadtype);
            this.binwriter.Write(outBuffer.Length);
            this.binwriter.Write(outBuffer);
            this.binwriter.Flush();
        }

        public Payload ReadPayload()
        {
            var p = new Payload(this.streamEncoding);
            p.payloadtype = (MessageTypes)this.binreader.ReadInt32();
            var len = this.binreader.ReadInt32();
            p.payload = this.binreader.ReadBytes(len);
            return p;
        }

        public class Payload
        {
            private Encoding streamEncoding;
            public MessageTypes payloadtype;
            public byte[] payload;

            public Payload(Encoding enc)
            {
                this.streamEncoding = enc;
            }

            public T ReadAs<T>()
            {
                var json = streamEncoding.GetString(this.payload);
                var ob = JsonConvert.DeserializeObject<T>(json);
                return ob;
            }
        }

        public void WriteObject<T>(T ob) where T : MessageBase
        {
            var json = JsonConvert.SerializeObject(ob);
            byte[] outBuffer = streamEncoding.GetBytes(json);
            this.WritePayload(ob.MessageType, outBuffer);
        }

        public TReply RequestReply<TReq, TReply>(TReq ob)
            where TReq : RequestBase
            where TReply : ResponseBase
        {

            this.WriteObject(ob);
            var payload = this.ReadPayload();

            if (payload.payloadtype != ob.MessageType)
            {
                throw new Exception(string.Format("Unexpected Reply - expected {0} received {1}", ob.MessageType, payload.payloadtype));
            }

            var resp = payload.ReadAs<TReply>();
            return resp;
        }

    } //PipeProtocol

    public enum MessageTypes
    {
        Hello,
        Greet,
        SumTheseNums,
        Quit
    }

    public class MessageBase
    {
        public MessageTypes MessageType { get; set; }

    }

    public class RequestBase : MessageBase
    {
    }

    public class ResponseBase : MessageBase
    {
        public bool Success { get; set; } = true;
        public string ErrorMsg { get; set; }
    }

    public class GreetRequest : RequestBase
    {
        public GreetRequest()
        {
            this.MessageType = MessageTypes.Greet;
        }
    }

    public class GreetResponse : ResponseBase
    {
        public string GreetingMessage { get; set; }
        public GreetResponse(string msg)
        {
            this.MessageType = MessageTypes.Greet;
            this.GreetingMessage = msg;
        }
    }


    public class HelloResponse : ResponseBase
    {
        public string HelloMessage { get; set; }

        public HelloResponse(string msg)
        {
            this.MessageType = MessageTypes.Hello;
            this.HelloMessage = msg;
        }
    }

    public class QuitRequest : RequestBase
    {
        public QuitRequest()
        {
            this.MessageType = MessageTypes.Quit;
        }
    }

    public class SumTheseNumsRequest : RequestBase
    {
        public int Number1 { get; set; }
        public int Number2 { get; set; }
        public SumTheseNumsRequest(int Num1,int Num2)
        {
            this.MessageType = MessageTypes.SumTheseNums;
            this.Number1 = Num1;
            this.Number2 = Num2;
        }
    }

    public class SumTheseNumsResponse : ResponseBase
    {
        public int SumResult { get; set; }
        public SumTheseNumsResponse(int result)
        {
            this.MessageType = MessageTypes.SumTheseNums;
            this.SumResult = result;
        }
    }
}

Client code (old .net framework) that starts the new code process and communicates with it.

using core_connector;
using System;
using System.Diagnostics;
using System.IO;
using System.IO.Pipes;
using System.Security.Principal;
using System.Threading;

namespace core_connector_client
{
    internal class Program
    {
        static Process StartServer(string pipename)
        {
            var fn = @"C:\app\core_connector.exe";

            Process myProcess = new Process();

            bool Debug = false;
            if (Debug)
            {
                myProcess.StartInfo.UseShellExecute = true;
                myProcess.StartInfo.CreateNoWindow = false;
            }
            else
            {
                myProcess.StartInfo.UseShellExecute = false;
                myProcess.StartInfo.CreateNoWindow = true;
                myProcess.StartInfo.RedirectStandardOutput = true;
                myProcess.StartInfo.RedirectStandardInput = true;
                myProcess.StartInfo.RedirectStandardError = true;
            }
            myProcess.StartInfo.FileName = fn;
            myProcess.StartInfo.Arguments = pipename;
            myProcess.Start();

            return myProcess;

        }
        static void Main(string[] args)
        {
            string pipename = "MEGAPIPE-3K";
            var serverProc = StartServer(pipename);

            var pipeClient =
                new NamedPipeClientStream(".", pipename,
                    PipeDirection.InOut, PipeOptions.None,
                    TokenImpersonationLevel.Anonymous);

            Console.WriteLine("Connecting to server...\n");
            pipeClient.Connect();

            var ss = new PipeProtocol(pipeClient as Stream);

            Console.WriteLine("Waiting for server...");
            var hello_back = ss.ReadPayload().ReadAs<HelloResponse>();
            Console.WriteLine($"Server says Hello With '{hello_back.HelloMessage}'");

            Console.WriteLine("Greet server");
            var greet_resp = ss.RequestReply<GreetRequest, GreetResponse>(new GreetRequest());
            Console.WriteLine($"Server says in Greeting '{greet_resp.GreetingMessage}'");

            Console.WriteLine("waiting 3 secs");
            Thread.Sleep(3 * 1000);

            Console.WriteLine("Sum These Numbers");
            var sum_resp = ss.RequestReply<SumTheseNumsRequest,SumTheseNumsResponse>(new SumTheseNumsRequest(7,4));
            Console.WriteLine("Result:{0}",sum_resp.SumResult);

            ss.WriteObject(new QuitRequest());

            Console.Write("Cleanup");
            pipeClient.Close();

            serverProc.Close();
            serverProc.Dispose();

            Console.Write("Press Enter to continue...");
            Console.ReadLine();
        }
    }
}