Showing posts with label AJAX. Show all posts
Showing posts with label AJAX. Show all posts

Sunday, July 27, 2025

Designing a Lightweight Web Server for Real-Time System Monitoring in C++

This article walks through the design and implementation of a lightweight dual-port web server that can serve a system monitoring dashboard and receive backend updates — all implemented in modern C++ using only standard libraries.

Ø  Overview

Ø  Architecture design

Ø  Implementation

Ø  Data flow diagrams

Ø  Analysis of RESTful API Usage in the System

You can find the source codes from https://github.com/happytong/lightweight_web_server:

·         Source codes of web server and backend monitor

·         HTML file generated by the web server

·         Running logs of the web server and backend monitor


Ø Overview

Objectives

·      Minimal Dependencies: Use only standard C/C++ libraries and POSIX sockets.

·      Dual-Port Design: Separate traffic for web interface (port 8080) and backend updates (port 12345).

·      Real-Time Update: Enable frontend users to see near real-time status.

·      Thread Safety: Ensure concurrent read/write operations are safe.

·      Extendibility: Easy to add new devices or endpoints.

Architecture Overview

 
+------------------+       POST /update_system         +---------------------+
|  Backend Monitor | --------------------------------> |  Web Server (12345) |
+------------------+                                   +---------------------+
       ^                                                         |
       |         Notify via TCP port 54321                       v
       |<--------------------------------------------------------+
       |
+-------------------+           GET/POST               +-------------------+
| Web Browser (UI)  | <------------------------------> | Web Server (8080) |
+-------------------+                                  +-------------------+
    

Component 1: Backend Monitor

The backend monitor simulates random faults, sends updates to the server, and listens for override commands.

 
POST Body Example:
system_status=Operational&Device1=ok&Device2=fault&Storage+Unit=operational
    

Component 2: Web Server

This component listens on two ports and uses pthreads to handle incoming connections concurrently.

Backend API (port 12345)

·       POST /update_system

Web UI API (port 8080)

·      GET / - Full HTML dashboard

·      GET /check_status - JSON system status

·      GET /device_status_json - JSON device list

·      POST /update_system_web

·      POST /update_device_web

Real-Time Sync

When the user updates a status via the web, the server forwards the update to the backend using TCP.

 
SYSTEM_STATUS_UPDATE:Critical
DEVICE:Device1=fault
END
    

Key Design Patterns

·         Socket + select(): Dual-port concurrent server loop

·         Detached pthread per client: Handle each connection independently

·         ostringstream:Build HTTP responses dynamically

·         Custom protocol (TCP): Lightweight backend-to-web sync

·         JSON + HTML + CSS:User-friendly frontend without JS libs

Production Improvements

·         Use std::thread or asio instead of pthreads

·         Add HTTPS support

·         Use structured logging (e.g., spdlog)

·         Use WebSocket instead of polling

·         Replace manual multipart parsing

Conclusion

This design enables a real-time, embeddable C++ monitoring system without relying on heavy frameworks—perfect for constrained environments like embedded or industrial systems.


Ø Architecture design

System Architecture Overview

Key Components:

·            Backend: Simulates devices, handles status updates (Port 12345)

·            Web Server: Serves UI and API endpoints (Port 8080 + 12345)

·            Notification Channel: Direct TCP communication (Port 54321)



Core Design Principles

1. Minimalist Concurrency Model

// web_server.cc - Select-based connection handling  
fd_set read_fds;  
FD_SET(backend_server_fd, &read_fds);  
FD_SET(web_server_fd, &read_fds);  
select(max_fd + 1, &read_fds, NULL, NULL, NULL);

·            Uses select() instead of heavy frameworks

·            Thread-per-client with mutex-protected shared state

·            Separate counters for web/backend connections

2. Efficient Data Flow

Backend → Web Server:

POST /update_system  
system_status=Operational&Device1=ok&Network+Controller=active

Web Server → Backend:

SYSTEM_STATUS_UPDATE:Maintenance  
DEVICE:Storage Unit=offline  
END

3. Shared State Management

// Central context with mutex protection  
struct ThreadContext {  
    pthread_mutex_t mutex;  
    std::string system_status;  
    std::vector<DeviceStatus> device_statuses;  
};

·       Single context object for all threads

·       Short critical sections using RAII locks

·       Copy-on-read for thread safety

 

Key Components Breakdown

Backend Monitor (backend_monitor.cc)

Responsibilities:

·      Device simulation with probabilistic faults

·      HTTP status pushing to web server

·      Notification listener for overrides

Efficiency Features:

·       Deterministic device update cycle (5s)

·       Connection reuse for HTTP posts

·       Lightweight URL encoding

 

// backend_monitor.cc - Update cycle  
void run() {  
    while (true) {  
        update_device_statuses();  
        std::this_thread::sleep_for(5s);  
    }  
}

Web Server (web_server.cc)

Dual-Port Architecture:

Port 12345 (Backend)

Port 8080 (Web)

POST /update_system

GET / (Dashboard)

GET /device_status_json

POST /update_system_web

Optimization Techniques:

·       Single HTML Page: Embedded CSS/JS

·       JSON Endpoints: Minimal data transfer

·       Connection Reuse: HTTP keep-alive

 

// Dashboard response - All-in-one HTML  
std::string handle_root_request() {  
    std::ostringstream html;  
    html << "<html><style>/* Embedded CSS */</style>"  
         << "<script>/* Embedded JS */</script>"  
         << "<body>...</body></html>";  
    return html.str();  
}

 

Real-Time Update Mechanism

·      Browser fetches /device_status_json every 10s

·      Web server copies device data under mutex

·      JSON response triggers DOM updates

// Embedded JavaScript - Auto-refresh  
function refreshDevices() {  
    fetch('/device_status_json')  
        .then(r => r.json())  
        .then(data => updateTable(data.devices));  
}  
setInterval(refreshDevices, 10000);

 

Synchronization Techniques

Concurrency Approach:

·      Writer Priority: Backend updates get immediate mutex access

·      Copy-on-Read: Web handlers snapshot data quickly

·      Connection Counting: Separate mutex for connection stats

// Safe data access pattern  
pthread_mutex_lock(&ctx->mutex);  
auto devices_copy = ctx->device_statuses; // Snapshot  
pthread_mutex_unlock(&ctx->mutex);  
// Use devices_copy for processing

 

Performance Considerations

·      Memory Footprint

1.    Shared context avoids duplication

2.    Stack-allocated buffers (4KB)

3.    No dynamic allocations in hot paths

·      CPU Efficiency

1.    select() scales better than epoll() for <100 connections

2.    Fixed update intervals prevent busy-waiting

3.    Header-only parsing (no regex)

·      Network Optimization

1.    URL-encoded POSTs instead of JSON

2.    Persistent connections

3.    Unified notification protocol

 

Lessons for Lightweight Systems

·      Vertical Integration

1.    Tight coupling between components reduces middleware

2.    Shared protocol understanding avoids validation overhead

·      Purpose-Built Protocols

1.    Custom TCP notifications > HTTP for critical commands

2.    Mixed text/binary formats where appropriate

·      Resource Awareness

1.    One mutex per resource type (data vs connections)

2.    Know when to copy vs reference

3.    Prefer steady intervals over event storms

 

Potential Improvements

·      Binary Protocol: Protobuf for device updates

·      Connection Pooling: Reuse backend sockets

·      Delta Updates: Send only changed device statuses

·      Watchdog Timer: Restart failed components

 

Conclusion

This lightweight architecture demonstrates how to build an efficient monitoring system using:

·       Select-based I/O for connection management

·       Shared context with strategic locking

·       Dual-purpose server handling API and UI

·       Direct communication for critical updates

By combining these techniques, we achieve a functional monitoring system with minimal resource consumption - perfect for embedded systems or edge deployments. The complete implementation fits in two files under 1,600 LOC, proving that lightweight doesn't mean limited functionality.

Final Tip: For production systems, add basic authentication and HTTPS. The current focus on efficiency provides an excellent foundation for these security enhancements.

(This section was enhanced by DeepSeek)


Ø Implementation

Core Architecture

Dual-Port Design

 

The server separates concerns by running two HTTP services on different ports (for logging purpose):

 

enum ServerType {
    BACKEND_SERVER,  // Port 12345: Backend API
    WEB_SERVER       // Port 8080: Web Interface
};

 

Key Data Structures

 

Thread Context for Shared State:

 

struct ThreadContext {
    pthread_mutex_t mutex;         // For system_status and device data
    pthread_mutex_t conn_mutex;    // For connection tracking
    int active_backend_connections;
    int active_web_connections;
    std::string system_status;
    std::vector<DeviceStatus> device_statuses;
};

 

HTTP Request Handling:

 

struct HttpRequest {
    std::string method;
    std::string path;
    std::string version;
    std::string body;
    bool keep_alive;
    ServerType server_type;
};

 

Multi-Threading Implementation

 

The server uses a thread-per-connection model with HTTP keep-alive support:

 

void* handle_client(void* arg) {
    ClientThreadArgs* args = static_cast<ClientThreadArgs*>(arg);
    
    while (true) {
        // Receive and parse request
        HttpRequest request = parse_http_request(buffer, bytes, client_fd, server_type);
        
        // Route and handle request
        std::string response = route_request(request, ctx, connection_id);
        send(client_fd, response.c_str(), response.length(), 0);
        
        // Break if not keep-alive
        if (!request.keep_alive) break;
    }
    
    // Update connection count and cleanup
    pthread_mutex_lock(&ctx->conn_mutex);
    if (server_type == WEB_SERVER) {
        ctx->active_web_connections--;
    } else {
        ctx->active_backend_connections--;
    }
    pthread_mutex_unlock(&ctx->conn_mutex);
    close(client_fd);
}

 

Real-Time Web Interface

AJAX-Based Updates

The web interface provides real-time status updates through JavaScript polling:

 

function refreshAll() {
    // Parallel requests for device and system status
    fetch('/device_status_json')
        .then(response => response.json())
        .then(data => updateDeviceTable(data));
    
    fetch('/check_status')
        .then(response => response.json())
        .then(data => updateSystemStatus(data));
}
 
// 10-second refresh interval
setInterval(refreshAll, 10000);

 

Server-Side JSON Endpoints

 

Device Status Endpoint:

 

std::string handle_device_status_json_request(ThreadContext* ctx) {
    pthread_mutex_lock(&ctx->mutex);
    std::vector<DeviceStatus> devices = ctx->device_statuses; // Thread-safe copy
    pthread_mutex_unlock(&ctx->mutex);
 
    std::ostringstream json;
    json << "{\"devices\":[";
    for (size_t i = 0; i < devices.size(); ++i) {
        if (i > 0) json << ",";
        json << "{\"name\":\"" << devices[i].name << "\",\"status\":\"" << devices[i].status << "\"}";
    }
    json << "]}";
    
    return create_json_response(json.str());
}

 

Form Processing

 

The server handles both URL-encoded and multipart form submissions:

 

URL-Encoded (Backend API):

 

std::string handle_update_system_request(ThreadContext* ctx, const std::string& body) {
    std::istringstream body_stream(body);
    std::string pair;
    
    while (std::getline(body_stream, pair, '&')) {
        size_t eq_pos = pair.find('=');
        if (eq_pos != std::string::npos) {
            std::string key = url_decode(pair.substr(0, eq_pos));
            std::string value = url_decode(pair.substr(eq_pos + 1));
            
            if (key == "system_status") {
                ctx->system_status = value;
            } else {
                // Update device status
                for (auto& device : ctx->device_statuses) {
                    if (device.name == key) {
                        device.status = value;
                        break;
                    }
                }
            }
        }
    }
}

 

Multipart Forms (Web Interface):

 

std::string handle_update_system_web_request(ThreadContext* ctx, const std::string& body) {
    // Simple multipart parsing for system_status field
    size_t field_pos = body.find("name=\"system_status\"");
    if (field_pos != std::string::npos) {
        size_t value_start = body.find("\n\n", field_pos) + 2;
        size_t value_end = body.find("------WebKit", value_start);
        
        std::string value = body.substr(value_start, value_end - value_start);
        // Trim whitespace
        value.erase(value.find_last_not_of(" \t\n\r") + 1);
        
        ctx->system_status = value;
        
        // Notify backend monitor
        notify_backend_monitor(value, ctx->device_statuses);
    }
}

 

Inter-Process Communication

TCP Socket Notifications

The webserver notifies the backend monitor of status changes via TCP sockets:

 

void notify_backend_monitor(const std::string& system_status, 
                           const std::vector<DeviceStatus>& devices) {
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    sockaddr_in backend_addr = {0};
    backend_addr.sin_family = AF_INET;
    backend_addr.sin_port = htons(54321);  // Backend notification port
    
    if (connect(sock, (sockaddr*)&backend_addr, sizeof(backend_addr)) == 0) {
        std::ostringstream notification;
        notification << "SYSTEM_STATUS_UPDATE:" << system_status << "\n";
        for (const auto& device : devices) {
            notification << "DEVICE:" << device.name << "=" << device.status << "\n";
        }
        notification << "END\n";
        
        send(sock, notification.str().c_str(), notification.str().length(), 0);
    }
    close(sock);
}

 

Backend Monitor Integration

 

The backend monitor (`backend_monitor.cc`) implements a notification listener:

 

void start_notification_listener() {
    std::thread listener_thread([this]() {
        int server_sock = socket(AF_INET, SOCK_STREAM, 0);
        bind(server_sock, ...); // Bind to port 54321
        listen(server_sock, 5);
        
        while (true) {
            int client_sock = accept(server_sock, nullptr, nullptr);
            std::thread([this, client_sock]() {
                handle_notification(client_sock);
                close(client_sock);
            }).detach();
        }
    });
    listener_thread.detach();
}
 
void handle_notification(int client_sock) {
    char buffer[4096];
    recv(client_sock, buffer, sizeof(buffer)-1, 0);
    
    std::string notification(buffer);
    std::istringstream stream(notification);
    std::string line;
    
    while (std::getline(stream, line)) {
        if (line.find("SYSTEM_STATUS_UPDATE:") == 0) {
            external_system_status = line.substr(21);
            external_status_override = true;
        } else if (line.find("DEVICE:") == 0) {
            // Parse and update device status
        }
    }
}

 

Backend Device Simulation

 

The backend monitor simulates realistic device behavior:

 

struct DeviceInfo {
    std::string name;
    std::string status;
    int fault_probability;  // 0-100% chance of fault
};
 
void update_device_statuses() {
    for (auto& device : devices) {
        int roll = random_dist(random_generator);
        
        if (roll <= device.fault_probability) {
            if (device.status != "fault") {
                device.status = "fault";
                printf("Device '%s' changed to FAULT\n", device.name.c_str());
            }
        } else {
            // Recover to normal state
            std::string new_status = (device.name.find("Controller") != std::string::npos) ? "operational" : "ok";
            if (device.status != new_status) {
                device.status = new_status;
                printf("Device '%s' recovered to %s\n", device.name.c_str(), new_status.c_str());
            }
        }
    }
    
    // Send status updates to webserver every 5 seconds
    send_status_update(overall_status);
}

 

Server Loop Implementation

 

The main server uses select() to handle both web and backend connections:

 

int main() {
    int backend_server_fd = create_server_socket(12345);  // Backend API
    int web_server_fd = create_server_socket(8080);       // Web interface
    
    while (true) {
        fd_set read_fds;
        FD_ZERO(&read_fds);
        FD_SET(backend_server_fd, &read_fds);
        FD_SET(web_server_fd, &read_fds);
        
        int activity = select(max_fd + 1, &read_fds, NULL, NULL, NULL);
        
        if (FD_ISSET(backend_server_fd, &read_fds)) {
            // Handle new backend connection
            int client_fd = accept(backend_server_fd, ...);
            ClientThreadArgs* args = new ClientThreadArgs();
            args->server_type = BACKEND_SERVER;
            pthread_create(&thread, nullptr, handle_client, args);
        }
        
        if (FD_ISSET(web_server_fd, &read_fds)) {
            // Handle new web connection
            int client_fd = accept(web_server_fd, ...);
            ClientThreadArgs* args = new ClientThreadArgs();
            args->server_type = WEB_SERVER;
            pthread_create(&thread, nullptr, handle_client, args);
        }
    }
}

 

Key Features Summary

 

Ø  Concurrent Processing:

1.    Thread-per-connection with HTTP keep-alive

2.    Fine-grained locking (separate mutexes for data and connections)

3.    Connection tracking and resource management

 

Ø  Real-Time Updates:

1.    AJAX polling every 10 seconds

2.    JSON endpoints for device and system status

3.    No-cache headers for real-time data

 

Ø  Form Processing:

1.    URL-encoded forms for backend API

2.    Multipart forms for web interface

3.    Thread-safe status updates with backend notification

 

Ø  Inter-Process Communication:

1.    TCP socket notifications between webserver and backend monitor

2.    Simple text protocol for status updates

3.    Probabilistic device simulation with fault injection

 

Ø  Error Handling:

1.    Graceful degradation when components fail

2.    Proper resource cleanup and connection management

3.    Comprehensive logging for debugging

 

Conclusion

 

This implementation demonstrates practical C++ network programming with:

Ø  Dual-port architecture for separating web and API traffic

Ø  Multi-threading for concurrent user support

Ø  Real-time updates through AJAX and server-side JSON endpoints

Ø  Form handling for both traditional and modern web interfaces

Ø  Inter-process communication for system integration

 

The code serves as a foundation for building custom web servers that need to handle both human users and automated systems while maintaining real-time data synchronization.

 


Ø Data flow diagrams (generated by Copilot)

 

# Dual-Port Webserver Data Flow Diagram

 

## System Architecture Overview

 

```

┌─────────────────────────────────────────────────────────────────┐

│                    DUAL-PORT WEBSERVER                          │

│                                                                 │

│  ┌─────────────────┐              ┌─────────────────┐           │

│  │   WEB INTERFACE │              │   BACKEND API   │           │

│  │    Port 8080    │              │   Port 12345    │           │

│  └─────────────────┘              └─────────────────┘           │

│                                                                 │

│  ┌─────────────────────────────────────────────────────────┐    │

│  │              SHARED DATA STORE                          │    │

│  │  ThreadContext {                                        │    

│  │    - device_statuses: vector<DeviceStatus>              │    

│  │    - system_status: string                              │    │

│  │    - mutex: pthread_mutex_t                             │    │

│  │  }                                                      │    

│  └─────────────────────────────────────────────────────────┘    

└─────────────────────────────────────────────────────────────────┘

           │                            

           │ TCP Socket (Port 54321)     │ Update statuses

           │ Notifications               │ (Port 12345)

           ▼                             

┌─────────────────────────────────────────────────────────────────┐

│                   BACKEND MONITOR                               │

│                  (backend_monitor.cc)                           │

│  - Receives status updates                                      │

│  - Overrides local status with web updates                      

│  - Maintains device state                                       │

└─────────────────────────────────────────────────────────────────┘

```

 

## Detailed Data Flow

 

### 1. Web Interface Data Flow (Port 8080)

 

```

┌─────────────┐    HTTPGET     ┌─────────────────┐    Read     ┌─────────────┐

│   Browser   │ ─────────────► │  Web Interface  │ ──────────► │   Shared    │

│   (Human    │                │   Port 8080     │             │    Data     │

│   User)     │                │                 │             │   Store     │

└─────────────┘                └─────────────────┘             └─────────────┘

      │                                 │

      │ JavaScript                      │ HTML + JSON

      │ Form Submit                     │ Response

      │ (POST)                          │

      ▼                                 ▼

┌─────────────┐    FormData    ┌─────────────────┐   Update    ┌─────────────┐

│   Browser   │ ─────────────► │  Form Handler   │ ──────────► │   Shared    │

│   (AJAX)    │                │ update_*_web    │             │    Data     │

└─────────────┘                └─────────────────┘             └─────────────┘

                                        │

                                        │ Notification

                                        ▼

                               ┌─────────────────┐

                               │ notify_backend  │

                               │   _monitor()    │

                               └─────────────────┘

```

 

### 2. Backend API Data Flow (Port 12345)

 

```

┌─────────────┐  URL-encoded   ┌─────────────────┐    Write    ┌─────────────┐

│  External   │  Form Data     │  Backend API    │ ──────────► │   Shared    │

│  System     │ ─────────────► │   Port 12345    │             │    Data     │

│ (Monitor)   │     POST       │                 │             │   Store     │

└─────────────┘                └─────────────────┘             └─────────────┘

      ▲                                 │

      │ JSON Response                   │ Success/Error

      │                                 │ Response

      └─────────────────────────────────┘

```

 

### 3. Inter-Process Communication Flow

 

```

┌─────────────────┐   Status Change    ┌─────────────────┐

│   Webserver     │ ─────────────────► │ notify_backend  │

│  (Any Update)   │                    │   _monitor()    │

└─────────────────┘                    └─────────────────┘

                                                │

                                                │ TCP Socket

                                                │ Port 54321

                                                ▼

┌─────────────────────────────────────────────────────────────────┐

│                  BACKEND MONITOR                                │

│                                                                 │

│  ┌─────────────────┐   Parse      ┌───────────────────────┐     │

│  │ Notification    │ ───────────► │ handle_notification() │     │

│  │ Listener        │              │                       │     │

│  │ (Port 54321)    │              │ SYSTEM_STATUS_UPDATE: │     │

│  └─────────────────┘              │ DEVICE:name=status    │     │

│                                   └───────────────────────┘    

│                                                │                │

│                                                ▼                │

│                                   ┌─────────────────┐           │

│                                   │ Update Local    │           │

│                                   │ Status Override │           │

│                                   └─────────────────┘           │

└─────────────────────────────────────────────────────────────────┘

```

 

## Data Structures and Formats

 

### ThreadContext (Shared Data Store)

```cpp

struct ThreadContext {

    vector<DeviceStatus> device_statuses;  // Device name + status pairs

    string system_status;                  // Overall system status

    pthread_mutex_t mutex;                 // Thread synchronization

};

```

 

### Message Formats

 

#### Web Interface (Port 8080)

- **GET /check_status**: Returns JSON `{"status": "value", "timestamp": "..."}`

- **GET /device_status_json**: Returns JSON device array

- **POST /update_system_web**: FormData `system_status=value&source=webpage`

- **POST /update_device_web**: FormData `device_name=name&device_status=status`

 

#### Backend API (Port 12345)

- **POST /update_system**: URL-encoded `system_status=value&Device1=status&...`

- **Response**: Plain text "Success" or error

 

#### Notifications (Port 54321)

- **System Update**: `SYSTEM_STATUS_UPDATE:new_status`

- **Device Update**: `DEVICE:device_name=new_status`

 

## Request Processing Flow

 

```

┌─────────────┐

│ HTTP Request│

└──────┬──────┘

       │

       ▼

┌─────────────────┐

│ parse_request() │ ──► Method, Path, Headers, Body

└─────────────────┘

       │

       ▼

┌─────────────────┐

│ Route Handler   │

│ - GET /         │ ──► HTML Generation

│ - GET /status   │ ──► JSON Response  

│ - POST /update  │ ──► Form Processing

└─────────────────┘

       │

       ▼

┌─────────────────┐

│ Data Access     │ ──► pthread_mutex_lock()

│ (Thread Safe)   │     Read/Write shared data

└─────────────────┘     pthread_mutex_unlock()

       │

       ▼

┌─────────────────┐

│ HTTP Response   │ ──► Status Code, Headers, Body

└─────────────────┘

       │

       ▼

┌─────────────────┐

│ Notification    │ ──► notify_backend_monitor()

│ (if update)     │     TCP Socket to Port 54321

└─────────────────┘

```

 

## Web Page Refresh Mechanism (Real-time Updates)

 

```

┌─────────────────────────────────────────────────────────────────┐

│                    BROWSER (Web Page)                           │

│                                                                 │

│  ┌─────────────────┐     10 Second Timer     ┌─────────────────┐│

│  │   JavaScript    │ ◄─────────────────────► │  setInterval()  ││

│  │  refreshAll()   │                         │   (10000ms)     ││

│  └─────────────────┘                         └─────────────────┘│

│           │                                                     │

│           ▼                                                     │

│  ┌─────────────────┐                                            │

│  │ Parallel AJAX   │                                            │

│  │    Requests     │                                            │

│  └─────────────────┘                                            │

└─────────────────────────────────────────────────────────────────┘

           │

           ▼

┌─────────────────────────────────────────────────────────────────┐

│  AJAX Request 1: GET /device_status_json                        │

│  ├─► fetch('/device_status_json')                               │

│  ├─► Returns: {"devices":[{"name":"Device1","status":"ok"}]}    │

│  └─► Updates: Device table in HTML                              │

└─────────────────────────────────────────────────────────────────┘

           │

           │ (Parallel)

           ▼

┌─────────────────────────────────────────────────────────────────┐

│  AJAX Request 2: GET /check_status                              

│  ├─► fetch('/check_status')                                     │

│  ├─► Returns: {"status":"Operational","timestamp":"..."}        

│  └─► Updates: System status display                             │

└─────────────────────────────────────────────────────────────────┘

           │

           ▼

┌─────────────────────────────────────────────────────────────────┐

│                    WEBSERVER (Port 8080)                        

│                                                                 │

│  ┌─────────────────┐   mutex_lock()   ┌─────────────────┐       │

│  │ GET Handler     │ ───────────────► │   Shared Data   │       │

│  │ /device_status  │                  │     Store       │       │

│  │ /check_status   │ ◄─────────────── │  (Read Only)    │       │

│  └─────────────────┘   mutex_unlock() └─────────────────┘       │

└─────────────────────────────────────────────────────────────────┘

```

 

### Refresh Flow Sequence

 

```

Time: 0s, 10s, 20s, 30s...

      │

      ▼

┌─────────────────┐

│ Timer Triggers  │ ──► refreshAll() called

└─────────────────┘

      │

      ▼

┌─────────────────┐

│ Spawn 2 AJAX    │ ──► fetch('/device_status_json')

│ Requests        │     fetch('/check_status')

└─────────────────┘

      │

      ▼

┌─────────────────┐

│ Process         │ ──► Update device table

│ Responses       │     Update system status

└─────────────────┘     Update timestamp

      │

      ▼

┌─────────────────┐

│ DOM Updates     │ ──► Visual refresh complete

│ Complete        │     Wait for next 10s timer

└─────────────────┘

```

 

### JavaScript Refresh Implementation

 

```javascript

// 10-second refresh timer

function startRefreshTimer() {

  setInterval(function() {

    refreshAll();  // Called every 10 seconds

  }, 10000);

}

 

// Parallel data fetching

function refreshAll() {

  refreshDevices();  // Includes both device and status calls

}

 

function refreshDevices() {

  // Request 1: Device status

  fetch('/device_status_json')

    .then(response => response.json())

    .then(data => updateDeviceTable(data));

   

  // Request 2: System status (parallel)

  fetch('/check_status')

    .then(response => response.json())

    .then(data => updateSystemStatus(data));

}

```

 

## Synchronization and Concurrency

 

```

┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐

│   Web Thread    │    │ Backend Thread  │    │  Notify Thread  │

│   (Port 8080)   │    │  (Port 12345)   │    │  (Port 54321)   │

└─────────────────┘    └─────────────────┘    └─────────────────┘

         │                       │                       │

         │ mutex_lock()          │ mutex_lock()          │ (no lock)

         ▼                       ▼                       │

┌─────────────────────────────────────────────────────────────────┐

│                    SHARED DATA STORE                            │

│  ┌─────────────────────────────────────────────────────────┐    │

│  │  device_statuses, system_status (protected by mutex)    │    

│  └─────────────────────────────────────────────────────────┘    

└─────────────────────────────────────────────────────────────────┘

```

 

This diagram shows how the dual-port webserver manages data flow between web users, external systems, and the backend monitor while maintaining thread safety and real-time updates.

 


Ø Analysis of RESTful API Usage in the System (Generated by DeepSeek)

1. Web Server (web_server.cc) - Partial RESTful Implementation

The web server implements REST-like principles but not a full RESTful API:

RESTful Characteristics Present:

// Resource-based endpoints
GET /check_status        → Returns system status JSON
GET /device_status_json  → Returns device list JSON

 

// Client-side usage (REST pattern)
fetch('/device_status_json')  // Resource retrieval via GET
  .then(r => r.json())

Non-RESTful Elements:

// Action-oriented endpoints
POST /update_system_web   // RPC-style (update system)
POST /update_device_web   // RPC-style (update device)

·            Uses verbs in URIs (update_*) instead of noun-based resources

·            Lacks proper HTTP methods for state changes (no PUT/PATCH/DELETE)

·            No HATEOAS (hypermedia controls)

 

2. Backend Monitor (backend_monitor.cc) - Non-RESTful

Implements custom protocols, not REST:

// HTTP Client (not server)
void send_status_update() {
  std::string http_request = "POST /update_system HTTP/1.1\r\n";  // RPC-style call
  send(sock, http_request.c_str(), ...);
}
 
// TCP Server (custom protocol)
void handle_notification() {
  // Parses raw text commands:
  // "SYSTEM_STATUS_UPDATE:Maintenance"
  // "DEVICE:Storage Unit=offline"
}

Key API Endpoints Summary

Endpoint (Web Server)

HTTP Method

REST Compliance

Payload Format

/update_system (backend)

POST

RPC-style

x-www-form-urlencoded

/check_status

GET

Resource

JSON

/device_status_json

GET

Resource

JSON

/update_system_web

POST

Verb-based

Multipart form

/update_device_web

POST

Verb-based

Multipart form

RESTful Gaps in the System

1.      Resource Identification

o              No unique URIs per device (e.g., /devices/{id})

o              Missing standard collection endpoints (/devices)

2.      HTTP Method Semantics

o              Status updates use POST instead of PUT/PATCH

o              No DELETE operations

3.      Hypermedia Controls

o              Responses don't contain links to related resources

o              No content negotiation (only JSON)

4.      Statelessness

o              Shared server context breaks stateless constraint

o              Relies on mutex-protected memory

Why This Hybrid Approach Works

Despite not being fully RESTful, the system achieves efficiency through:

1.      Specialized Protocols

o              Raw TCP for critical notifications (lower overhead)

// backend_monitor.cc
send(notify_sock, "DEVICE:Storage Unit=offline\n", ...);  // 28 bytes

vs REST alternative:

PATCH /devices/storage-unit HTTP/1.1
Content-Type: application/json
Content-Length: 25
 
{"status":"offline"}
```  // ~100+ bytes

2.      Optimized for Monitoring Workload

o              Few resource types (devices/system status)

o              Simple CRUD-like operations

o              High read-to-write ratio (10:1)

3.      Embedded System Constraints

o              Avoids heavy REST frameworks

o              Minimizes parsing complexity

o              Reduces memory allocations

Recommendations for RESTful Enhancement

To evolve toward RESTful compliance:

// 1. Resource-oriented URIs
GET /api/devices        // List all devices
PUT /api/devices/{id}   // Update specific device
 
// 2. Proper HTTP methods
// Before (current)
POST /update_device_web → {"name":"Device1", "status":"offline"}
 
// After (RESTful)
PUT /api/devices/Device1 → {"status":"offline"}
 
// 3. HATEOAS support
{
  "name": "Device1",
  "status": "offline",
  "_links": {
    "self": "/api/devices/Device1",
    "system": "/api/system"
  }
}

Conclusion

The system uses a hybrid approach:

·            Web server: Implements REST-like patterns for data retrieval (GET /check_status), but uses RPC-style endpoints for updates

·            Backend: Uses custom protocols (HTTP client + TCP server) without REST principles

This design prioritizes lightweight efficiency over RESTful purity, making appropriate tradeoffs for an embedded monitoring system. For expansion to broader APIs, adopting full REST principles would improve discoverability and standardization.

Designing a Lightweight Web Server for Real-Time System Monitoring in C++

This article walks through the design and implementation of a lightweight dual-port web server that can serve a system monitoring dashboard ...