前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >用C语言搓一个小型的服务器,拥有路由解析器(支持MVC架构)

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

原创
作者头像
Karos
修改2024-01-05 11:28:15
5251
修改2024-01-05 11:28:15
举报
文章被收录于专栏:MyBlog-Karos

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

架构讲解

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

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

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

代码语言:c
复制
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
img

添加描述

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

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

img
img

添加描述

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

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

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

上代码

代码语言:c
复制
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-请求处理的代码

代码语言:c
复制
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语言哈希怎么办?手搓!一个路由对应一个函数

哈希实现

代码语言: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是啥?我们肯定还要对上下文进行封装,看看实现吧

代码语言:c
复制
//
// 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

路由分析工具

还有一个路由分析工具

代码语言:c
复制
//
// 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了,直接自己封装一个

代码语言:c
复制
//
// 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

主函数编写

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

代码语言:c
复制
// 使用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层执行的

代码语言:c
复制
char* res=toJSON(runRoute(RouteHash, requestContext->routeParrtern).run(requestContext));

咱们来看看Controller层的代码

Controller层

是不是有Java那味儿了?

代码语言:c
复制
//
// 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层吧

代码语言:c
复制
//
// 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

代码语言:c
复制
//
// 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了,哈哈,杂交语言组合完毕

代码语言:c
复制
//
// 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

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 用C语言搓一个小型的服务器,拥有路由解析器(支持MVC架构)
  • 架构讲解
  • 哈希实现
  • 请求上下文封装
  • 路由分析工具
  • JSON封装工具
  • 主函数编写
  • 控制层调用
  • Controller层
  • Service层
  • Dao层
  • 路由添加
相关产品与服务
云数据库 Redis
腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档