You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Sun RPC的聊天系统离线用户消息存储实现方法问询

Hey there! Let's tackle how to implement offline message storage for your Sun RPC-based chat system on Ubuntu. Here's a practical, step-by-step breakdown aligned with your workflow:


1. Extend Your XDR Definition First

You'll need to add structures and RPC calls to handle offline messages. Update your .x file (e.g., chat.x) with these additions:

/* chat.x */
struct Message {
    string sender<>;
    string receiver<>;
    string content<>;
    unsigned int timestamp; // Track when the message was sent
};

// RPC call to store a message for an offline user
bool store_offline_msg(Message msg);

// RPC call to fetch all offline messages when a user logs in
struct MessageList {
    unsigned int count;
    Message messages<>;
};
MessageList fetch_offline_msgs(string username);

// Optional: Add a call to check if a user is online
bool is_user_online(string username);

2. Generate Code with rpcgen

Run this command in your Ubuntu terminal to generate the base RPC code:

rpcgen chat.x

This will create chat.h, chat_clnt.c, chat_svc.c, and chat_xdr.c — we'll modify the server-side chat_svc.c to implement the offline logic.


3. Implement Offline Message Storage on the Server

You have two main options for storage: in-memory (simple, but loses data on server restart) or persistent (e.g., SQLite, for durability). Let's cover both.

Option 1: In-Memory Storage (Fast, Simple)

Use a thread-safe hash table (we'll use GLib's GHashTable for this) to map usernames to their message lists.

Setup Dependencies

First, install GLib on Ubuntu:

sudo apt-get install libglib2.0-dev

Server-Side Code Modifications

Add this to chat_svc.c:

#include <pthread.h>
#include <glib.h>
#include <time.h>
#include <string.h>
#include <stdlib.h>

// Thread-safe hash table: key = username, value = GList of Message*
static GHashTable *offline_msg_store;
static pthread_mutex_t msg_mutex = PTHREAD_MUTEX_INITIALIZER;

// Initialize storage when the server starts
void init_offline_storage() {
    offline_msg_store = g_hash_table_new(g_str_hash, g_str_equal);
}

// Helper to free Message structs
void free_message(Message *msg) {
    free(msg->sender);
    free(msg->receiver);
    free(msg->content);
    free(msg);
}

// Implement store_offline_msg RPC
bool *store_offline_msg_1_svc(Message *msg, struct svc_req *rqstp) {
    static bool success = FALSE;
    pthread_mutex_lock(&msg_mutex);

    // Create or fetch the receiver's message list
    GList *msg_list = g_hash_table_lookup(offline_msg_store, msg->receiver);
    if (!msg_list) msg_list = g_list_new();

    // Copy the message (XDR data is temporary)
    Message *new_msg = malloc(sizeof(Message));
    *new_msg = *msg;
    new_msg->sender = strdup(msg->sender);
    new_msg->receiver = strdup(msg->receiver);
    new_msg->content = strdup(msg->content);
    new_msg->timestamp = time(NULL);

    // Add to list and update hash table
    msg_list = g_list_append(msg_list, new_msg);
    g_hash_table_insert(offline_msg_store, strdup(msg->receiver), msg_list);

    success = TRUE;
    pthread_mutex_unlock(&msg_mutex);
    return &success;
}

// Implement fetch_offline_msgs RPC
MessageList *fetch_offline_msgs_1_svc(char **username, struct svc_req *rqstp) {
    static MessageList result;
    result.count = 0;
    result.messages = NULL;

    pthread_mutex_lock(&msg_mutex);
    GList *msg_list = g_hash_table_lookup(offline_msg_store, *username);

    if (msg_list) {
        // Populate the result struct
        result.count = g_list_length(msg_list);
        result.messages = malloc(sizeof(Message) * result.count);
        int i = 0;
        for (GList *iter = msg_list; iter; iter = iter->next) {
            Message *src = (Message*)iter->data;
            result.messages[i] = *src;
            result.messages[i].sender = strdup(src->sender);
            result.messages[i].receiver = strdup(src->receiver);
            result.messages[i].content = strdup(src->content);
            i++;
        }

        // Clean up: remove messages from storage after fetch
        g_hash_table_remove(offline_msg_store, *username);
        g_list_free_full(msg_list, (GDestroyNotify)free_message);
    }

    pthread_mutex_unlock(&msg_mutex);
    return &result;
}

Don't forget to call init_offline_storage() at the start of your server main function.

Option 2: Persistent Storage with SQLite (Durable)

If you want messages to survive server restarts, use SQLite.

Setup Dependencies

Install SQLite on Ubuntu:

sudo apt-get install libsqlite3-dev

Server-Side Code Modifications

Add this to chat_svc.c:

#include <sqlite3.h>
#include <time.h>
#include <string.h>
#include <stdlib.h>

sqlite3 *db;

// Initialize database on server start
void init_db() {
    int rc = sqlite3_open("chat_offline.db", &db);
    if (rc != SQLITE_OK) {
        fprintf(stderr, "Failed to open DB: %s\n", sqlite3_errmsg(db));
        exit(1);
    }

    // Create messages table if it doesn't exist
    const char *create_table = "CREATE TABLE IF NOT EXISTS offline_msgs ("
                                "id INTEGER PRIMARY KEY AUTOINCREMENT,"
                                "sender TEXT NOT NULL,"
                                "receiver TEXT NOT NULL,"
                                "content TEXT NOT NULL,"
                                "timestamp INTEGER NOT NULL);";
    char *err_msg;
    rc = sqlite3_exec(db, create_table, NULL, 0, &err_msg);
    if (rc != SQLITE_OK) {
        fprintf(stderr, "SQL Error: %s\n", err_msg);
        sqlite3_free(err_msg);
        sqlite3_close(db);
        exit(1);
    }
}

// Callback for SQLite fetch queries
static int fetch_msg_callback(void *data, int argc, char **argv, char **azColName) {
    MessageList *result = (MessageList*)data;
    result->messages = realloc(result->messages, sizeof(Message) * (result->count + 1));
    Message *msg = &result->messages[result->count];
    msg->sender = strdup(argv[1]);
    msg->receiver = strdup(argv[2]);
    msg->content = strdup(argv[3]);
    msg->timestamp = atoi(argv[4]);
    result->count++;
    return 0;
}

// Implement store_offline_msg RPC
bool *store_offline_msg_1_svc(Message *msg, struct svc_req *rqstp) {
    static bool success = FALSE;
    char insert_sql[512];
    snprintf(insert_sql, sizeof(insert_sql),
             "INSERT INTO offline_msgs (sender, receiver, content, timestamp) "
             "VALUES ('%s', '%s', '%s', %u);",
             msg->sender, msg->receiver, msg->content, (unsigned int)time(NULL));

    char *err_msg;
    int rc = sqlite3_exec(db, insert_sql, NULL, 0, &err_msg);
    if (rc == SQLITE_OK) success = TRUE;
    else fprintf(stderr, "SQL Error: %s\n", err_msg);
    sqlite3_free(err_msg);
    return &success;
}

// Implement fetch_offline_msgs RPC
MessageList *fetch_offline_msgs_1_svc(char **username, struct svc_req *rqstp) {
    static MessageList result;
    result.count = 0;
    result.messages = NULL;

    // Fetch messages
    char select_sql[256];
    snprintf(select_sql, sizeof(select_sql),
             "SELECT * FROM offline_msgs WHERE receiver = '%s';", *username);
    char *err_msg;
    int rc = sqlite3_exec(db, select_sql, fetch_msg_callback, &result, &err_msg);
    if (rc != SQLITE_OK) fprintf(stderr, "SQL Error: %s\n", err_msg);
    sqlite3_free(err_msg);

    // Delete fetched messages
    char delete_sql[256];
    snprintf(delete_sql, sizeof(delete_sql),
             "DELETE FROM offline_msgs WHERE receiver = '%s';", *username);
    sqlite3_exec(db, delete_sql, NULL, 0, &err_msg);
    sqlite3_free(err_msg);

    return &result;
}

Call init_db() at the start of your server main function.


4. Adjust Client Logic
  • When sending a message: First check if the receiver is online via the is_user_online RPC. If they're offline, call store_offline_msg instead of sending a real-time message.
  • When a user logs in: Immediately call fetch_offline_msgs to retrieve and display all stored messages.

Example client code snippet for sending messages:

void send_msg(char *sender, char *receiver, char *content) {
    CLIENT *clnt = clnt_create("your_server_ip", CHAT_PROG, CHAT_VERS, "tcp");
    if (!clnt) { clnt_pcreateerror("your_server_ip"); exit(1); }

    bool *is_online = is_user_online_1(&receiver, clnt);
    if (*is_online) {
        // Send real-time message (your existing logic)
    } else {
        Message msg = {sender, receiver, content, 0};
        bool *stored = store_offline_msg_1(&msg, clnt);
        printf(stored ? "Message stored for offline user\n" : "Failed to store message\n");
    }

    clnt_destroy(clnt);
}

5. Compile & Run (Ubuntu)
  • For in-memory storage:
    gcc chat_svc.c chat_xdr.c -o chat_server -lrpcsvc -lglib-2.0 -pthread
    gcc chat_clnt.c chat_xdr.c client.c -o chat_client -lrpcsvc
    
  • For SQLite storage:
    gcc chat_svc.c chat_xdr.c -o chat_server -lrpcsvc -lsqlite3
    gcc chat_clnt.c chat_xdr.c client.c -o chat_client -lrpcsvc
    
  • Start rpcbind if it's not running:
    sudo systemctl start rpcbind
    
  • Launch the server first, then run clients.

Hope this helps you get the offline message feature up and running smoothly! If you hit snags with concurrency or XDR serialization, feel free to share more details.

内容的提问来源于stack exchange,提问作者grakatine97

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 06:57:24