AMPSharp - An AMP library for Mono and Microsoft .NET

  • 100% managed C# code
  • Client and Server
  • Extensive unit-test coverage
  • Runs with Microsoft .NET or Mono
  • Blocking and Non-Blocking (asynchronous) interfaces
  • Full set of AMP data types
  • Packaged in Debian unstable as of September, 2011
  • Packaged via NuGet

Code and downloads are hosted on Launchpad:

https://launchpad.net/ampsharp

Examples

Server for a Sum, and a Divide command:

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;

namespace MathServer
{
    class Server
    {
        static void Main(string[] args)
        {
            int port = 1234;
            var listener = new TcpListener(port);
            listener.Start(5);
            
            Console.WriteLine("Listening on port {0}", port);

            TcpClient client;
            AMP.Protocol protocol;

            // the AMP.Command's we're going to respond to
            var sum = new Sum();
            var div = new Divide();
            while (true)
            {
                client = listener.AcceptTcpClient();
                protocol = new AMP.Protocol(client.GetStream());

                // we could also pass a 'state' argument to RegisterResponder
                // which becomes the 'state' argument in the responders (below).
                // Since we aren't passing anything, then state will always be null
                protocol.RegisterResponder(sum, handleSum);
                protocol.RegisterResponder(div, handleDivide);
                protocol.StartReading(); // start the async data-reading loop
            }

        }

        static Object handleSum(AMP.Msg reqMsg, Object state)
        {
            int a = (int)reqMsg["a"];
            int b = (int)reqMsg["b"];
            int total = a + b; 
            Console.WriteLine("Did a sum: {0} + {1} = {2}", a, b, total);
            return new AMP.Msg {{"total", total}};
        }

        static Object handleDivide(AMP.Msg reqMsg, Object state)
        {
            int numerator   = (int)reqMsg["numerator"];
            int denominator = (int)reqMsg["denominator"];
            decimal result = (decimal)numerator / denominator;

            Console.WriteLine("Did a division: {0} / {1} = {2}", numerator, denominator, result);
            return new AMP.Msg { { "result", result } };
        }
    }

    public class Sum: AMP.Command
    {
        public Sum()
            : base("Sum")
        {
            AMP.IAmpType int32 = new AMP.Type.Int32();

            AddArgument("a", int32);
            AddArgument("b", int32);

            AddResponse("total", int32);
        }
    }

    public class Divide : AMP.Command
    {
        public Divide()
            : base("Divide")
        {
            AMP.IAmpType int32 = new AMP.Type.Int32();
            AMP.IAmpType dec = new AMP.Type.Decimal();

            AddArgument("numerator", int32);
            AddArgument("denominator", int32);

            AddResponse("result", dec);

            AddError(typeof(DivideByZeroException), Encoding.UTF8.GetBytes("ZERO_DIVISION"));
        }
    }
}

Client code that excersies Sum and Divide commands in various ways:

using System;
using System.Net.Sockets;
using System.Threading;

namespace MathClient
{
    class Client
    {
        static void Main(string[] args)
        {
            string hostname = "localhost";
            int port = 1234;

            var client = new TcpClient(hostname, port);
            var protocol = new AMP.Protocol(client.GetStream());
            protocol.StartReading(); // start the async data-reading loop

            // Instantiate the Commands we're going to call
            AMP.Command sum = new MathServer.Sum();
            AMP.Command div = new MathServer.Divide();

            // Make one remote call synchronously. CallRemote() will raise an exception if
            // anything goes wrong making the call.
            AMP.Msg msg = protocol.CallRemote(sum, new AMP.Msg { { "a", 5 }, { "b", 7 } });
            Console.WriteLine("Got sum result: {0}", (int)msg["total"]);
            Thread.Sleep(2000);

            // Now make some asynchronous calls in a loop
            int count = 3;
            while (count-- > 0)
            {
                // pass the AMP.Protocol object though as the `state' parameter since we need it in the callback
                protocol.BeginCallRemote(sum, new AMP.Msg { { "a", 13 }, { "b", 81 } }, CBGotSumResponse, protocol);
                Thread.Sleep(2000);
            }

            // Do a division
            try
            {
                msg = protocol.CallRemote(div, new AMP.Msg { { "numerator", 10 }, { "denominator", 3 } });
            }
            catch (DivideByZeroException)
            {
                System.Console.WriteLine("Shouldn't have caused zero division here!");
                return;
            }
            Console.WriteLine("Got division result: {0}", (decimal)msg["result"]);

            // Now do it again, but this time cause a DivideByZeroException on the remote end,
            // which will result in a DivideByZeroException being raised here, because the 
            // Divide command has been implemented to handle this particular Exception.
            try
            {
                msg = protocol.CallRemote(div, new AMP.Msg { { "numerator", 10 }, { "denominator", 0 } });
            }
            catch (DivideByZeroException)
            {
                System.Console.WriteLine("Got DivideByZeroException as expected.");
            }

            Console.ReadKey(); // Press any key to continue...
        }
        
        // Call-back method for receiving result from server asynchronously
        static void CBGotSumResponse(IAsyncResult ar)
        {
            var protocol = (AMP.Protocol)ar.AsyncState;
            // EndCallRemote() will raise if any error occured during the remote call
            AMP.Msg respMsg = protocol.EndCallRemote(ar);
            Console.WriteLine("Got sum: {0}", (int)respMsg["total"]);
        }
    }
}