Skip to content

An Echo Server-Client Example

In this section, we will build a simple echo server-client application using the Python Banyan Framework.

First, we will run the demo and then examine the code for both the server and client Banyan components.

Running The Example

The Backplane is installed as an executable module when Python Banyan is installed. By typing 'backplane' in a command or terminal window, the Backplane will start.

The components, simple_echo_server.py, and simple_echo_client.py are invoked from the command line using the typical method to start a Python program.

The following screenshots were taken on a Linux computer running Python 3, and that is why the python3 command is displayed. You may run these examples on Linux, Windows, Mac or, Raspberry using either Python 2 or Python 3. Simply substitute your system's specific python command to run the examples.

First, start the Backplane in a terminal window.

backplane

Start the server in a second terminal window.

Go to the directory that contains simple_echo_server.py and type:

python3 simple_echo_server.py

The base class prints an informational header to the console.

Start the client in a third terminal window.

Go to the directory that contains simple_echo_client.py and type:

python3 simple_echo_client.py

A standard Banyan header is printed to the client's console, and the client begins sending its messages.

When the client completes sending all of its messages, the client's console will indicate the number of messages sent and received.

The console for the server will display all of the message numbers it received

Examining The Server Code

The server's role is to simply wait for the "echo" topic messages to arrive and then to extract the message's payload and publish a "reply" topic message.

Let's look at the code below:

Lines 20 through 22 import the component's required modules.

Line 22 illustrates the standard syntax used to import the BanyanBase base class.

Line 25 declares the EchoServer component class. It inherits from the BanyanBase base class.

     1  """
     2  simple_echo_server.py
     3
     4   Copyright (c) 2016-2019 Alan Yorinks All right reserved.
     5
     6   Python Banyan is free software; you can redistribute it and/or
     7   modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
     8   Version 3 as published by the Free Software Foundation; either
     9   or (at your option) any later version.
    10   This library is distributed in the hope that it will be useful,
    11   but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    13   General Public License for more details.
    14
    15   You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
    16   along with this library; if not, write to the Free Software
    17   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
    18
    19  """
    20  import sys
    21
    22  from python_banyan.banyan_base import BanyanBase
    23
    24
    25  class EchoServer(BanyanBase):
    26      """
    27      This class is a simple Banyan echo server
    28
    29      """

Lines 31 through 44 define the __init__ method.

When inheriting from another class, the parent class must be initialized.

Line 34 initializes the parent and sets the parent's process_name parameter. The process_name is printed to the console when the component is first invoked. All other default values are accepted as is.

Line 37 calls the BanyanBase method set_subscriber_topic to subscribe to 'echo' messages.

Line 41 calls the BanyanBase method receive_loop to wait for incoming messages.

Line 42 through 44 traps a Control-C entered by the user. The inherited BanyanBase clean_up method is called to close the ZeroMQ connections. The program is then exited.

    31      def __init__(self, ):
    32
    33          # initialize the parent
    34          super(EchoServer, self).__init__(process_name='EchoServer')
    35
    36          # subscribe to receive 'echo' messages from the client
    37          self.set_subscriber_topic('echo')
    38
    39          # wait for messages to arrive
    40          try:
    41              self.receive_loop()
    42          except KeyboardInterrupt:
    43              self.clean_up()
    44              sys.exit(0)

Lines 46 through 55 overrides the base class incoming_message_process method. Whenever a message is received by the receive_loop, it calls incoming_message_processing to process the message.

The incoming_message_processing method requires two parameters, a topic string and a payload. Both the topic and payload are decoded within the receive_loop, so they are ready for use by the incoming_message_processing method.

Line 53 republishes the payload just received, setting the topic to 'reply,' The message number of the received message is then printed to the console.

    46      def incoming_message_processing(self, topic, payload):
    47          """
    48          Process incoming messages from the client
    49          :param topic: message topic
    50          :param payload: message payload
    51          :return:
    52          """
    53          self.publish_payload(payload, 'reply')
    54          print('Message number:', payload['message_number'])
    55

Lines 61 through 62 create a function to instantiate the EchoServer class. This function is added so that we can install our component as an executable file in our system if we desire to do so. Installing Components as executable modules is explained in a later section.

Lines 57 through 62 use a standard Python construct to call the instantiation function.

    57  def echo_server():
    58      EchoServer()
    59
    60
    61  if __name__ == '__main__':
    62      echo_server()


Examining The Client Code

The role of the client is to publish 'echo' messages. It maintains a message sequencing number, incorporated into the payload. Shortly after being invoked, the client sends out the first 'echo' message. It then waits for a 'reply' message from the server before publishing the next message.

The messaging sequencing number is decremented after each message is published. When the sequencing number reaches zero, the final message is published, and the client halts.

The code for the client is very similar to that of the server. In this section, the major differences between the client and server are highlighted.

Line 38 calls the inherited BanyanBase method set_subscriber_topic to subscribe to 'reply' messages.

Line 41 establishes instance variables to keep track of the current message sequencing number and the total messages sent. Both are initialized to a value of 10.

Line 44 publishes an "echo" message with a payload containing the current message number.

Line 48 calls the component's receive_loop, and the client waits for the next incoming 'reply' message.

     1  """
     2  simple_echo_client.py
     3
     4   Copyright (c) 2016-2019 Alan Yorinks All right reserved.
     5
     6   Python Banyan is free software; you can redistribute it and/or
     7   modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
     8   Version 3 as published by the Free Software Foundation; either
     9   or (at your option) any later version.
    10   This library is distributed in the hope that it will be useful,
    11   but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    13   General Public License for more details.
    14
    15   You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
    16   along with this library; if not, write to the Free Software
    17   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
    18
    19  """
    20
    21  import sys
    22
    23  from python_banyan.banyan_base import BanyanBase
    24
    25
    26  class EchoClient(BanyanBase):
    27      """
    28      This is a simple echo client derived from the BanyanBase class. It sends out a series of messages and expects an
    29      echo reply from the server.
    30      """
    31
    32      def __init__(self):
    33
    34          # initialize the parent
    35          super(EchoClient, self).__init__(process_name='EchoClient')
    36
    37          # accept banyan messages with the topic of reply
    38          self.set_subscriber_topic('reply')
    39
    40          # sequence number of messages and total number of messages to send
    41          self.message_number = self.number_of_messages = 10
    42
    43          # send the first message - make sure that the server is already started
    44          self.publish_payload({'message_number': self.message_number}, 'echo')
    45
    46          # get the reply messages
    47          try:
    48              self.receive_loop()
    49          except KeyboardInterrupt:
    50              self.clean_up()
    51              sys.exit(0)

Line 53 is the incoming_message_processing method.

Line 62 tests the sequencing value of the 'reply' message. When the sequencing value is set to 0, the final message was sent and received. Lines 63 through 66 print the total number of messages sent and received to the console, and the user is asked to press enter to exit the program.

Lines 69 through 71 process non-terminating 'reply' messages. The message number is decremented by 1, and the next 'echo' message is then built and published.

    53      def incoming_message_processing(self, topic, payload):
    54          """
    55          Process incoming messages received from the echo client
    56          :param topic: Message Topic string
    57          :param payload: Message Data
    58          :return:
    59          """
    60
    61          # When a message is received and its number is zero, finish up.
    62          if payload['message_number'] == 0:
    63              print(str(self.number_of_messages) + ' messages sent and received. ')
    64              input('Press enter to exit.')
    65              self.clean_up()
    66              sys.exit(0)
    67          # bump the message number and send the message out
    68          else:
    69              self.message_number -= 1
    70              if self.message_number >= 0:
    71                  self.publish_payload({'message_number': self.message_number}, 'echo')
    72
    73
    74  def echo_client():
    75      EchoClient()
    76
    77
    78  if __name__ == '__main__':
    79      echo_client()



Copyright (C) 2017-2020 Alan Yorinks All Rights Reserved