:maxdepth: 3
:caption: Frequently Asked Questions
To configure your environment for developing with the multicast library, follow the steps in the Install Guide. Key steps include:
- Ensuring you have a supported version of Python installed.
- Using pip to install (e.g.,
pip install multicast) - Verifying the installation works:
in Python |
in bash |
|---|---|
import multicast |
python3 -m multicast --help |
With multicast installed and set up, this guide will assume you want to listen to multicast
group 224.0.0.1 on the UDP port 59595.
This command will allow you to quickly prototype and test receiving multicast messages without needing to write a full Python script. Once you confirm that the command works as expected, you can then proceed to implement a more advanced solution in Python as necessary.
python3 -m multicast --daemon --use-std HEAR --port 59595 --group 224.0.0.1- Explanation of the Command-Line Options
| Option | Description |
|---|---|
--daemon |
This option runs the multicast listener as a background daemon. |
--use-std |
This specifies the action to take when any output is produced. In this case, it uses the standard output to print received messages instead of the default to just log messages. |
HEAR |
This specifies the action to take. In this case, it will receive multicast. |
--port 59595 |
This sets the UDP port number to 59595, which is used to identify/filter the multicast messages that will be accepted. |
--group 224.0.0.1 |
This specifies the multicast group address to join. You can replace 224.0.0.1 with your desired multicast group address. |
-
Steps to Run
- Open your terminal.
- Ensure you have the multicast module installed and accessible.
- Run the command above, adjusting the port and group as needed for your specific use case.
Most users will want to stick to using HEAR when receiving multicast from the CLI. Alternatively,
users can use the ephemeral RECV (by omitting the --daemon flag) to receive individual UDP
messages, no more than one message at a time.
Tip
Caveat: RECV is much more useful if actually used in a loop, for example:
while true ; do # until user Ctrl+C interrupts
python3 -m multicast --use-std RECV --port 59595 --group 224.0.0.1 --groups 224.0.0.1
doneWhile the command line interface is useful for prototyping, the Python API is better for
the rest of the development process. So, once you are ready to proceed to implement a more advanced
solution in Python, you can import the multicast library module with the usual import multicast
logic.
With multicast installed and set up, this guide will assume you want to send a message to
multicast group 224.0.0.1 on the UDP port 59595.
Sending is similar to listening, you create a sender and pass it the options, except that unlike listeners and receivers, senders are always synchronous (e.g., allowing only one message per call).
from multicast import send
# Create a multicast sender
sender = send.McastSAY()
# Send a message
sender(group='224.0.0.1', port=59595, ttl=1, data='Hello, Multicast!')python3 -m multicast SAY --group 224.0.0.1 --port 59595 --message "Hello World!"- Explanation of the Command-Line Options
| Option | Description |
|---|---|
SAY |
This specifies the action to take. In this case, it will transmit multicast. |
--group 224.0.0.1 |
This specifies the multicast group address to transmit messages to. You can replace 224.0.0.1 with your desired multicast group address. |
--port 59595 |
This sets the UDP port number to 59595, which is used by other members of the multicast group to help identify/filter the multicast messages to accept. |
--message |
This specifies the rest of the input is to be the message to transmit. |
"Hello World!" |
This specifies the multicast message content to transmit. In this case, it is the greeting "Hello World!" |
-
Steps to Run
- Open your terminal.
- Ensure you have the multicast module installed and accessible.
- Run the command above, adjusting the message, port and group as needed for your specific use case.
This command will allow you to quickly prototype sending multicast messages without needing to write a full Python script. You can test the sending functionality alongside the receiving functionality to ensure that messages are being transmitted and received correctly.
Warning
Caveat: this example is still prototype focused, and should be considered a minimal implementation (e.g., lacks useful input validation, lacks error handling, etc.)
# Optional setup console logging
import logging
multicast_logging_sink = logging.getLogger()
multicast_logging_sink.setLevel(logging.INFO) # increase default logging from multicast module
handler = logging.StreamHandler() # example trivial log handler
multicast_logging_sink.addHandler(handler)
# imports
import multicast
from multiprocessing import Process
import random # for random port
# Multicast group address and port
MCAST_GRP = "224.0.0.1" # Replace with your multicast group address (use IPv4 dotted notation)
MCAST_PORT = int(random.SystemRandom().randint(49152, 65535)) # Replace with your multicast port
# Options for multicast listener
listener_options = {
"is_daemon": True, # bool: enable daemon mode
"port": MCAST_PORT, # int: UDP port for multicast
"group": MCAST_GRP # str: multicast group address (use IPv4 dotted notation)
}
# Create a multicast listener
listener = multicast.hear.McastHEAR()
# create a separate process for the listener
p = Process(
target=listener,
name="HEAR", kwargs=listener_options
)
p.daemon = listener_options["is_daemon"]
p.start()
# ... use CTL+C (or signal 2) to shutdown the server 'p'Low-level with example handler:
# imports
import multicast
import random # for random port
import functools # for decorator func metadata
# Multicast group address and port
MCAST_GRP = "224.0.0.1" # Replace with your multicast group address (use IPv4 dotted notation)
MCAST_PORT = int(random.SystemRandom().randint(49152, 65535)) # Replace with your multicast port
# Other important settings (Non-Multicast)
# Multicast does not care about the host IP, but the UDP protocol layer of the Python socket does
# There are 3 logical choices for the vast majority of users:
# 1. '0.0.0.0' for Promiscuous mode (Usually needs privileges to use on most Operating Systems)
# 2. The actual interface IPv4 dot notation address for unprivileged mode
# 3. None, Linux and macOS implementations can let the system choose by passing the
# MCAST_GRP to the Python socket.bind operation (handled by multicast.skt when missing Host IP)
# Windows users must use option 1 or 2 for now.
# This address is per socket (e.g., can be chosen per socket even if on a single interface)
# Options for multicast listener
listener_options = {
"is_daemon": False, # bool: enable/disable daemon mode
"groups": [MCAST_GRP], # list[str]: multicast group addresses (use IPv4 dotted notation list)
"port": MCAST_PORT, # int: UDP port for multicast
"iface": None, # str: System specific interface name, or None to let system choose
"group": MCAST_GRP # str: primary multicast group address (use IPv4 dotted notation)
}
# Example setup for Low-Level use-case
# Define a decorator to loop until able to print a string result of enough length
def printLoopStub(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
while True:
# cache function result
cache = func(*args, **kwargs)
if cache and len(cache) > 1:
print( str( cache ) )
break
elif not cache:
continue
else:
print("Result is too short.")
return wrapper
# Example low-level handler
# Define a decorated handler to only return successfully received messages
@printLoopStub
def inputHandler():
# Create an ephemeral multicast receiver
receiver = multicast.recv.McastRECV()
# create an empty default string
out_string = str()
# try to receive some multicast messages
(didWork, buffer_string) = receiver(
**listener_options
)
# check the result and "handle" if successful
if didWork:
out_string += buffer_string
del receiver # optionally cleanup receiver beforehand
return out_string
# create the actual handler instance
inputHandler()and elsewhere (e.g., in another module or even on another system); an example for the sender API:
# imports
import multicast
from multiprocessing import Process
# Multicast group address and port
MCAST_GRP = "224.0.0.1" # Replace with your multicast group address (use IPv4 dotted notation)
MCAST_PORT = int(59595) # Replace with your multicast port (use the same port as the listeners)
# Other important settings (Non-Multicast)
# Multicast does not care about the host IP, but the UDP protocol layer of the Python socket does
# The sender will default to letting the system choose
# HOST_BIND_IP = "0.0.0.0"
# Options for multicast sender
sender_options = {
"port": MCAST_PORT, # int: UDP port for multicast
"group": MCAST_GRP, # str: multicast group address (use IPv4 dotted notation)
"data": "Default listener only knows: STOP" # str: message content to try to _transmit_
}
# Create an ephemeral multicast sender
sender = multicast.send.McastSAY()
# create a separate process for the sender
p = Process(
target=sender,
name="SAY", kwargs=sender_options
)
p.daemon = False # sender should not be a daemon
try:
p.start()
except Exception as baton:
p.join() # good practice to handle clean up
raise RuntimeError("multicast seems to have failed.") from baton # re-raise
finally:
# clean up some stuff
if p:
p.join() # if not already handled don't forget to join the process and other overhead
# hint: if you use a loop and need to know the exit code
didWork = (p is not None and p.exitcode <= 0) # e.g. check for successWarning
Caveat: the above examples assume the reader is knowledgeable about general
interprocess communication theory and the standard python multiprocessing module and its use.
Together these examples demonstrate a trivial message passing IPC using multicast python sockets.
See also USAGE Guide
The multicast module uses a null log handler by default as part of its logging architecture. This
means that simply calling logging.basicConfig(level=logging.INFO) is insufficient to display the
multicast module's log records, as there's no active handler to capture and output them.
To see multicast module logging output, you must explicitly add a log handler:
%%{ init: { 'theme': 'base', 'themeVariables': { 'darkMode': true, 'primaryColor': '#3e3e3e', 'background': 'transparent', 'primaryBorderColor': 'Orange', 'lineColor': 'darkOrange', 'secondaryColor': 'transparent', 'tertiaryColor': '#030303' }} }%%
sequenceDiagram
participant Python as Your Python Code
participant multicast as Multicast Module
participant logging as `logging` module
Python-->>logging: import logging
opt setup custom log handler
create participant Handler as Your Explicit Log Handler
Python-->>Handler: create `logger` object
Python-->>logging: add custom log handler
end
Python-->>multicast: import multicast
multicast-->>logging: add default logging.NullHandler log handler
multicast-->>logging: logs any multicast module output
opt has added custom log handler
logging->>Handler: handle logging
Handler->>logging: the handler logic
end
multicast-->>Python: initialized
loop main stuff
alt doing whatever
Python-->>Python: Your Non-multicast logic
else using multicast
Python-->>multicast: make API call to multicast
opt module has log output
multicast-->>logging: logs any multicast module output
opt has handler
logging->>Handler: handle logging
Handler->>logging: the handler logic
end
end
multicast-->>Python: reply to API call
end
end
Python-xPython: done
opt has custom log handler
destroy Handler
Python--xHandler: cleanup
end
Important
logging must be set up before importing multicast otherwise the NullHandler set up by
multicast will be first and still be-able to intercept the module's logging.LogRecords just
as it normally would.
import logging
multicast_logging_sink = logging.getLogger()
multicast_logging_sink.setLevel(logging.INFO) # optional, otherwise the module is very quiet
handler = logging.StreamHandler() # replace with your explicit handler
multicast_logging_sink.addHandler(handler)This explicit setup ensures that:
- The log level is set appropriately (e.g.,
logging.INFO) - An active handler (e.g.,
StreamHandler) is added to capture module log records - The multicast module's log output becomes visible to users
- While logging.basicConfig() might seem simpler, it won't work with the multicast module's logging architecture and will result in no visible log output from the library.
To run the test suite, follow the instructions in the Testing Guide.
The guide explains test organization, and how to run tests outside CI/CD.
CI is configured as described in the CI Guide. Key points:
Automated builds and tests are run via GitHub Actions. Each pull request is tested for compatibility and code quality. The guide details the CI workflow files, environment variables, and what to expect when contributing changes.
Important
The default multicast group address is 224.0.0.1.
From the API documentation:
The Value of "224.0.0.1" is chosen as a default multicast group as per RFC-5771 on the rational that this group address will be treated as a local-net multicast (caveat: one should use link-local for ipv6).
Note
The default multicast bind address is the default group. This is effectively 224.0.0.1.
Important
The default multicast Time-to-Live (TTL) is 1.
From RFC-1112 §6.1
... If the upper-layer protocol chooses not to specify a time-to-live, it should default to 1 for all multicast IP datagrams, so that an explicit choice is required to multicast beyond a single network.
From the API documentation:
A Value of 1 (one TTL) is chosen as per RFC-1112 §6.1 on the rational that an explicit value that could traverse beyond the local connected network should be chosen by the caller rather than the default value. This is in line with the principle of none, one or many.
Important
The default UDP port used by multicast is 59595.
From the API documentation:
Arbitrary port to use by default, though any dynamic and free port would work.
While developers and network administrators must consider other factors in real-world deployments,
it is fair to say any free port in the dynamic or "ephemeral" port range of 49152-65535 should
work as far as the multicast module is concerned.
- For
SAYthe port refers to the destination port. - for
RECVandHEARthe port refers to the port to listen on.
Caution
It is best to specify the port in use at this time, as the default will not be properly
assigned to multicast ( see related #62 ) by any central
authority.
0 success
1 non-success - and is often accompanied by warnings or errors
2-225 failure of specific reason
- Any exit value outside the range of
0-255inclusive should be decoded with the formula:| input % 256 |which will yield the correct exit code.
Note
These are inline with CEP-8's POSIX-based guidelines.
-
Typically, the documentation will be automatically built by CI/CD and posted to the official documentation site.
-
However, if one still wishes to build the documentation manually, there is a
maketarget specifically for building from source:make build-docs
By default, the documentation links to the stable branch on GitHub. To override this and link
to the specific commit you're working on, set the DOCS_BUILD_REF environment variable:
# git clone ...
# git checkout <specific commit>
# shellcheck disable=SC2155
export DOCS_BUILD_REF=$("${GIT:-git}" rev-parse --verify HEAD)
make build-docs # or your own documentation build commandThis command dynamically sets DOCS_BUILD_REF to the current Git commit hash, ensuring that
documentation links point to the exact version of your code.