用C语言搓一个小型的服务器,拥有路由解析器(支持MVC架构)

C/C++
173
0
0
2024-03-18

用C语言搓一个小型的服务器,拥有路由解析器(支持MVC架构)

架构讲解

最近做学校专周,用C语言和RIO搓一个Tiny服务器,本身没啥难度,但是是让你返回一个页面。

对于特别习惯前后端分离开发的我来说,头疼,还是给json吧,前端html自己接收。

要求我们实现登录和注册,然后大概的方式是前端对tiny进行请求,tiny进行路由解析后,通过fork创建新的进程,再通过execve(filename, argv, envp)进行一个cgi执行,使用setenv来进行程序上下文的传递

void serve_dynamic(int fd, const char *filename, const char *cgiargs)
{
    char buf[MAXLINE], *emptylist[] = {NULL};

    /* Return first part of HTTP response */
    sprintf(buf, "HTTP/1.0 200 OK\r\n");
    Rio_writen(fd, buf, strlen(buf));
    sprintf(buf, "Server: Tiny Web Server\r\n");
    Rio_writen(fd, buf, strlen(buf));

    if (Fork() == 0)
    { /* Child */ // line:netp:servedynamic:fork
        /* Real server would set all CGI vars here */
        setenv("QUERY_STRING", cgiargs, 1);                         // line:netp:servedynamic:setenv
        Dup2(fd, STDOUT_FILENO); /* Redirect stdout to client */    // line:netp:servedynamic:dup2
        Execve(filename, emptylist, environ); /* Run CGI program */ // line:netp:servedynamic:execve
    }
    Wait(NULL); /* Parent waits for and reaps child */ // line:netp:servedynamic:wait
}

img

添加描述

但是,按照原来的思路,我要写很多个业务处理程序,很麻烦,有没有简单一点的?

有!那不就是Spring Boot和Spring MVC吗?

img

添加描述

但是不能用框架,vocal,那怎么办?

没有环境,咱们就创建环境,没有条件,咱们就创建条件!

来说说思路,我们现在在tiny层重写一个路由解析,相当于把tiny服务器当作一个网关,把请求的内容按照我们的约定来重新封装,再通过setenv进行路由信息传递,原来是传参数,那么我们就要改,改为“METHOD URL/?PARAM”的形式

上代码

void serve_dynamic(int fd, const char *filename, const char *cgiargs, char uri[8192])
{
    char buf[MAXLINE], *emptylist[] = {NULL};
    char *url=strdup(uri);
    if(strlen(cgiargs)){
        strcat(url,"?");
        strcat(url,cgiargs);
    }
    printf("【serve_dynamic-Fork】uri写入环境变量:%s\n",url);

    /* Return first part of HTTP response */
    sprintf(buf, "HTTP/1.0 200 OK\r\n");
    Rio_writen(fd, buf, strlen(buf));
    sprintf(buf, "Server: Tiny Web Server\r\n");
    Rio_writen(fd, buf, strlen(buf));
//#if usingFork == 1
    printf("【serve_dynamic】尝试打开进程:%s 传入子进程信息如下:%s\r\n",filename,uri);
    if (Fork() == 0)
    { /* Child */ // line:netp:servedynamic:fork
        /* Real server would set all CGI vars here */
        setenv("QUERY_STRING", url, 1);                         // line:netp:servedynamic:setenv
        char *buff=getenv("QUERY_STRING");
        /* Extract the two arguments */
        printf("【serve_dynamic-Fork】进程%s打开成功!\t环境变量取出尝试:%s\r\n\r\n",filename,buff);
        Dup2(fd, STDOUT_FILENO); /* Redirect stdout to client */    // line:netp:servedynamic:dup2
        Execve(filename, emptylist, environ); /* Run CGI program */ // line:netp:servedynamic:execve
//        free(url);
    }
    Wait(NULL); /* Parent waits for and reaps child */ // line:netp:servedynamic:wait
//    free(url);
}

url我们在之前拼接,给你们看看doit-请求处理的代码

void doit(int fd)
{
    int is_static;      // 判断访问的资源是否为静态资源
    struct stat sbuf;   // todo:
    char buf[MAXLINE], method[MAXLINE], uri[MAXLINE], version[MAXLINE];
    char filename[MAXLINE], cgiargs[MAXLINE];
    rio_t rio;

    /* Read request line and headers */
    Rio_readinitb(&rio, fd);
    if (!Rio_readlineb(&rio, buf, MAXLINE)) // line:netp:doit:readrequest
        return;
    printf("%s", buf);
    sscanf(buf, "%s %s %s", method, uri, version); // line:netp:doit:parserequest
    // 如果不是get请求,就拒绝,客户端报异常
    printf("ParseURI:%s\n", uri);
    if (strcasecmp(method, "GET")&&strcasecmp(method, "POST"))
    { // line:netp:doit:beginrequesterr
        clienterror(fd, method, "501", "Not Implemented",
                    "Tiny does not implement this method");
        return;
    }                       // line:netp:doit:endrequesterr`
    read_requesthdrs(&rio); // line:netp:doit:readrequesthdrs

    /* Parse URI from GET request */
    is_static = parse_uri(uri, filename, cgiargs); // line:netp:doit:staticcheck

    // 如果文件读取失败,客户端报错
    if (stat(filename, &sbuf) < 0 && is_static)
    { // line:netp:doit:beginnotfound
        printf("文件名:%s\n",filename);
        clienterror(fd, filename, "404", "Not found",
                    "Tiny couldn't find this file");
        return;
    } // line:netp:doit:endnotfound

    if (is_static)
    { /* Serve static content */
        if (!(S_ISREG(sbuf.st_mode)) || !(S_IRUSR & sbuf.st_mode))
        { // line:netp:doit:readable
            clienterror(fd, filename, "403", "Forbidden",
                        "Tiny couldn't read the file");
            return;
        }
        printf("访问静态文件:%s",filename);
        // 如果访问的是静态文件,那么对静态文件进行响应的处理
        serve_static(fd, filename, sbuf.st_size); // line:netp:doit:servestatic
    }
    else
    { /* Serve dynamic content */
        strcpy(filename,"./api/cgin.cgi");
        if(stat(filename, &sbuf) < 0){
            clienterror(fd, filename, "403", "Forbidden",
                        "CGIN框架加载失败");
            return;
        }
        if (!(S_ISREG(sbuf.st_mode)) || !(S_IXUSR & sbuf.st_mode))
        { // line:netp:doit:executable
            clienterror(fd, filename, "403", "Forbidden",
                        "Tiny couldn't run the CGI program");
            return;
        }
        char urlmsg[8910]="";
        strcat(urlmsg,method);
        strcat(urlmsg," ");
        strcat(urlmsg,uri);
        // 对交互操作进行响应
        serve_dynamic(fd, filename, cgiargs, urlmsg); // line:netp:doit:servedynamic
    }
}

那么业务层我们怎么做?

我们可以用哈希来进行映射,C语言哈希怎么办?手搓!一个路由对应一个函数

哈希实现

//
// Created by 30398 on 2023/12/30.
//

#ifndef  C_HASH_TABLE_H
#define  C_HASH_TABLE_H
//使用C语言编写个哈希表
#include <stdio.h>
#include "RequestContext.h"
#include "Response.h"
#define DEFAULT_RouterTable_SIZE 1<<5
int NotFound(RequestContext* requestContext){
    return 404;
}
typedef struct {
    char* key;
    ResponseData* (*run)(RequestContext* requestContext);
    struct Entry* nextEntry;
} Entry;
const Entry NOTFOUND_ENTRY={
        "404",
        NotFound,
        NULL
};
typedef struct {
    Entry** slots;
    int     size;   //
    int     count;
}RouterTable;
int isNULLEntry(Entry* entry){
    if(entry==NULL) return 1;
    if(entry->key==NULL) return 1;
    return 0;
}
RouterTable* createRouterTable(int size){
#ifdef DEBUG
    printf("[MAIN-Hash]哈希表创建中\n");
#endif
    RouterTable* RouterTable=malloc(sizeof(RouterTable));
    RouterTable->count=0;
    RouterTable->size=size;
#ifdef DEBUG
    printf("[MAIN-Hash]slots创建中\n");
#endif
    RouterTable->slots= malloc(sizeof(Entry*)*size);
#ifdef DEBUG
    printf("[MAIN-Hash]正在对每一个slot进行初始化\n");
#endif
    for (int i = 0; i < size; ++i) {
        RouterTable->slots[i]= malloc(sizeof (Entry));
        RouterTable->slots[i]->key=NULL;
        RouterTable->slots[i]->nextEntry=NULL;
        RouterTable->slots[i]->run=NULL;
    }
#ifdef DEBUG
    printf("[MAIN]哈希对象创建成功!\n");
#endif
    return RouterTable;
}
void freeRouterTable(RouterTable* RouterTable){
#ifdef DEBUG
    printf("[freeRouterTable]RouterTable.slots Begin to %d\r\n", RouterTable->size);
#endif
    for (int i = 0; i < RouterTable->size; ++i) {
#ifdef DEBUG
        printf("[freeRouterTable]RouterTable.slots[%d]\r\n",i);
#endif
        Entry* entry=RouterTable->slots[i];
        while (entry!=NULL){
            Entry* nextEntry=entry->nextEntry;
            entry->key=NULL;
            entry->run=NULL;
            free(entry);
            entry=nextEntry;
        }
    }
    free(RouterTable->slots);
    free(RouterTable);
}
RouterTable* createDefaultRouterTable(){
#ifdef DEBUG
    puts("创建默认哈希对象中...\n");
#endif
    return createRouterTable(DEFAULT_RouterTable_SIZE);
}
// 计算在哈希表中的slot位置
long getHashSlotByKEY(RouterTable *RouterTable,char* key){
    long long h=0;
    for(char *s=key;*s!='\0';s++){
        // printf("h=5*%d+%d\n",h,(int)*s);
        h=5*h+(*s);
    }
    long long slot=(long long)h&((RouterTable->size)-1);
    // printf("h:%d size:%d slot: %d\n",h,RouterTable->size,slot);
    return slot;
}
long getHashSlot(RouterTable *RouterTable,Entry* entry){
    char* key = entry->key;
    return getHashSlotByKEY(RouterTable,key);
}
void entryCpy(Entry* old , Entry* current){
    current->key=old->key;
    current->run=old->run;
    current->nextEntry=old->nextEntry;
}
void rehash(RouterTable* RouterTable){
    if(RouterTable->count*4<RouterTable->size*3) return;
    Entry** oldSlots=RouterTable->slots;
    int oldSize=RouterTable->count;
    RouterTable->size=oldSize<<1;
    RouterTable->count=0;
    Entry** newSlots = RouterTable->slots=malloc(RouterTable->size*sizeof(Entry*));
    for(int i=0;i<oldSize;i++){
        Entry* entry=oldSlots[i];
        while(!isNULLEntry(entry)){
            long hashcode= getHashSlot(RouterTable,entry);
            Entry* slot=newSlots[hashcode];
            while(!isNULLEntry(slot->nextEntry)){
                slot=slot->nextEntry;
            }
            Entry * nxt = slot->nextEntry= malloc(sizeof(Entry*));
            entryCpy(entry,nxt);
            nxt->nextEntry=NULL;
            entry=entry->nextEntry;
        }
    }

    free(oldSlots);
}

void putEntry(RouterTable* RouterTable,Entry* entry){
    rehash(RouterTable);
    RouterTable->count++;
    entry->nextEntry=NULL;
    long slotCode=getHashSlot(RouterTable,entry);
    Entry ** slots =RouterTable->slots;
    Entry* slot=slots[slotCode];
    if(isNULLEntry(slot)){
        slots[slotCode]=slot=malloc(sizeof(Entry*));
        entryCpy(entry,slot);
        entryCpy(slot,slots[slotCode]);
        return;
    }
    while(!isNULLEntry(slot->nextEntry)){
        slot=slot->nextEntry;
    }
    slot->nextEntry=malloc(sizeof(Entry*));
    entryCpy(entry,slot->nextEntry);
}
void addRoute(RouterTable* RouterTable,char* key,int(*run)(RequestContext* requestContext)){
    Entry* entry=malloc(sizeof(Entry));
    entry->key = key;
    entry->run=run;
    entry->nextEntry=NULL;
    putEntry(RouterTable,entry);
}
Entry runRoute(RouterTable* RouterTable,char* key){
#ifdef DEBUG
    printf("[runRoute]路由%s进入哈希表查找",key);
#endif
    long slotCode = getHashSlotByKEY(RouterTable,key);
    Entry* slot = RouterTable->slots[slotCode];
    if(!isNULLEntry(slot)&&!strcmp(slot->key,key)){
#ifdef DEBUG
        printf("[runRoute]路由%s在slot",key);
#endif
        return *slot;
    }
    while(!isNULLEntry(slot->nextEntry)){
        slot=slot->nextEntry;
        if(!strcmp(slot->key,key)){
#ifdef DEBUG
            printf("[runRoute]路由%s存在",key);
#endif
            return *slot;
        }
    }
#ifdef DEBUG
    printf("[runRoute]路由%s不存在",key);
#endif
    return NOTFOUND_ENTRY;
}
#endif

由于是新的进程,所以我直接用哈希,如果是直接面向业务层来进行的请求的话,那么我建议这里可以做个渐进式哈希,便于后面实现通过UI来新增路由

请求上下文封装

RequestContext是啥?我们肯定还要对上下文进行封装,看看实现吧

//
// Created by 30398 on 2024/1/2.
//
#ifndef  _REQUEST_CONTEXT_H
#define _REQUEST_CONTEXT_H
#include <stdlib.h>
#include <string.h>
#include "RouterAnalysis.h"
typedef struct {
    char* uri;
    char* routePath;
    char** argName;
    char** argValue;
    char* routeParrtern;
    char* method;
    int argNums;
} RequestContext;

char* strdup(const char* str) {
    // 获取字符串长度
    size_t len = strlen(str);

    // 分配足够的内存(包括字符串结尾的 '\0')
    char* new_str = (char*)malloc(len + 1);

    // 如果内存分配成功,则字符串并返回指针
    if (new_str != NULL) {
        strcpy(new_str, str);
    }

    return new_str;
}
RequestContext* initializeRequestContext(char* uri) {
    // 分配内存给 RequestContext 结构体指针
    RequestContext* context = (RequestContext*)malloc(sizeof(RequestContext));
#ifdef DEBUG
    printf("[RequestContextInit] 正在给 %s 生成上下文,context对象生成中\n",uri);
#endif

    // 检查内存分配是否成功
    if (context == NULL) {
        // 处理内存分配失败的情况
        return NULL;
    }
    // "POST /api/login"
    int pos=0;
    context->uri = strdup(uri); // 假设有一个 strdup 函数用于字符串

    char** argName= malloc(sizeof(char*)*50);
    char** argValue=malloc(sizeof(char*)*50);
#ifdef DEBUG
    printf("[RequestContextInit] URL参数分析中...\n");
#endif
    int numArgs = UriParamAnlysis(uri, &argName, &argValue);
    context->argNums=numArgs;
    // 分配内存来存储 argName 和 argValue 数组的指针
    context->routePath=strdup(UriRouteAnalysis(uri));
    context->argName = (char**)malloc(numArgs * sizeof(char*));
    context->argValue = (char**)malloc(numArgs * sizeof(char*));
    context->method=malloc(sizeof(char)*10);
    context->routeParrtern=malloc(sizeof(char)*256);
    sscanf(uri,"%s %s",context->method,context->routeParrtern);
    free(context->routeParrtern);
    context->routeParrtern=malloc(sizeof(char)*512);
    strcat(context->routeParrtern,context->method);
    strcat(context->routeParrtern," ");
    strcat(context->routeParrtern,context->routePath);
#ifdef DEBUG
    printf("%s\n",context->method);
    printf("%s\n",context->routeParrtern);
#endif
    // 检查内存分配是否成功
    if (context->argName == NULL || context->argValue == NULL) {
        // 处理内存分配失败的情况
        free(context->uri);
        free(context->argName);
        free(context->argValue);
        free(context);
        return NULL;
    }

    // 将解析后的参数拷贝到 context->argName 和 context->argValue 数组中
    for (int i = 0; i < numArgs; ++i) {
        context->argName[i] = strdup(argName[i]);
        context->argValue[i] = strdup(argValue[i]);
    }

    // 释放 UriParamAnlysis 分配的内存
    for (int i = 0; i < numArgs; ++i) {
        free(argName[i]);
        free(argValue[i]);
    }
    free(argName);
    free(argValue);
#ifdef DEBUG
    printf("[RequestContextInit] %s 上下文生成完毕,参数:",uri);
    for (int i = 0; i < numArgs; ++i) {
        printf("%s ",context->argName[i]);
    }
    printf("值:");
    for (int i = 0; i < numArgs; ++i) {
        printf("%s ",context->argValue[i]);
    }
    printf("\n");
#endif
    return context;
}
// 函数用于释放 RequestContext 结构体占用的内存
void freeRequestContext(RequestContext* context) {
    free(context->uri);

    // 释放 argName 和 argValue 数组的指针所指向的字符串内存
    for (int i = 0; context->argName[i]!=NULL; ++i) {
        free(context->argName[i]);
        free(context->argValue[i]);
    }

    // 释放 argName 和 argValue 数组的指针数组
    free(context->argName);
    free(context->argValue);
}
char** getParam(RequestContext* requestContext,char* argName){
#ifdef DEBUG
    printf("正在查找参数%s",argName);
#endif
    for (int i = 0; requestContext->argNums; ++i) {
        if (strcmp(requestContext->argName[i], argName) == 0) {
            return requestContext->argValue[i];
        }
    }
    return NULL;
}

#endif

路由分析工具

还有一个路由分析工具

//
// Created by 30398 on 2023/12/30.
//
#ifndef _ROUERANALYSIS_H_
#define _ROUERANALYSIS_H_
#include <string.h>

char* UriRouteAnalysis(char *uri){
    // 获取出连接的路由,去除hostname和参数 uri的格式为:METHOD URL(URI/?argkey=argvalue)
    char *start = strdup(strchr(uri,' '));
    start++;
    char *end = strchr(start, '?');
    if (end != NULL) {
        *end = '\0';
    }
#ifdef DEBUG
    printf("uri【%s】解析成功:%s\n",uri,start);
#endif
    char *result = start;
    return result;
}

int UriParamAnlysis(const char* uri, char*** argName, char*** argValue) {
    if(argName==NULL || argValue==NULL){
        return 0;
    }
    char* pvstrs = strchr(uri, '?');

    if (pvstrs == NULL) {
        // 没有参数部分,直接返回
        return 0;
    }

    // 创建字符串的副本进行解析
    char* pvstrsCopy = strdup(pvstrs + 1);

    if (pvstrsCopy == NULL) {
        // 内存分配失败
        return -1;
    }

    // 通过等号来分析,将每个参数 参数名放入params, value放在values
    char* iter = strtok(pvstrsCopy, "&");
    int numParams = 0;
//    printf("%s\n",iter);
    while (iter != NULL) {
        char* eq = strchr(iter, '=');

        if (eq != NULL) {
            *eq = '\0';
            char* key = iter;
            char* value = eq + 1;
            // 分配内存来存储参数名和参数值
            (*argName)[numParams] = strdup(key);
            (*argValue)[numParams] = strdup(value);
//            printf("%s %s\n",(*argName)[numParams],(*argValue)[numParams]);
            numParams++;
        }

        iter = strtok(NULL, "&");
    }

    // 释放副本的内存
    free(pvstrsCopy);

    return numParams;
}

#endif

JSON封装工具

响应是使用JSON,我这里用的很少,就不用CJson了,直接自己封装一个

//
// Created by 30398 on 2024/1/2.
//

#ifndef TINY_RESPONSE_H
#define TINY_RESPONSE_H
#include <stdio.h>

typedef struct {
    int code;
    char*  data;
    char*  message;
}ResponseData;

char* toJSON(ResponseData* res){
    int  code=res->code;
    char*  data=res->data;
    char* message=res->message;
    char* resf= malloc(sizeof(char)*10240);
    sprintf(resf,"{\n"
                 "   \"code\": %d,\n"
                 "   \"data\": \"%s\",\n"
                 "   \"message\": \"%s\"\n"
                 "}\n",code,data,message);
    return resf;
}

ResponseData* RES_SUCESS_DATA(char* data){
    ResponseData* res = malloc(sizeof (ResponseData));
    res->code=0;
    res->data= strdup(data);
    res->message="success";
    return res;
}

ResponseData* RES_SUCESS(){
    ResponseData* res = malloc(sizeof (ResponseData));
    res->code=0;
    res->data="";
    res->message="success";
    return res;
}

ResponseData* RES_FAIL(){
    ResponseData* res = malloc(sizeof (ResponseData));
    res->code=10001;
    res->data="";
    res->message="fail";
    return res;
}
ResponseData* RES_FAIL_MESSAGE(char* message){
    ResponseData* res = malloc(sizeof (ResponseData));
    res->code=10001;
    res->data="";
    res->message= strdup(message);
    return res;
}
ResponseData* RES_FAIL_CODE_MESSAGE(int code,char* message){
    ResponseData* res = malloc(sizeof (ResponseData));
    res->code=code;
    res->data="";
    res->message = strdup(message);
    return res;
}
#endif //TINY_RESPONSE_H

主函数编写

好了,下面是主函数的编写

// 使用Demo
int main() {
    // 这两句话放在主函数中
    RouterTable *RouteHash = createDefaultRouterTable();
    RouterInit(RouteHash);
    redisInit();
    char *buf=NULL;
    char content[1024];
    memset(content,0,sizeof content);
//    /* Extract the two arguments */
//    strdup(strcat(strdup(POST_ROUTE_LOGIN),"?username=admin&password=12345"))
    if ((buf = getenv("QUERY_STRING")) != NULL) {
        RequestContext *requestContext = initializeRequestContext(buf);
        // 对于所有类型的请求拦截使用routePath
        // 对于指令method使用 Parrtern,并且添加路由的时候命名规则为: “METHOD routerName”
#ifdef DEBUG
        printf("[MAIN]进行路由处理 {Router: %s}\n",buf);
#endif
        char* res=toJSON(runRoute(RouteHash, requestContext->routeParrtern).run(requestContext));
        sprintf(content,"%s\r\n",res);
        printf("Connection: close\r\n");
        printf("Content-length: %d\r\n", (int)strlen(content));
        printf("Content-type: text/json;charset=UTF-8\r\n\r\n");
        printf("%s\r\n", content);
#ifdef DEBUG
        printf("[MAIN]释放返回体content: %s\n",content);
#endif
        fflush(stdout);
#ifdef DEBUG
        printf("[MAIN]释放请求上下文requestContext\n");
#endif
        freeRequestContext(requestContext);
    }
    else {
        buf= strdup("NULL");
        printf("Connection: close\r\n");
        printf("Content-length: %d\r\n", 17);
        printf("Content-type: text/html;charset=UTF-8\r\n\r\n");
        printf("404 - NOT FIND of %s\r\n",buf);
        fflush(stdout);
    }
#ifdef DEBUG
    printf("[MAIN]释放路由表\n");
#endif
    freeRouterTable(RouteHash);
    exit_status(0);
}

控制层调用

其中,这段代码是拿来进行Controller层执行的

char* res=toJSON(runRoute(RouteHash, requestContext->routeParrtern).run(requestContext));

咱们来看看Controller层的代码

Controller层

是不是有Java那味儿了?

//
// Created by 30398 on 2023/12/31.
//

#ifndef TINY_USER_CONTROLLER_H
#define TINY_USER_CONTROLLER_H
#include <string.h>
#include "../RequestAndResponse/RouterHash.h"
#include "./service/userdao.h"

// 使用define定义路由

const char* POST_ROUTE_LOGIN = "POST /api/login";
ResponseData* login(RequestContext* requestContext){
    // 解析参数
    char* username=getParam(requestContext,"username");
    char* password=getParam(requestContext,"password");
    if(loginOPER(username,password)){
        return RES_SUCESS("登录成功");
    }
    return RES_FAIL_MESSAGE("密码错误或检查数据库连接");
}

const char* POST_ROUTE_REG = "POST /api/reg";
ResponseData* reg(RequestContext* requestContext){
    // 解析参数
    char* username=getParam(requestContext,"username");
    char* password=getParam(requestContext,"password");
    if(regOPER(username,password)){
        return RES_SUCESS("注册成功");
    }
    return RES_FAIL_MESSAGE("注册失败,请检查数据库连接");
}


#endif //TINY_USER_CONTROLLER_H

Service层

看看Service层吧

//
// Created by 30398 on 2024/1/3.
//

#ifndef TINY_USERDAO_H
#define TINY_USERDAO_H
#include "daoconfig.h"
int loginOPER(char* username, char* password){
    return !strcmp(getHashMapping("user",username),password);
}

int regOPER(char* username, char* password){
    putHashMapping("user",username,password);
    return 1;
}

#endif //TINY_USERDAO_H

Dao层

这里,我用的hiredis,课程设计原本使用sqlite3

//
// Created by 30398 on 2024/1/3.
//

#ifndef TINY_DAOCONFIG_H
#define TINY_DAOCONFIG_H
//#include <sqlite3.h>
//#define connectDB(db) sqlite3_open("/home/tiny/student_test.db", &db);
//#define closeDB(db) sqlite3_close(&db);
//#define store()     connectDB(db);rc=sqlite3_exec(db, sql,NULL,NULL,NULL);closeDB(db);
//sqlite3 *db;
//int rc;
#include <hiredis/hiredis.h>
// 包含hiredis的所有包

#define __REDIS__IP "服务器账号"
#define __REDIS__PORT 服务器端口
#define __REDIS__PASSWORD "服务器密码"
redisContext* RedisContext;

void redisInit(){
//    char *sql = "CREATE TABLE IF NOT EXISTS user (id integer constraint user_pk primary key,"
//                "username text, "
//                "password text);";
//    store();
#ifdef DEBUG
    printf("[MAIN]Redis连接中");
#endif
    RedisContext = redisConnect(__REDIS__IP,__REDIS__PORT);
#ifdef DEBUG
    printf("[MAIN]Redis正在鉴权");
#endif
    redisCommand(RedisContext,"AUTH %s", __REDIS__PASSWORD);
#ifdef DEBUG
    printf("[MAIN]Redis连接成功");
#endif
}
char* readValue(char* key){
    redisReply *reply;
    reply = redisCommand(RedisContext,"GET %s", key);
    return reply->str;
}
void setValue(char* key, char* value){
    redisCommand(RedisContext,"SET %s %s", key, value);
    return;
}
char* deleteValue(char* key){
    redisReply *reply;
    reply = redisCommand(RedisContext,"DEL %s", key);
    return reply->str;
}
int putHashMapping(char* key, char* field, char* value){
    redisReply *reply;
    reply=redisCommand(RedisContext,"HSET %s %s %s", key, field, value);
    return reply->integer;
}
char* getHashMapping(char* key, char* field){
    redisReply *reply;
    reply = redisCommand(RedisContext,"HGET %s %s", key, field);
    return reply->str;
}
void exit_status(int stat){
//    closeDB(db);
#ifdef DEBUG
    printf("[MAIN]释放Redis上下文\n");
#endif
    redisFree(RedisContext);

    exit(stat);
}
#endif //TINY_DAOCONFIG_H

路由添加

这个地方就有点像Golang了,哈哈,杂交语言组合完毕

//
// Created by 30398 on 2024/1/4.
//

#ifndef TINY_ROUTE_H
#define TINY_ROUTE_H

#include "RequestAndResponse/RequestContext.h"
#include "RequestAndResponse/Response.h"
#include "RequestAndResponse/RouterHash.h"
#include "controller/UserController.h"
#include "controller/service/daoconfig.h"
#include "../csapp.h"

// 路由方法初始化
void RouterInit(RouterTable* RouteHash){
#ifdef DEBUG
    printf("[MAIN]路由表初始化中\n");
#endif
    addRoute(RouteHash,POST_ROUTE_LOGIN,login);
    addRoute(RouteHash,POST_ROUTE_REG,reg);
#ifdef DEBUG
    printf("[MAIN]路由表初始化结束\n");
#endif
}

#endif //TINY_ROUTE_H