Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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

  1. In simulator/, replace tcpserver.bash with 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
    
  2. 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
    
  3. 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:

CommandPortHandler
./tcpserver.bash9399Echo
./tcpserver.bash 88888888Echo
./tcpserver.bash advanced_connection_handler.sh9399Command-aware
./tcpserver.bash 8888 advanced_connection_handler.sh8888Command-aware

Stop each server with Ctrl+C before trying another command on the same port.

Test the command-aware replies

  1. Start the advanced handler on 9399:

    ./tcpserver.bash 9399 advanced_connection_handler.sh
    
  2. In another terminal, connect a client:

    socat - TCP:127.0.0.1:9399
    
  3. 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.