Toggle Theme Editor
Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate Charcoal

Instant Messaging System

Discussion in 'Java Update' started by Joe, 17/8/19.

  1. Joe

    Joe Thành viên VIP

    WHAT'S PUBLISHER/SUBSCRIBER MESSAGING SYSTEM ?

    Messaging System is an old technology. As IBM still was the mighty king of the IT world it invented its networking architecture (buzzword "SNA" and stands for "System Network Architecture".) All IT foot-soldiers like DEC, PRIME, TANDEM, WANG, etc. had no other choice than to follow IBM's SNA path. One of IBM's Messaging Queuing Service (MQS) is the MQSeries (MQ for Messaging Queues) and that was the starting point of Messaging Queueing for all IT foot soldiers. The hype about MQS was high (like IoT of today). To avoid naming the "You-know-who" people whispered MOM (as Message Oriented Middleware.) Also, nothing to do with a natural "mom". But IBM's MQSeries (plus SNA) was so complicated that MOM worked and ran like a cripple on the Olympic 100-Meter-Race.
    [​IMG]
    End of 1980 SUN started to implement JMS (Java Message Service) in JAVA. Java Messaging Service or JMS was born. However, "MOM" JMS still requires some intermediator (as JMS Provider) and that makes JMS complex. Complicated things usually won't become popular. And it's also the sorrowful fate of JMS. However, since the birth of JMS MQSes wildly sprout like fungi after the warm rain. RabbitMQ is one of the JMS-fungi.

    JMS provides two following architectures:

    PEER-TO-PEER (or P2P) [​IMG]
    Fig.1
    and PUBLISHER-SUBCRIBER (or PubSub) [​IMG]
    Fig.2

    PUBLISHER-SUBSCRIBER -PubSub- and INSTANT MESSAGING

    P2P is simple. Two partners communicate via a MQS-Provider (like 2 smartphone users communicate via Viettel). In case that one of the partner is off-line, the message is stored in a queue provided by JMS provider (like a voice mailbox).

    The PubSub communication is more complex and requires a lot of synchronization. Instead of showing you how to work with a certain MQS-Provider I lead you to a Grassroot-IT-Development using the P2P-PubSub technological architecture. Fig.2 shows you 3 tiers:
    • Publisher
    • Topics (or queues)
    • Subscribers
    Usually the topics are advertised on a billboard provided by someone (provider). Instead of "having an affair" with an unknown provider why don't we merge Publisher and Provider together ? The task is not difficult if you are keen on real development, and not on searching for any ready-for-use tool library.

    Instant Messaging (IM) is known as online messaging (like "WeChat" or "WhasApp"). Very popular among mobile phone users. However, IM implementation requires a good knowledge of the WEB and the underlying networking communication. Again, there're lots of ready-for-use packages or tools for downloading. But, as I said, we do the Grassroot-Development. We develop the IM-P2P_PubSub by ourselves.

    The main problem of IM is the accessibility of Publisher and Subscribers. Normally one has to establish a Publisher domain where the subscribers can reach any time and the subscribers have to provide their reachable IP address to the Publisher. The IP and Domain question are the main obstacle for an IM implementation. And that costs money. Lots of money. To circumvent these "problems" it's wiser to use a "Multicasting IP address". JAVA does support the Multicasting with Multicasting Socket.
    A Multicasting IP works as a group. One participant sends a message to "his" group and everyone of the group "instantly" gets it. And that is the clue. An easy-workable solution for an IM-P2P_PubSub implementation. The range of Multicasting IP addresses is between 224.0.0.0 and 239.255.255.255. The first IP 224.0.0.0 shouldn't be used and is usually reserved for other purposes.
    [​IMG]
    Caution: Multicasting works with UDP. That means some messages could unnoticed get lost.

    IM-P2P_PubSub IMPLEMENTATION

    The requirements should be as simple as possible:
    • Only with standard JAVA APIs (reason: multiple-platform)
    • Protected messages (with encrypt/decrypt algorithm)
    • Message Persistency (availability)
    • P2P between Subscribers, between Subscriber and Publisher
    • PubSub from Publisher to all Subscribers
    • As an API-Package (flexible App-Development)
    [​IMG]

    As usual I select JAVA as the preferred OOPL because of its widespread base of users. All the sources are Javadoc-able (run: javadoc -d [DirectoryName] *.java).

    Protected messages are not a big deal. Any self-made Encrypt-Decrypt Algorithm (EDA) will do. Let's take the simple EDA:

    EDA Joe.java (original: EnDecrypt.java).
    PHP:
    package pubsub;
    /**
    a simple Encrypt/Decrypt algorithm
    @author Joe Nartca (C)
    */
    public class Joe {
      
    /**
        decrypt an encrypted String
        @param inp encrypted String
        @return decrypted String (null if invalid inp)
      */
      
    public static String decrypt(String inp) {
        if (
    inp == null || inp.trim().length() == || (inp.length() % 5) > 0) return null;
        
    int mx inp.length();
        
    String en inp.substring(2mx-3);
        
    StringBuilder sb = new StringBuilder(mx);
        
    int f Integer.parseInt(inp.substring(mx-3)+inp.substring(02));
        for (
    int i=0,x=en.length();ixi+=5sb.append(""+(char)(Integer.parseInt(en.substring(i,i+5))/f));
        return 
    sb.toString();
      }
      
    /**
        encrypt() a String
        @param inp to be encrypted String
        @return encrypted String (null if invalid inp)
      */
      
    public static String encrypt(String inp) {
        if (
    inp == null || inp.trim().length() == 0) return null;
        
    int mx inp.length(), 0;
        while (
    || 255= (int)(Math.random()*255);
        
    String F String.format("%05d"f);
        
    StringBuilder sb = new StringBuilder(5+mx*5);
        
    sb.append(F.substring(3));
        for (
    int i 0mx; ++isb.append(String.format("%05d",((int)inp.charAt(i)) * f));
        
    sb.append(F.substring(03));
        return 
    sb.toString();
      }
    }
    The 3rd point is more or less a list of some conventions which are easily specified and implemented. A simple text file (suffix .txt) is sufficient enough to keep the messages consistent. The upload or download happens when Publisher is instantiated or closed (or on disrupting case such as power interruption, etc.). Here is an example: All conventions are kept in a config.txt file using the format of JAVA Properties.
    PHP:
    #----------------------------------------------------------------#
    # simple config file for Publisher/Subscriber                    #
    # MAX_SIZE is the max. UDP package size (min. 1024 max. 8192)    #
    # MAX_Q defines the max. number of messages per registered queue #
    # MC_PORT is the port number of MC-Socket                        #
    # MC_ADDR is the MC IP which is between the range of 224.0.0.1 - #
    #         239.255.255.255                                        #
    # SUB_LIST is the "persistent" file of Publisher                 #
    #----------------------------------------------------------------#
    MAX_SIZE 2048
    MAX_Q 
    50
    MC_PORT 
    8888
    MC_ADDR 
    224.0.0.3
    SUB_LIST 
    C:/JFX/InstantMessaging/example/subscribers
    The 3 last points require min. 3 following APIs (beside the EnDecrypt API):
    • Interface: PublishEvent.java
    • Subscriber: Subscriber.java
    • Publisher: Publisher.java
    and 2 examples show you how to develop your own IM-P2P_PubSub apps. Further, To ease up our "grassroot" development work we define and design some rules or commands: all special characters between x01 - x1F are reserved and used exclusively by Publisher/Subscriber. The following hex. codes are currently in use as leading command:

    - 0x01: message of Publisher (PubSub), or to other Subscriber (P2P)
    - 0x02: login (User ID and Password)
    - 0x03: register (User ID and Password)
    - 0x04: unregister
    - 0x05: getQueue
    - 0x06: clearQueue
    - 0x07: successful (returned value)
    - 0x08: failed (returned value)
    - 0x09: Publisher ID
    - 0x0A: onlineList
    - 0x0B: toPublisher (P2P)
    - 0x0C: close

    This arrangement is necessary for an implementation of P2P. Let's start with the interface PublishEvent. This interface requires two mandatory methods: onLogin() and onPublish(). Subscriber and Publisher work with PublishEvent-implemented application (similar to JAVA Listener and its adapter).

    Interface PublishEvent.java
    PHP:
    package pubsub;
    /**
    PublishEvent
    @author Joe Nartca (C)
    */
    public interface PublishEvent {
      
    /**
      onPublish. Callback by Subscriber or Publisher
      @param msg String, the newest published message
      */
      
    public void onPublish(String msg);
      
    /**
      onLogin. Callback by Subscriber (or Publisher)
      @param nMsg int, number of messages pending in Queue
      */
      
    public void onLogin(int nMsg);
    }
    The Subscriber is the one who has "to login" or "to register", in order to be able to consume the published messages. He must firstly register before he is entitled to consume instant (published) messages. Later on he needs only to sign in (login) in order to participate the IM circle. Method unregister(), close(), onlineList(), getQueue(), clearQueue(), toSubscriber() and toPublisher(), allow him actively to work with the IM circle.

    Subscriber API methods:
    • boolean register(String UserID, String Password): register to a Publisher
    • unregister(): unregister from Publisher
    • boolean login(String UserID, String Password)
    • ArrayList<String> getQueue( ): retrieve the list of queueing messages
    • clearQueue(): clear Message queue
    • ArrayList<String> onlineList( ): retrieve a list of all online participants
    • toSubscriber(String id, String msg): send a message to a Subcriber
    • toPublisher(String msg): send a message to Publisher
    • boolean isOnline(String id): querry the online status of Subscriber with this id
    • close(): close the session
    • add(PublishEvent event): add an PublishEvent into queue
    • remove(PublishEvent event): removeadd an PublishEvent from queue

    The method panicShutdown() is for emergency case of, for example, power interruption.

    The last API is the Publisher API which is a combination of MQS-Publisher and MQS-Provider. It's an API, hence it consists of only 3 accessible methods: sendMsg() to a Subscriber, sendAll() to all Subscribers and close() to terminate the "Publishing service". Publisher is also monitored by a watchdog "panicShutdown()".

    Publisher API methods:
    • ArrayList<String> subscriberList(): retrieve the list of Subscribers
    • sendMsg(String msg, String id): send a message to Subscriber with the ID
    • sendAll(String msg): send a message to ALL Subscribers
    • close(): close the session
    • add(PublishEvent event): add an PublishEvent into queue
    • remove(PublishEvent event): removeadd an PublishEvent from queue
    Both APIs Subscriber and Publisher work with encrypted text on sending and decrypted text on receiving.
    [​IMG]

    Unzip the sources (Joe.java, PublishEvent.java, Subscriber.java and Publisher.java) and run on a WINDOWS-CMD
    PHP:
    C:\PubSub>javac -g:none -./classes *.java
    C
    :\PubSub>cd classes
    C
    :\PubSub\classes>jar -cvf ../pubsub.jar ./pubsub/*.class > ../o.txt
    C:\PubSub\classes>cd ..
    C:\PubSub>dir *.jar
      ...
    30.08.2018  18:29            15.109 pubsub.jar
      ...
    C:\JoeApp\PubSub>
    With this pubsub.jar you need only to include it in your CLASSPATH...Now, the examples (included in the folder example of the ZIP).

    1) Subcriber (here in SWING) as an implementation of PublishEvent.
    PHP:
    import pubsub.Subscriber;
    import pubsub.PublishEvent;
    public class 
    SubscriberApp extends JFrame implements PublishEvent {
      public 
    SubscriberApp(String configthrows Exception {
        ...
        
    // load the configuration (see the 3rd point: config.txt)
        
    Properties p = new Properties();
        
    p.load(new FileInputStream(config));
        
    // instantiate Subscriber.
        
    Subscriber sub = new Subscriber(p.getProperty("MC_ADDR"),
                         
    Integer.parseInt(p.getProperty("MC_PORT")),
                         
    Integer.parseInt(p.getProperty("MAX_SIZE")));
        
    // introduced this app to Subscriber
        
    sub.add(this);
        ...
        
    jta = new JTextArea(...);
        
    JButton b0 = new JButton("Login");
        
    b0.addActionListener(-> {
          
    String id JOptionPane.showInputDialog(this"Your ID");
          
    String pw JOptionPane.showInputDialog(this"Your Password");
          try { 
    // sign in
            
    if (!sub.login(idpw)) {
              
    jta.append("\r\n"+id+" is unknown or already logged in.");
              
    id null// reset id
            
    } else {
              
    b0.setEnabled(false);
              
    b1.setEnabled(false);
            }
          } catch (
    Exception ex) {
            
    jta.append("\r\nUnable to register:"+id+"\r\nReason:"+ex.toString());
          }
          
    jta.setCaretPosition(jta.getDocument().getLength());
        });
        
    JButton b1 = new JButton("Register");
        
    b1.addActionListener(-> {
          
    String id JOptionPane.showInputDialog(this"Your ID");
          
    String pw JOptionPane.showInputDialog(this"Your Password");
          try { 
    // do the registration
            
    if (sub.register(idpw)) {
              
    jta.append("\r\n"+id+" is registered");
              
    b0.setEnabled(false);
              
    b1.setEnabled(false);
            } else {
              
    jta.append("\r\n"+id+" is already registered.");
              
    id null// reset id
            
    }
          } catch (
    Exception ex) {
            
    jta.append("\r\nUnable to register:"+id+"\r\nReason:"+ex.toString());
          }
          
    jta.setCaretPosition(jta.getDocument().getLength());
        });
        ...
      }
      
    // implemented PublishEvent interface
      
    public void onPublish(String msg) {
        
    jta.append("\r\nReceived:"+msg);
        
    jta.setCaretPosition(jta.getDocument().getLength());
      }
      public 
    void onLogin(int nMsg) {
        
    jta.append("\r\nLogin successfully."+(nMsg 0?" Pending Message"+
                  (
    nMsg 1?"s:":":")+nMsg:" No pending Message"));
        
    jta.setCaretPosition(jta.getDocument().getLength());
      }
      ...
    }
    2) Publisher
    PHP:
    import pubsub.Subscriber;
    import pubsub.PublishEvent;
    public class 
    PublisherApp extends JFrame implements PublishEvent {
      public 
    PublisherApp(String configthrows Exception {
        
    setTitle("PubSub-Application");
        
    Properties p = new Properties();
        
    p.load(new FileInputStream(config));
        
    // load the configuration (see the 3rd point: config.txt)
        
    Publisher pub = new Publisher(p.getProperty("MC_ADDR"),
                                      
    p.getProperty("SUB_LIST"),
                                      
    Integer.parseInt(p.getProperty("MC_PORT")),
                                      
    Integer.parseInt(p.getProperty("MAX_Q")),
                                      
    Integer.parseInt(p.getProperty("MAX_SIZE")));
        
    pub.add(this);
        
    //
        
    ...
        
    JButton b0 = new JButton("List Subscribers");
        
    b0.addActionListener(-> {
          
    ArrayList<Stringslst pub.subscriberList();
          if (
    slst.size() > 0) {
            for (
    String idslst) {
              
    jta.append("\r\n- Subscriber:"+id+" is registered");
            }
            
    jta.setCaretPosition(jta.getDocument().getLength());
          }
        });
        
    JButton b1 = new JButton("Send Subscriber");
        
    b1.addActionListener(-> {
          
    String id JOptionPane.showInputDialog(this"Subscriber ID");
          
    String msg JOptionPane.showInputDialog(this"Message");
          
    pub.sendMsg(msgid);
          
    jta.append("\r\nMessage was sent to "+id);
          
    jta.setCaretPosition(jta.getDocument().getLength());
        });
        
    JButton b2 = new JButton("Send ALL");
        
    b2.addActionListener(-> {
          
    String msg JOptionPane.showInputDialog(this"Message");
          
    jta.append("\r\nMessage was sent to ALL subscribers");
          
    pub.sendAll(msg);
          
    jta.setCaretPosition(jta.getDocument().getLength());
        });
        ...
      }
      ...
      
    // implemented PublishEvent interface
      
    public void onPublish(String msg) {
        
    jta.append("\r\nReceived from Subscriber:"+msg);
        
    jta.setCaretPosition(jta.getDocument().getLength());
      }
      
    // do NOTHING..
      
    public void onLogin(int nMsg) { }
    }
    If you've successfully compiled the examples and run them as following you get this screenshot
    PHP:
    C:\JFX\InstantMessaging\example>javaw PublisherApp
    C
    :\JFX\InstantMessaging\example>javaw SubscriberApp
    C
    :\JFX\InstantMessaging\example>javaw SubscriberApp

    [​IMG]

    Have fun wit your own Instant Messaging System. Pubsub.zip contains the sources and the examples.
     

    Attached Files:

    Last edited: 19/8/19

Chia sẻ trang này

Loading...