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

Linux下C语言Socket服务端共享内存键值存储实现相关问题

实现建议

问题1:指令拆分与存储逻辑改造方案

你现有Input.c里的逻辑可以直接迁移到服务端通信流程中,仅需做3处适配调整:

  • 把原逻辑中从标准输入gets()读数据的逻辑,替换为从socket连接句柄cfd读取客户端输入,注意要先去除Telnet传输附带的换行符\r\n/\n,避免strtok拆分指令时携带多余无效字符
  • 把原逻辑中局部定义的KeyStorage数组调整为全局存储或共享内存存储:原局部变量仅能在单连接单进程场景下生效,多连接场景下数据会丢失,你可以直接用POSIX共享内存接口shm_open+mmap实现跨进程共享;如果你的服务端暂时用单进程单线程模型,先改成全局变量也能跑通,后续再迭代为共享内存实现即可
  • 新增指令合法性校验逻辑:比如GET/DEL命令不需要第三个value参数,PUT必须同时传key和value,避免参数缺失导致的数组越界访问

问题2:GET/PUT/DEL函数调用时机

你理解的没错,就是在server.c读取到客户端输入之后、返回响应输出之前调用,完整执行顺序为:

  1. 从socket读取客户端发送的一行指令
  2. 用strtok拆分指令、校验参数合法性
  3. 根据指令类型调用对应的get/put/del函数,操作共享内存的keyValueStore
  4. 把函数执行结果按照要求的格式拼装为响应字符串
  5. 把响应字符串通过socket写回给客户端

可直接运行的改造后代码示例

整合后的server.c代码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define BUFSIZE 1024
#define PORT 5678
#define MAX_ARRAY 100
#define MAX_KV 100

// 键值对存储结构,后续可直接替换为共享内存实现
typedef struct Value_ {
    char key[MAX_ARRAY];
    char value[MAX_ARRAY];
} KeyStorage;
KeyStorage storageKey[MAX_KV];
int kv_count = 0;

// 三个操作函数实现
char* put(char* key, char* value) {
    // 先查找是否存在已有key,存在则更新
    for(int i=0; i<kv_count; i++) {
        if(strcmp(storageKey[i].key, key) == 0) {
            strcpy(storageKey[i].value, value);
            static char res[BUFSIZE];
            snprintf(res, BUFSIZE, "PUT:%s:%s\r\n", key, value);
            return res;
        }
    }
    // 不存在则新增
    if(kv_count < MAX_KV) {
        strcpy(storageKey[kv_count].key, key);
        strcpy(storageKey[kv_count].value, value);
        kv_count++;
        static char res[BUFSIZE];
        snprintf(res, BUFSIZE, "PUT:%s:%s\r\n", key, value);
        return res;
    }
    return "PUT:error:storage_full\r\n";
}

char* get(char* key) {
    for(int i=0; i<kv_count; i++) {
        if(strcmp(storageKey[i].key, key) == 0) {
            static char res[BUFSIZE];
            snprintf(res, BUFSIZE, "GET:%s:%s\r\n", key, storageKey[i].value);
            return res;
        }
    }
    static char res[BUFSIZE];
    snprintf(res, BUFSIZE, "GET:%s:key_nonexistent\r\n", key);
    return res;
}

char* del(char* key) {
    for(int i=0; i<kv_count; i++) {
        if(strcmp(storageKey[i].key, key) == 0) {
            // 把最后一个元素挪到当前位置,降低移动开销
            if(i != kv_count -1) {
                memcpy(&storageKey[i], &storageKey[kv_count-1], sizeof(KeyStorage));
            }
            kv_count--;
            static char res[BUFSIZE];
            snprintf(res, BUFSIZE, "DEL:%s:key_deleted\r\n", key);
            return res;
        }
    }
    static char res[BUFSIZE];
    snprintf(res, BUFSIZE, "DEL:%s:key_nonexistent\r\n", key);
    return res;
}

int main() {
    int rfd, cfd;
    struct sockaddr_in client, server;
    socklen_t client_len;
    char in[BUFSIZE]; 
    int bytes_read; 

    rfd = socket(AF_INET, SOCK_STREAM, 0);
    if (rfd < 0 ){
        perror("socket error");
        exit(-1);
    }

    memset(&server, '\0', sizeof(server));
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = INADDR_ANY;
    server.sin_port = htons(PORT);

    int brt = bind(rfd, (struct sockaddr *) &server, sizeof(server));
    if (brt < 0 ){
        perror("bind error");
        exit(-1);
    }

    int lrt = listen(rfd, 5);
    if (lrt < 0 ){
        perror("listen error");
        exit(-1);
    }

    while (1) {
        client_len = sizeof(client);
        cfd = accept(rfd, (struct sockaddr *) &client, &client_len);
        if(cfd <0) {
            perror("accept error");
            continue;
        }

        memset(in, 0, BUFSIZE);
        bytes_read = read(cfd, in, BUFSIZE-1);
        while (bytes_read > 0) {
            // 去除Telnet传输的换行符
            char *p = strstr(in, "\r\n");
            if(p) *p = '\0';
            p = strstr(in, "\n");
            if(p) *p = '\0';

            // 拆分指令
            char *argv[3] = {0};
            int i=0;
            char *token = strtok(in, " ");
            while(token != NULL && i<3) {
                argv[i++] = token;
                token = strtok(NULL, " ");
            }

            char *resp = "ERROR:invalid_command\r\n";
            if(argv[0] != NULL) {
                if(strcmp(argv[0], "PUT") ==0 && argv[1] != NULL && argv[2] != NULL) {
                    resp = put(argv[1], argv[2]);
                } else if(strcmp(argv[0], "GET") ==0 && argv[1] != NULL) {
                    resp = get(argv[1]);
                } else if(strcmp(argv[0], "DEL") ==0 && argv[1] != NULL) {
                    resp = del(argv[1]);
                }
            }

            write(cfd, resp, strlen(resp));
            memset(in, 0, BUFSIZE);
            bytes_read = read(cfd, in, BUFSIZE-1);
        }

        close(cfd);
    }
    close(rfd);
    return 0;
}

共享内存改造说明

如果需要切换为共享内存存储,仅需要把全局的storageKey数组替换为共享内存映射的地址即可,核心步骤为:

  1. 服务端启动时调用shm_open创建共享内存对象,用ftruncate设置大小为MAX_KV * sizeof(KeyStorage)
  2. 调用mmap把共享内存映射到进程地址空间,替换原来的storageKey数组
  3. 可以加互斥锁避免多连接同时操作共享内存出现竞态问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.14 20:54:05