CreatechStudio / ATCP

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

  1. Abstract

    ATCP is a powerful C/S long-TCP connection framework developed by ATATC™. It supports:

    • p2p communication (even 2 client of the same IP)
    • TCP transition
    • RSA encryption/signature
    • Message Flags
    • Mutil-threads processing
    • Load balance
    • Local data compression
  2. Before Reading

    *** Before reading, notice the form of text, they have meanings.**

    1. object or class or type

      XXX

    2. specific name

      XXX

    3. method and its notes

      XXX
      

      XXX

  3. Download

    Download .jar, .java or .py files here.

  4. Main Logic

    Lifecycle:200%

    1. Message Blocking

      When send() is called, the message will be added to the message queue, it won't be sent until the last message gets its result or there's no message before. In several cases, the next message will be sent at the same time as current message. Cases including using Flags.RESULT_NO_NEED, or current message is a result (this can't be caused manually).

      MIDs are to bind the result to its original message. Each message that is manually sent has a MID, method onRecved() will be called back when the result is received. Use the MID to recognize the result's original message.

  5. Objects

    1. Log

      *** indi.atatc.atcp_server.packages.log.Log. This is an singleton object, which stores server logs and all the Input/Output Streams.**

      1. Set Debug Level

        public enum  DebugLevel {
            INFO, DEBUG, WARNING, ERROR
        }

        INFO < DEBUG < WARNING < ERROR Log will only print the message to the console if the message's debug level is bigger than or the same as current debug level.

        Log.getInstance().debugLevel = Log.DebugLevel.DEBUG;

        Set the Log's debug level as DEBUG, then the Log will only print the message whose debug level is from INFO to DEBUG (including INFO and DEBUG).

      2. Publish

        The most important method is log.publish().

        Log.getInstance().publish();

        The parameter "message" can be either Exception or any type else. If Exception, the debug level must be ERROR. Otherwise, you must fill the parameter "debugLevel". For both cases, you can specify the end of the message, it's "\n" in default.

        For example:

        Log log = Log.getInstance();
        log.debugLevel = Log.DebugLevel.DEBUG;
        log.publish("log message", Log.DebugLevel.INFO);
        log.publish(new RuntimeException("a runtime exception for test"));
        GOES TO
        log message
        

        Because ERROR > BEBUG > INFO.

        Another sample:

        Log log = Log.getInstance();
        log.debugLevel = Log.DebugLevel.DEBUG;
        log.publish("log message 1", ", ", Log.DebugLevel.INFO);
        log.publish("log message 2", Log.DebugLevel.INFO);
        GOES TO
        log message 1, log message 2
        

        In this case, I specified the end of the first message.

      3. Prison

        Prison is an object which stores many blacklist Columns. Each Column can store objects. There will always be a Column with the name "ip", the IPs failed in judge() won't be added to the Column automatically, you can add them manually in judge().

        Get the Prison object:

      Log.Prison prison = Log.getInstance().prison;

      
      Main methods:
      
      ```java
      String name = "column 1";
      Log.Prison.Column column = new Log.Prison.Column(name);
      prison.add(column);
      

      Add a blacklist Column to the Prison.

      Throw StatusError if there is already a Column which has the same name as the given Column.

      Column column1 = prison.getColumnByName("column 1");

      Get the Column with the name.

      Throw StatusError if there is no such Column in the Prison.

      column1.add("element 1 in column 1");
      prison.contains("element 1 in column 1"); // will return true;

      Return whether any Column contains the element.

      prison.clear();

      Empty the Prison.

      1. Time

        Log also provides a static method to get String time.

        String time = Log.time();
        Log.getInstance().publish(time, Log.DebugLevel.INFO);
        GOES TO
        2020-7-24 15:37:26

        「YEAR」-「MONTH (max-12)」-「DAY (max-366)」 「HOUR (max-24)」:「MINUTE (max-60)」:「SECOND (max-60)」

        yyyy-MM-dd HH:mm:ss

        You can also specify the format:

        String format = "HH:mm";
        String time = Log.time(format);
        Log.getInstance().publish(time, Log.DebugLevel.INFO);
        GOES TO
        15:37

        Use the same format as SimpleDateFormat.

    2. Values

      *** indi.atatc.atcp_server.packages.data.Values. This is an singleton object which stores global attributes. The attribute names must be String. **

      1. Define New Attributes

      String name = "attribute 1"; Object value = "value of attribute 1"; Values.getInstance().put(name, value);

      
      > Set a new attribute or rewrite the attribute named "attribute 1". 
      
      ```java
      String valueOfAttribute1 = (String) Values.getInstance().get("attribute 1");
      

      Get the attribute named "attribute 1".

      Method get() will throw AccidentEvents.StatusError when there is no such attribute.

      1. Pre-defined Attributes

        There are 6 pre-defined attributes:

        Name Default Value Usage
        "log_path" "log.txt" The path of the log file.
        "key_length" 2048 The key length of the RSA key pair.
        "separator_first_grade" "\\\"
        "separator_second_grade" "@"
        "separator_third_grade" "#"
        "separator_flag" ":"
    3. Flag & Flags

      1. Flag

        Flags are used to identify the properties of a message. There are 2 types of Flags: system-owned Flags and costume Flags. There are totally five pre-defined (system-owned) Flags: RESULT_NO_NEED, KILL, RES, TO_PID, FROM_PID.

        Name String Usage User Available User Visible
        RESULT_NO_NEED \\res_no_need\\ The message doesn't need a result. The message with this Flag won't be blocked). True False
        FROM_PID \\from_pid\: PID The message is a p2p message from a PID. False True
        TO_PID \\to_pid\\: PID The message is a p2p message which expected to be sent to a PID. False False
        KILL \\kill\\ This is the last message. False False
        RES \\res\\ This is a result. False False

        Flags can have values, like Flags.FROM_PID and Flags.TO_PID. The values must be String.

        1. Create A Costume Flag
          Basics.ContainerClass.Flag flag = new Basics.ContainerClass.Flag("costume flag 1");

          Create a costume Flag named "costume flag 1".

          Basics.ContainerClass.Flag flag = new Basics.ContainerClass.Flag("costume flag 2", "value of costume flag 2");

          Create a costume Flag named "costume flag 2" with value "value of costume flag 2".

        2. Get The Flag's Value
          Basics.ContainerClass.Flag flag1 = new Basics.ContainerClass.Flag("flag 1", "I am flag 1");
          String valueOfFlag1 = flag1.getValue();
          Log.getInstance().publish(valueOfFlag1, Log.DebugLevel.INFO);
          GOES TO
          I am flag 1
          
      2. Flags

        *** Flags is a serial of Flags. All the pre-defined Flags are also stored inside as static members. **

        1. Create A New Flags Object
          Flags flags = new Flags(Flags.RESULT_NO_NEED, new Basics.ContainerClass.Flag("costume flag 1", "value of costume flag 1"));

          Create a new Flags object which contains Flags.RESULT_NO_NEED and a costume Flag.

        2. Add
          flags.add(new Basics.ContainerClass.Flag("costume flag 2"));

          Add a new costume Flag to the Flags object.

        3. Get Value
          String valueOfFlag1 = flags.valueOf("costume flag 1");
          Log.getInstance().publish(valueOfFlag1, Log.DebugLevel.INFO);
          GOES TO
          value of costume flag 1
          

          Get the value of Flag "costume flag 1". Method valueOf() will return null if there is no such Flag.

          Or,

          String valueOfFlag1 = flags.valueOf(new Basics.ContainerClass.Flag("costume flag 1"));
          Log.getInstance().publish(valueOfFlag1, Log.DebugLevel.INFO);
          GOES TO
          value of costume flag 1
          

          Get the value of Flag "costume flag 1". Method valueOf() will return null if there is no such Flag.

        4. Contains
          boolean contains = flags.contains(new Basics.ContainerClass.Flag("costume flag 1"));
          Log.getInstance().publish(contains, Log.DebugLevel.INFO);
          GOES TO
          true
          

          Get whether the Flags object contains Flag Flags.RESULT_NO_NEED.

        5. Iteration

          Use toArray() to turn the Flags object into an array. Then use foreach for iteration:

          for (Basics.ContainerClass.Flag flag: flags.toArray()) {
          }
  6. Usage

    *** All the samples are written with JAVA, if you are using Python version, the differences are mentioned in the source code.**

    1. Server

      1. Setup Your Configuration

        Create a new Configutation object:
        Server.Configuration configuration = new Server.Configuration();
        configuration.name = "ATCP Test"; // the server's name
        configuration.port = 1024; // server port, it's 4747 in default
        configuration.project = "ATCP"; // the project to which the server belongs
      2. Create A Simple Server Object

        Server server = new Server(2000, configuration) {
            @Override
            public Process onConnected(Connection connection) {
                return new Process(this, connection) {
                    @Override
                    protected void onSent(MID mid) {
                    }
        
                    @Override
                    protected void onRecved(MID mid, String result, Flags flags) {
                    }
        
                    @Override
                    protected String process(String msg, Flags flags) {
                    }
                };
            }
        };

        Override 1 abstract method:

        abstract Process onConnected(Connection connection)

        This method should return a Process object which will be used to handle the client's requests.

        There are 5 callback methods in a Server object:

        void onStart()

        Called when

        void onStarted()

        Called when

        abstract Process onConnected(Connection connection)

        Called when

        void onInterrupt()

        Called when

        void onInterrupted()

        Called when

        *** Attention, make sure that you have complete all the settings here because you can never meet this Server object again anymore.**

        Use

        Process = new Process(this, connection) {
            @Override
            protected void onSent(MID mid) {}
        
            @Override
            protected void onRecved(MID mid, String result, Flags flags) {}
        
            @Override
            protected String process(String msg, Flags flags) {
                return "recved" + msg;
            }
        };

        to create a new Process object. There are 9 callback methods during a Process object's lifecycle:

        void onStart()

        Called when Thread has been started.

      void onStarted()

      
      > Called when the connection parameters have been determined. 
      
      ```java
      void onSend(MID mid)
      

      Called when a message is about to be sent.

      abstract void onSent(MID mid)

      Called when a message has been sent.

      abstract void onRecved(MID mid, String result, Flags flags)

      Called when a result is sent back to the server.

      abstract String process(String msg, Flags flags);

      Called when the server receives a message. This method usually should return a String, if you don't want to return anything, just return "".

         void onClosed()

      Called when the Process is about to end.

      void onDiscard()

      Called when the Process has ended.

      void onDiscard()

      Called when the Process object is about to be destroyed.

      1. Setup Process Attributes

        You can disable any additional function like RSA encryption, message blocking, multi-threads processing. If you change these attributes after the Process has been started (generally you can't), it won't work.

        1. RSA Encryption
          server.setRSAOn(true/false);
          Boolean Affect
          true (default) Use RSA encryption and signature to keep every message safe.
          false All the messages will be sent in plaintext.

          Make sure you have a secure environment before you disable this function.

        2. Message Blocking
          server.setMessageBlockingOn(true/false);
          Boolean Affect
          true (default) There won't be 2 messages being handled by the remote at the same time.
          false The message will be sent and handled by the remote as long as the user calls send().

          Generally, this function is ineffective to the main function. Disable this function to improve the performance, but might cause some bugs.

        3. Multi-threads Processing
          server.setMultiThreadsOn(true/false);
          Boolean Affect
          true Every message will be handled in several threads if able.
          false (default) Every message will be handled in the same thread.

          This function is disabled in default. Enable it if you have a lot of computations for each client.

        4. Error Handling
          server.setErrorHandlingOn(true/false);
          Boolean Affect
          true (default) Catch every known exception automatically.
          false Catch every known exception and throw an AccidentEvent with the origin message.

          Never disable this function unless in tests. If disabled, all will be thrown directly. Disable function might cause more exceptions.

      2. Setup LoopListeners

        Use

        LoopListener loopListener = new LoopListener() {
          	// ToDo: override this method
            @Override
            public boolean when() {
                if (...) return true; // return true to execute
              	else return false; // return false to pass
            }
        
          	// ToDo: Override this method
            @Override
            public void run() {
              	// run sth
            }
        };

        to create a new LoopListener. If when() returns true, run() will be called. A LoopListener can be added to a Server or a Process. Generally, you should send or do anything but settings inside a LoopListener.

      3. Set Connect Rule

        Override judge() in the Server object.

        Server server = new Server(configuration) {
          	//Todo: override this method
          	@Override
          	public boolean judge(Log log, IP ip) {
              	if (...) return true; // return true to allow the ip to connect
                else return false; // return false to discard this connection
            }
            @Override
            public Process onConnected(Connection connection) {
                return new Process(this, connection) {
                    @Override
                    protected void onSent(ID.MID mid) {}
        
                    @Override
                    protected void onRecved(ID.MID mid, String result, Flags flags) {}
        
                    @Override
                    protected String process(String msg, Flags flags) {
                        return "recved msg: " + msg;
                    }
                };
            }
        };
      4. Start

        server.start();

        The server will run in the main thread, if you need it non-blocking in a child thread, fill the parameter with Server.Mode.MT.

        server.start(Server.Mode.MT);

        There are 3 modes to start the server.

        Name Full Name Usage
        MT Multi-threads Run the server in a child thread.
        T Test Run the server as a test (only accept from IP that starts with 192.168 or localhost).
        MT_T Multi-threads test Run the server as a test (only accept from IP that starts with 192.168 or localhost) in a child thread.
      5. Specify ActionCallback

        ActionCallback is an interface of periodic callback method. Specifying the periodic callback methods when handling a message instead of recognising by MID inside the default callback methods is much simpler and more practical (at least I think so).

        Create a new ActionCallback interface:

        Process.ActionCallback actionCallback = new Process.ActionCallback() {
          @Override
          public void onSend() {}
        
          @Override
          public void onSent() {}
        
          @Override
          public void onRecved(String result, Flags flags) {}
        };

        Override 3 abstract methods:

        abstract void onSent(MID mid)
        abstract String process(String msg, Flags flags)
        abstract void onRecved(MID mid, String result, Flags flags)

        It's easy to find out that these methods in an ActionCallback interface are part of the callback methods of a Process object, and its function and lifecycle are consistent with those in a Process object.

        process.specifyActionCallback(mid, actionCallback);
      6. Interrupt

        server.interrupt();

        This method will directly stop all the process and stop listening the port immediately without blocking.

      7. Stop

        server.stop();

        This method will stop listening the port. As long as all the process have ended, the Server will no longer be maintained.

    2. Client

    3. Transition Server

About


Languages

Language:Java 83.9%Language:Python 16.1%