5.5 Client development


  • 5.5.4 The C# client

    The C# client supports the static mode, the Variant mode and the script mode. The static mode and the Variant mode are introduced here.

    The following code works on the VS2013 console project.

    • The static mode

      1. First, create the C# solution and appliction project, determine the source code directory sdir for the application project, and execute the following command:

      2.

      java -jar <path to limax.jar> xmlgen –c# –noServiceXML –outputPath sdir example.client.xml
      

      3. There are two new created directories xmlsrc and xmlgen in the source code directory. The xmlgen does not need to be submitted to the version control system.

      4. Create the same directory structure in the project according to the generated directory structure, use the operation of "Adding Existing Item" to manually add all the generated files, and the next new generated file is also manually added.

      5. Add the Limax project of the C# version into the solution.

      6. Add the reference to the Limax project for the application project.

      Finally form the following tree structure solution.


      class Program
      {
          private const int providerId = 100;
          private static void start()
          {
              Endpoint.openEngine();
              EndpointConfig config = Endpoint.createEndpointConfigBuilder(
                      "127.0.0.1", 10000, LoginConfig.plainLogin("testabc", "123456", "test"))
                  .staticViewClasses(example.ExampleClient.share.ViewManager.createInstance(providerId))
                  .build();
              Endpoint.start(config, new MyListener());
          }
          private static void stop()
          {
              object obj = new object();
              Action done = () => { lock (obj) { Monitor.Pulse(obj); } };
              lock (obj)
              {
                  Endpoint.closeEngine(done);
                  Monitor.Wait(obj);
              }
          }
          static void Main(string[] args)
          {
              start();
              Thread.Sleep(2000);
              stop();            
          }
      }
      

      The source code of this main function has no difference with the one of the Java version.


      class MyListener : EndpointListener
      {
          public MyListener()
          {
          }
          public void onAbort(Transport transport)
          {
              Exception e = transport.getCloseReason();
              Console.WriteLine("onAbort " + transport + " " + e);
          }
          public void onManagerInitialized(Manager manager, Config config)
          {
              Console.WriteLine("onManagerInitialized " + config.GetType().Name + " " + manager);
          }
          public void onManagerUninitialized(Manager manager)
          {
              Console.WriteLine("onManagerUninitialized " + manager);
          }
          public void onTransportAdded(Transport transport)
          {
              Console.WriteLine("onTransportAdded " + transport);
              MySessionView.getInstance().registerListener(e => Console.WriteLine(e));
          }
          public void onTransportRemoved(Transport transport)
          {
              Exception e = transport.getCloseReason();
              Console.WriteLine("onTransportRemoved " + transport + " " + e);
          }
          public void onSocketConnected()
          {
              Console.WriteLine("onSocketConnected");
          }
          public void onKeyExchangeDone()
          {
              Console.WriteLine("onKeyExchangeDone");
          }
          public void onKeepAlived(int ms)
          {
              Console.WriteLine("onKeepAlived " + ms);
          }
          public void onErrorOccured(int source, int code, Exception exception)
          {
              Console.WriteLine("onErrorOccured " + source + " " + code + “ “ + exception);
          }
      }
      

      Except that the ViewChangedEvent implements the access using the internal attribute method, there is no difference in other aspects

      The internal implementation of the MyTemporaryView.java:

      MyTemporaryView.java


      override protected void onOpen(ICollection<long> sessionids) {
          Console.WriteLine(this + " onOpen " + sessionids);
          registerListener(e => Console.WriteLine(e));
          MySessionView.getInstance().control(99999);
      }
      override protected void onClose() { Console.WriteLine(this + " onClose"); }    
      

      Comiple and run the program, the result:

      onManagerInitialized DefaultEndpointConfig EndpointManagerImpl

      onSocketConnected

      onKeyExchangeDone

      onTransportAdded limax.net.StateTransportImpl

      [class = MyTemporaryView ProviderId = 100 classindex = 2 instanceindex = 6] onOpen System.Collections.Generic.HashSet`1[System.Int64]

      [class = MyTemporaryView ProviderId = 100 classindex = 2 instanceindex = 6] 61440 _var0 Hello 61440 NEW

      [class = MySessionView ProviderId = 100 classindex = 1] 61440 var0 Hello 61440 NEW

      onKeepAlived 47

      [class = MySessionView ProviderId = 100 classindex = 1] 61440 var0 99999 REPLACE

      [class = MyTemporaryView ProviderId = 100 classindex = 2 instanceindex = 6] 61440 _var0 99999 REPLACE

      onTransportRemoved limax.net.StateTransportImpl System.Exception: channel closed manually

      [class = MyTemporaryView ProviderId = 100 classindex = 2 instanceindex = 6] onClose

      onManagerUninitialized EndpointManagerImpl

      It is consistent withe the previous versions.


    • The Variant mode

      Modify the definition of the EndpointConfig:


      EndpointConfig config = Endpoint.createEndpointConfigBuilder(
              "127.0.0.1", 10000, LoginConfig.plainLogin("testabc", "123456", "test"))
          .variantProviderIds(100)
          .build();    
      

      Use variantProviderIds to provide the PVID parameter.

      Modify the onTransportAdded:


      private class MyTemporaryViewHandler : TemporaryViewHandler
      {
          private readonly VariantView mySessionView;
          public MyTemporaryViewHandler(VariantView v) { mySessionView = v; }
          public void onOpen(VariantView view, ICollection<long> sessionids)
          {
              Console.WriteLine(view + " onOpen " + sessionids);
              view.registerListener(e => Console.WriteLine(e));
              Variant param = Variant.createStruct();
              param.setValue("var0", 99999);
              mySessionView.sendControl("control", param);
          }
          public void onClose(VariantView view) { Console.WriteLine(view + " onClose"); }
          public void onAttach(VariantView view, long sessionid) { }
          public void onDetach(VariantView view, long sessionid, int reason) { }
      }
      
      public void onTransportAdded(Transport transport)
      {
          Console.WriteLine("onTransportAdded " + transport);
          VariantManager manager = VariantManager.getInstance((EndpointManager)transport.getManager(), providerId);
          VariantView mySessionView = manager.getSessionOrGlobalView("share.MySessionView");
          mySessionView.registerListener(e => Console.WriteLine(e));
          manager.setTemporaryViewHandler("share.MyTemporaryView", new MyTemporaryViewHandler(mySessionView));
      }    
      

      Compared with the Java version, execpt that the C# does not support to directly create the object through anonymous class and have to define the MyTemporaryViewHandler, there is no any difference.

      Compile and runthe program, the result:

      onManagerInitialized DefaultEndpointConfig EndpointManagerImpl

      onSocketConnected

      onKeyExchangeDone

      onTransportAdded limax.net.StateTransportImpl

      [view = share.MyTemporaryView ProviderId = 100 classindex = 2 instanceindex = 8] onOpen System.Collections.Generic.HashSet`1[System.Int64]

      [view = share.MyTemporaryView ProviderId = 100 classindex = 2 instanceindex = 8] 61440 _var0 Hello 61440 NEW

      [view = share.MySessionView ProviderId = 100 classindex = 1] 61440 var0 Hello 61440 NEW

      onKeepAlived 38

      [view = share.MySessionView ProviderId = 100 classindex = 1] 61440 var0 99999 REPLACE

      [view = share.MyTemporaryView ProviderId = 100 classindex = 2 instanceindex = 8] 61440 _var0 99999 REPLACE

      onTransportRemoved limax.net.StateTransportImpl System.Exception: channel closed manually

      [view = share.MyTemporaryView ProviderId = 100 classindex = 2 instanceindex = 8] onClose

      onManagerUninitialized EndpointManagerImpl

      It is consistent with the previous versions.


    • The conclusion of the C# version

      Except few difference brought by the language feature, the usage and the precautions of the C# version are consistent with the ones of the Java version. The framework provides the class library structure and references to the javadoc document for the detailed information.

      The exception specification of the C# is not perfect. So when dealing with all kinds of EndpointListener messages, it is suggested that do not throw any exception.

      The framework uses the C# language feature of the .NET 3.5. Because the Diffie-Hellman protocol is used as the key exchange in the network authentication process, the support for the BigInteger is needed. If only the .NET 3.5 could be used, another BigInteger library over the .NET 4.0 should be referred.

      In addition, two assistant methods runOnUiThread and uiThreadSchedule are provided in the limax.util package so that the application could schedule the View message notification in its own UI thread. Execute the statement .executor(r => runOnUiThread) when creating the configuration, then call the uiThreadSchedule when its own UI thread is idle.


Prev Next