4.2 Run a command-aware TCP simulator
Extend the echo server so that GetID? returns the handler’s process ID
and GetTemp? returns an integer between 0 and 100. Other lines are echoed.
Use the simulator/ directory created in Chapter 3.
Keep connection_handler.sh for the default echo mode.
Replace the launcher and add a handler
-
In
simulator/, replacetcpserver.bashwith this version:#!/usr/bin/env bash # SPDX-License-Identifier: GPL-2.0-or-later # Run a line-based device handler for each local TCP connection. set -euo pipefail PORT=9399 HANDLER=connection_handler.sh [[ $# -le 2 ]] || { printf 'Usage: %s [PORT] [HANDLER]\n' "$0" >&2; exit 2; } if [[ $# -gt 0 ]]; then if [[ "$1" =~ ^[0-9]+$ ]]; then PORT="$1" shift fi if [[ $# -gt 0 ]]; then HANDLER="$1" shift fi fi [[ $# -eq 0 ]] || { printf 'Too many arguments\n' >&2; exit 2; } [[ "$PORT" =~ ^[0-9]{1,5}$ ]] || { printf 'Invalid port\n' >&2; exit 2; } PORT="$((10#$PORT))" (( PORT >= 1 && PORT <= 65535 )) || { printf 'Invalid port\n' >&2; exit 2; } SCRIPT_DIR="$(cd -- "$(dirname -- "$0")" && pwd)" case "$HANDLER" in /*) HANDLER_SCRIPT="$HANDLER" ;; *) HANDLER_SCRIPT="$SCRIPT_DIR/$HANDLER" ;; esac [[ -x "$HANDLER_SCRIPT" ]] || { printf 'Handler is not executable: %s\n' "$HANDLER_SCRIPT" >&2; exit 1; } if command -v socat >/dev/null; then printf 'Attempting to start socat echo server on 127.0.0.1:%s\n' "$PORT" exec socat "TCP-LISTEN:$PORT,bind=127.0.0.1,reuseaddr,fork" "EXEC:'$HANDLER_SCRIPT'" elif command -v tcpsvd >/dev/null; then printf 'Attempting to start tcpsvd echo server on 127.0.0.1:%s\n' "$PORT" exec tcpsvd -l localhost -c 1 -vvE 127.0.0.1 "$PORT" "$HANDLER_SCRIPT" fi printf 'Install socat to run this simulator.\n' >&2 exit 1 -
In the same directory, save
advanced_connection_handler.sh:#!/usr/bin/env bash # SPDX-License-Identifier: GPL-2.0-or-later # Reply with a handler ID, a temperature, or the original input. while IFS= read -r received_text; do case "$received_text" in "GetID?") printf '%s\n' "$$" ;; "GetTemp?") printf '%s\n' "$((RANDOM % 101))" ;; *) printf '%s\n' "$received_text" ;; esac done -
Make both scripts executable:
chmod +x tcpserver.bash advanced_connection_handler.sh
Choose a port and handler
Start one of these commands in a simulator terminal:
| Command | Port | Handler |
|---|---|---|
./tcpserver.bash | 9399 | Echo |
./tcpserver.bash 8888 | 8888 | Echo |
./tcpserver.bash advanced_connection_handler.sh | 9399 | Command-aware |
./tcpserver.bash 8888 advanced_connection_handler.sh | 8888 | Command-aware |
Stop each server with Ctrl+C before trying another command on the same port.
Test the command-aware replies
-
Start the advanced handler on 9399:
./tcpserver.bash 9399 advanced_connection_handler.sh -
In another terminal, connect a client:
socat - TCP:127.0.0.1:9399 -
Enter these commands, one per line:
GetID? GetTemp? WhatIsThis?The replies must be a numeric process ID, a temperature from 0 through 100, and the literal string
WhatIsThis?.
Run three independent simulators
In three terminals, start:
./tcpserver.bash 9399 advanced_connection_handler.sh
./tcpserver.bash 9400 advanced_connection_handler.sh
./tcpserver.bash 9401 advanced_connection_handler.sh
If GNU Parallel is installed, the equivalent command is:
parallel ./tcpserver.bash {} advanced_connection_handler.sh ::: 9399 9400 9401
Each connection has its own handler process. This lets the next lesson check that the three IOC connections reach different devices.
Verification
Connect a client to each port and repeat the three commands above. The three handler IDs must differ. Stop all clients and servers with Ctrl+C; their listening ports must be released.