A Simple Messaging Service

Introduction

A message server is a middleware program that handles receiving, storing, and forwarding messages.

Project #1

  1. Create two programs (a client and a server)
    • message server
      • receives a message, saves it, and forwards it to a client when requested.
    • message client
      • compose and send messages to the server
      • request and receive messages from the server
  2. UDP will be used to communicate over the network
    (basic Python UDP client/server provided below)
  3. Messages are CSV strings
  4. Messages will contain NO non-printable characters
  5. Messages should be small in length for testing. (max 256 bytes?)
    (what, if any, is the difference between characters and bytes?)
  6. A "no message" response is sent if the server has no messages
  7. After the server forwards a message, the message is deleted from the server's memory

Proposed Message Formats

from-tomessage formatdescription
sender to server"for, message"forward this message
receiver to server"req"request next message
server to receiver"nxt, message"next message
server to receiver"xxx"no message
The first field (ignoring leading ad trailing spaces) in a message indicates the type of message and is case insensitive.

Click HERE (link) for some useful Python files.

Click HERE (link) for design notes/problems/flaws.

Project #2

Add this capability to the server

  1. The server will save multiple messages (a message queue)
  2. The server will store a maximum number of messages (6 max?)
  3. Each message should be time stamped with the server receive time
  4. Messages are returned in received order, oldest first
    First in, First out (FIFO queue)
  5. If the message queue is full, the oldest message is deleted and the new message saved

Proposed message formats

from-tomessage formatdescription
sender to server"for, message"forward this message
receiver to server"req"request next message
server to receiver"nxt, timeStamp, message"next message
server to receiver"xxx"no message
The first field (ignoring leading and trailing spaces) in a message indicates the type of message and is case insensitive.

Project #3

Add a client sender ID and receiver ID fields to messages. (Note: theses are client IDs, not a server ID.)

The client should be modified to allow the user to pretend to be be different clients. A receiver ID specifies who will receive a message.

Proposed message formats

from-tomessage formatdescription
sender to server"F, receiverID, senderID, message"forward this message to receiverID
receiver to server"R, receiverID"request next message for receiverID
server to receiver"N, timeStamp, senderID, message"next message
server to receiver"X"no message
The first and only (non-space) character in the first field in a message indicates the type of message and is case insensitive.

Design questions

Project #4

An administrator should be able to send control messages to the server. Control messages should have a unique senderID such as "admin" or message type "A".

Here are some suggested control messages ...

Open the Firewall (if necessary)

The following command assumes that the OS is Linux and UFW is installed. It opens UDP port 21001 and allows incoming messages. (Windows and macOS may require a different process/command.)

sudo ufw allow 21001 udp

Note: UFW (the Uncomplicated FireWall) is a front end for iptables. It provides an easy interface for people unfamiliar with firewall concepts.

Links

Low-level networking interface
UncomplicatedFirewall

Test Encoding/Decoding

#! /usr/bin/python3
# ===================================================================
# test encoding/decoding of strings to transmit bytes across the network
# ===================================================================

x = 'this is a test string'

print()
print(type(x))
print(len(x))
print(x)

x1 = str.encode(x)

print()
print(type(x1))
print(len(x1))
print(x1)

x2 = x1.decode()

print()
print(type(x2))
print(len(x2))
print(x2)

Test Unicode Characters

#! /usr/bin/python3
# ===================================================================
# test unicode characters/bytes
# ===================================================================

##u = 'hello \u2713 world'          # 3 byte utf-8 character
u = 'hello \03A9 world'             # 2 byte utf-8 character
print()
print(type(u))
print(len(u))
print(u)

##b = str.encode(u)                 # str class method, utf-8 encoding
##b = u.encode('utf-8'))            # forced utf-8 encoding
b = u.encode()                      # default utf-8 encoding

print()
print(type(b))
print(len(b))
print(b)

print()
print(':'.join('{bb:02x}' for bb in b))
print(':'.join('{bb:c}' for bb in b))

UDP Server

#! /usr/bin/python3
# ===================================================================
# UDP server
# From: pythontic.com/modules/socket/udp-client-server-example
# ===================================================================

import socket

localIP     = '127.0.0.1'
localPort   = 21001
bufferSize  = 1024
msg         = 'Hello UDP Client'
bytesToSend = str.encode(msg)

##print(f'msg         {type(msg)}')
##print(f'bytesToSend {type(bytesToSend)}')

# ---- create server socket

UDPServerSocket = socket.socket(family=socket.AF_INET,
                                type=socket.SOCK_DGRAM)

# ---- bind to address and port

UDPServerSocket.bind((localIP,localPort))

print('Message server is listening')

# ---- listen for incoming datagrams

while(True):

    byteAddressPair = UDPServerSocket.recvfrom(bufferSize)

    message = byteAddressPair[0]
    address = byteAddressPair[1]

    ##print(f'message {type(message)}')
    ##print(f'address {type(address)}')

    clientMessage = f'Client message: {message}'
    clientAddress = f'Client address: {address}'

    print(clientMessage)
    print(clientAddress)

    msg = bytes.decode(message)

    print(f'Client message: {msg}')

    # ---- send reply to client

    UDPServerSocket.sendto(bytesToSend, address)

UDPClientSocket.close()

UDP Client

#! /usr/bin/python3
# ===================================================================
# UDP client
# From: pythontic.com/modules/socket/udp-client-server-example
# ===================================================================

import socket

msg               = 'Hello UDP Server'
bytesToSend       = str.encode(msg)
serverAddressPort = ('127.0.0.1',21001)
bufferSize        = 1024

##print(f'msg         {type(msg)}')
##print(f'bytesToSend {type(bytesToSend)}')

# ---- create UDP socket

UDPClientSocket = socket.socket(family=socket.AF_INET,
                                type=socket.SOCK_DGRAM)

# ---- send to server using UDP socket

UDPClientSocket.sendto(bytesToSend, serverAddressPort)

# ---- receive from server using UDP socket

msgFromServer = UDPClientSocket.recvfrom(bufferSize)

msg = f'Message from server {msgFromServer[0]}'

##print(f'msg {type(msgFromServer[0])}')
##print(f'msg {type(msg)}')

print(msg)

msg = bytes.decode(msgFromServer[0])

print(f'Message from server {msg}')

UDPClientSocket.close()