应用——Web服务器项目代码解析

Web服务器项目代码解析

项目概述

这是一个基于C语言实现的轻量级Web服务器,具备用户登录、商品搜索、商品详情展示等功能,使用SQLite数据库存储数据,支持HTTP协议处理。

一、文件结构说明

1. HTML模板文件(前端页面)

项目包含6个HTML模板文件,位于html/目录下:

1.1 detail.html - 商品详情页
html 复制代码
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>商品详情</title> 
</head>
<body>
    <!-- 页面结构:商品图片、名称、价格、简介、详细描述 -->
    <div class="product-detail">
        <img id="product-img" src="" alt="商品图片">
        <h1 id="product-name">商品名称</h1>
        <div id="product-price">¥0.00</div>
        <!-- ... 其他内容 ... -->
    </div>
</body>
</html>
1.2 error.html - 错误页面
html 复制代码
<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <title>登陆</title>
</head>

<body>
    <div class="error-container">
        <h1>出错了!</h1>
        <p>抱歉,页面出现了一些问题,请稍后再试。</p>
        <div class="links">
            <a href="/login.html">返回登录</a>
            <a href="/search.html">返回搜索</a>
        </div>
    </div>
</body>

</html>
  • 用于显示系统错误或登录失败

  • 提供返回登录页和搜索页的链接

1.3 list.html - 商品列表页
html 复制代码
<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
    <title>商品</title> 
</head>
<body>
    <div class="header">
        <h1>商品搜索结果</h1>
        <a href="/search.html" class="back-link">返回搜索</a>
    </div>
    
    <div class="search-box">
        <form action="/search" method="GET">
            <input type="text" name="keyword" placeholder="继续搜索..." value="{{KEYWORD}}">
            <button type="submit">搜索</button>
        </form>
    </div>
    
    <div class="result-count">
        找到 <span id="product-count">0</span> 个商品
    </div>
    
    <div class="product-grid">
        <!-- 这里将被商品列表替换 -->
    </div>
</body>
</html>
  • 显示搜索结果

  • 包含继续搜索的表单

  • 使用模板变量{``{KEYWORD}}动态填充搜索关键词

1.4 login.html - 用户登录页
html 复制代码
<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
    <title>登录</title> 
</head>
<body>
    <h2>用户登录</h2>
    <form action="login">
        用户名: <input type="text" name='username' required='required' placeholder='请输入账号'><br>
        密码: <input type="password" name='password' required='required' placeholder='请输入密码'><br>
        <button type="submit">登录</button>
        <a href="/register.html">注册</a>
    </form>
</body>
</html>
1.5 register.html - 用户注册页
html 复制代码
<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
    <title>注册</title> 
</head>
<body>
    <h2>用户注册</h2>
    <form action="/register" method="GET">
        <div class="input-group">
            <input type="text" name="username" placeholder="用户名" required>
        </div>
        <div class="input-group">
            <input type="password" name="password" placeholder="密码" required>
        </div>
        <button type="submit">注册</button>
        </form>
        <div class="links">
            <a href="/login.html">已有账号?立即登录</a>
        </div>
    </div>
</body>
</html>
  • 简单的注册表单

  • 提供跳转到登录页的链接

1.6 search.html - 搜索主页
html 复制代码
<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
    <title>搜索</title> 
</head>
<body>
    <h1>搜索商品</h1>
    <form action="/search" method="GET">
        <input type="text" name="keyword" placeholder="输入商品名称" required>
        <button type="submit">搜索</button>
    </form>
    
    <div class="keyword">
        <h3>热门搜索:</h3>
        <a href="/search?keyword=诺基亚">诺基亚</a>
        <a href="/search?keyword=手机">手机</a>
        <a href="/search?keyword=耳机">耳机</a>
        <a href="/search?keyword=充电器">充电器</a>
        <a href="/search?keyword=三星">三星</a>
        <a href="/search?keyword=摩托罗拉">摩托罗拉</a>
        <a href="/search?keyword=索爱">索爱</a>
    </div>
</body>
</html>
  • 搜索表单

  • 热门搜索关键词链接(诺基亚、手机、耳机等)

2. C语言源代码文件

2.1 ser.c - 主服务器程序
2.1.1 数据库初始化
cpp 复制代码
void init_database() {
    if (sqlite3_open("123.db", &db) != SQLITE_OK) {
        printf("数据库连接失败: %s\n", sqlite3_errmsg(db));
        exit(1);
    }
    sqlite3_exec(db, "PRAGMA encoding = 'UTF-8'", NULL, NULL, NULL);
}
  • 连接SQLite数据库123.db

  • 设置数据库编码为UTF-8,支持中文

2.1.2 HTTP响应处理函数
cpp 复制代码
void send_http_response(int conn, const char *content_type, const char *content) {
    char header[512];
    long len = strlen(content);
    sprintf(header,
        "HTTP/1.1 200 OK\r\n"
        "Content-Type: %s; charset=utf-8\r\n"
        "Content-Length: %ld\r\n"
        "Connection: close\r\n\r\n",
        content_type, len);
    send(conn, header, strlen(header), 0);
    send(conn, content, len, 0);
}
2.1.3 文件发送函数
cpp 复制代码
void send_file(int conn, const char *filename) {
    FILE *fp = fopen(filename, "rb");
    if (!fp) {
        char *error = "HTTP/1.1 404 Not Found\r\n\r\n<h1>404 Not Found</h1>";
        send(conn, error, strlen(error), 0);
        return;
    }
    // ... 根据文件扩展名设置Content-Type ...
}
  • 支持HTML、JPEG、PNG、GIF等文件类型

  • 自动设置正确的Content-Type头部

2.1.4 用户登录验证
cpp 复制代码
int check_login(const char *user, const char *pass) {
    sqlite3_stmt *stmt;
    const char *sql = "SELECT user_id FROM admin_user WHERE user_name = ? AND password = ?";
    // 使用预编译语句防止SQL注入
    sqlite3_bind_text(stmt, 1, user, -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 2, pass, -1, SQLITE_STATIC);
    // ... 执行查询 ...
}
2.1.5 商品图片查找
cpp 复制代码
char *find_product_image(int goods_id) {
    static char img_path[512];
    const char *dirs[] = {
        "images/200905/thumb_img",    // 缩略图目录
        "images/200905/goods_img",    // 商品图目录
        "images/200905/source_img",   // 原图目录
        "images"                      // 根图片目录
    };
    // 按目录优先级查找商品图片
    // 格式:{goods_id}_xxx.jpg
    // 如果找不到,使用默认图片no_picture.gif
}
2.1.6 商品搜索处理
cpp 复制代码
void handle_search(int conn, const char *keyword) {
    // SQL查询:在商品名称和简介中搜索关键词
    const char *sql = "SELECT goods_id, goods_name, shop_price, goods_brief FROM goods WHERE goods_name LIKE ? OR goods_brief LIKE ? LIMIT 50";
    // 构建包含商品图片、名称、价格、详情的HTML响应
}
2.1.7 商品详情处理
cpp 复制代码
void handle_detail(int conn, int product_id) {
    // 查询商品详细信息
    const char *sql = "SELECT goods_name, shop_price, goods_desc, goods_brief FROM goods WHERE goods_id = ?";
    // 生成包含完整商品信息的HTML页面
}
2.1.8 URL参数解析
cpp 复制代码
void get_url_param(char *url, const char *name, char *output, int size) {
    // 从URL中提取参数值
    // 例如:从"/search?keyword=手机"中提取keyword的值"手机"
    // 支持URL解码(处理中文字符)
}
2.1.9 主服务器循环
cpp 复制代码
int main() {
    // 1. 初始化数据库
    // 2. 创建socket,绑定8080端口
    // 3. 监听连接请求
    // 4. 处理HTTP请求(路由分发)
    // 5. 关闭连接,循环继续
}

路由处理逻辑:

cpp 复制代码
if (strcmp(url, "/") == 0 || strcmp(url, "/login.html") == 0) {
    send_file(conn, "html/login.html");
}
else if (strncmp(url, "/login?", 7) == 0) {
    // 处理登录请求
}
else if (strncmp(url, "/search?", 8) == 0) {
    // 处理搜索请求
}
else if (strncmp(url, "/detail/", 8) == 0) {
    // 处理商品详情请求
}
// ... 其他路由 ...
2.1.10 代码
cpp 复制代码
#include "url2.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sqlite3.h>
#include <dirent.h>
#include <fcntl.h>

typedef struct sockaddr *SA;
sqlite3 *db = NULL;

void init_database() {
    if (sqlite3_open("123.db", &db) != SQLITE_OK) {
        printf("数据库连接失败: %s\n", sqlite3_errmsg(db));
        exit(1);
    }
    printf("数据库连接成功\n");
    sqlite3_exec(db, "PRAGMA encoding = 'UTF-8'", NULL, NULL, NULL);
}

int file_exists(const char *path) {
    FILE *fp = fopen(path, "rb");
    if (fp) {
        fclose(fp);
        return 1;
    }
    return 0;
}

void send_http_response(int conn, const char *content_type, const char *content) {
    char header[512];
    long len = strlen(content);
    sprintf(header,
        "HTTP/1.1 200 OK\r\n"
        "Content-Type: %s; charset=utf-8\r\n"
        "Content-Length: %ld\r\n"
        "Connection: close\r\n\r\n",
        content_type, len);
    send(conn, header, strlen(header), 0);
    send(conn, content, len, 0);
}

void send_file(int conn, const char *filename) {
    FILE *fp = fopen(filename, "rb");
    if (!fp) {
        char *error = "HTTP/1.1 404 Not Found\r\n\r\n<h1>404 Not Found</h1>";
        send(conn, error, strlen(error), 0);
        return;
    }

    fseek(fp, 0, SEEK_END);
    long size = ftell(fp);
    fseek(fp, 0, SEEK_SET);

    const char *content_type = "text/plain";
    if (strstr(filename, ".html")) content_type = "text/html";
    else if (strstr(filename, ".jpg") || strstr(filename, ".jpeg")) content_type = "image/jpeg";
    else if (strstr(filename, ".png")) content_type = "image/png";
    else if (strstr(filename, ".gif")) content_type = "image/gif";

    char header[512];
    sprintf(header,
        "HTTP/1.1 200 OK\r\n"
        "Content-Type: %s\r\n"
        "Content-Length: %ld\r\n"
        "Connection: close\r\n\r\n",
        content_type, size);
    send(conn, header, strlen(header), 0);

    char buffer[4096];
    size_t bytes_read;
    while ((bytes_read = fread(buffer, 1, sizeof(buffer), fp)) > 0) {
        send(conn, buffer, bytes_read, 0);
    }
    fclose(fp);
}

char *safe_get_text(sqlite3_stmt *stmt, int col) {
    const unsigned char *text = sqlite3_column_text(stmt, col);
    if (!text) return strdup("");
    int len = sqlite3_column_bytes(stmt, col);
    if (len <= 0) return strdup("");
    char *result = malloc(len + 1);
    memcpy(result, text, len);
    result[len] = '\0';
    return result;
}

int check_login(const char *user, const char *pass) {
    sqlite3_stmt *stmt;
    const char *sql = "SELECT user_id FROM admin_user WHERE user_name = ? AND password = ?";
    if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) == SQLITE_OK) {
        sqlite3_bind_text(stmt, 1, user, -1, SQLITE_STATIC);
        sqlite3_bind_text(stmt, 2, pass, -1, SQLITE_STATIC);
        if (sqlite3_step(stmt) == SQLITE_ROW) {
            sqlite3_finalize(stmt);
            return 1;
        }
        sqlite3_finalize(stmt);
    }
    return 0;
}

char *find_product_image(int goods_id) {
    static char img_path[512];
    const char *dirs[] = {
        "images/200905/thumb_img",
        "images/200905/goods_img",
        "images/200905/source_img",
        "images"
    };
    for (int d = 0; d < 4; d++) {
        DIR *dir = opendir(dirs[d]);
        if (dir) {
            struct dirent *entry;
            char prefix[50];
            snprintf(prefix, sizeof(prefix), "%d_", goods_id);
            while ((entry = readdir(dir)) != NULL) {
                if (strncmp(entry->d_name, prefix, strlen(prefix)) == 0) {
                    snprintf(img_path, sizeof(img_path), "%s/%s", dirs[d], entry->d_name);
                    closedir(dir);
                    if (file_exists(img_path)) return img_path;
                }
            }
            closedir(dir);
        }
    }
    if (file_exists("images/no_picture.gif")) strcpy(img_path, "images/no_picture.gif");
    else strcpy(img_path, "");
    return img_path;
}

void handle_search(int conn, const char *keyword) {
    sqlite3_stmt *stmt;
    const char *sql = "SELECT goods_id, goods_name, shop_price, goods_brief FROM goods WHERE goods_name LIKE ? OR goods_brief LIKE ? LIMIT 50";
    if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) {
        send_http_response(conn, "text/html", "<h1>数据库查询错误</h1>");
        return;
    }
    char like[256];
    snprintf(like, sizeof(like), "%%%s%%", keyword);
    sqlite3_bind_text(stmt, 1, like, -1, SQLITE_STATIC);
    sqlite3_bind_text(stmt, 2, like, -1, SQLITE_STATIC);

    char html[65536];
    snprintf(html, sizeof(html),
        "<!DOCTYPE html>"
        "<html><head><meta charset='utf-8'><title>搜索结果</title>"
        "<style>"
        "body { font-family: Arial; margin: 20px; }"
        ".header { background: #f0f0f0; padding: 10px; margin-bottom: 20px; }"
        ".product-grid { display: flex; flex-wrap: wrap; }"
        ".product-item { border: 1px solid #ddd; margin: 10px; padding: 15px; width: 280px; }"
        ".product-item img { max-width: 120px; max-height: 120px; }"
        ".price { color: #e44; font-size: 18px; font-weight: bold; }"
        ".detail-link { display: inline-block; margin-top: 10px; padding: 5px 10px; background: #007bff; color: white; text-decoration: none; }"
        "</style></head><body>"
        "<div class='header'><h1>商品搜索结果</h1><p>搜索关键词: <strong>%s</strong></p>"
        "<a href='/search.html'>返回搜索</a></div>"
        "<form action='/search' method='GET' style='margin: 20px 0;'>"
        "<input type='text' name='keyword' value='%s' style='padding: 5px; width: 300px;'>"
        "<button type='submit' style='padding: 5px 15px;'>搜索</button></form>"
        "<div class='product-grid'>", keyword, keyword);

    int count = 0;
    while (sqlite3_step(stmt) == SQLITE_ROW) {
        int id = sqlite3_column_int(stmt, 0);
        char *name = safe_get_text(stmt, 1);
        double price = sqlite3_column_double(stmt, 2);
        char *brief = safe_get_text(stmt, 3);
        char *img_path = find_product_image(id);

        char item[1024];
        if (strlen(img_path) > 0) {
            snprintf(item, sizeof(item),
                "<div class='product-item'><img src='/%s' alt='%s'>"
                "<h3><a href='/detail/%d'>%s</a></h3>"
                "<p class='price'>¥%.2f</p><p>%s</p>"
                "<a class='detail-link' href='/detail/%d'>查看详情</a></div>",
                img_path, name, id, name, price, brief, id);
        } else {
            snprintf(item, sizeof(item),
                "<div class='product-item'><h3><a href='/detail/%d'>%s</a></h3>"
                "<p class='price'>¥%.2f</p><p>%s</p>"
                "<a class='detail-link' href='/detail/%d'>查看详情</a></div>",
                id, name, price, brief, id);
        }
        strcat(html, item);
        count++;
        free(name);
        free(brief);
    }
    sqlite3_finalize(stmt);

    strcat(html, "</div>");
    if (count == 0) strcat(html, "<p>未找到相关商品</p>");
    else {
        char count_msg[50];
        snprintf(count_msg, sizeof(count_msg), "<p>共找到 <strong>%d</strong> 个商品</p>", count);
        strcat(html, count_msg);
    }
    strcat(html, "<br><a href='/search.html'>返回搜索页面</a></body></html>");
    send_http_response(conn, "text/html", html);
}

void handle_detail(int conn, int product_id) {
    sqlite3_stmt *stmt;
    const char *sql = "SELECT goods_name, shop_price, goods_desc, goods_brief FROM goods WHERE goods_id = ?";
    if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) {
        send_http_response(conn, "text/html", "<h1>数据库查询错误</h1>");
        return;
    }
    sqlite3_bind_int(stmt, 1, product_id);
    if (sqlite3_step(stmt) != SQLITE_ROW) {
        char error[256];
        snprintf(error, sizeof(error), "<h1>商品不存在 (ID: %d)</h1>", product_id);
        send_http_response(conn, "text/html", error);
        sqlite3_finalize(stmt);
        return;
    }
    char *name = safe_get_text(stmt, 0);
    double price = sqlite3_column_double(stmt, 1);
    char *desc = safe_get_text(stmt, 2);
    char *brief = safe_get_text(stmt, 3);
    sqlite3_finalize(stmt);
    char *img_path = find_product_image(product_id);

    char html[32768];
    snprintf(html, sizeof(html),
        "<!DOCTYPE html><html><head><meta charset='utf-8'><title>%s</title>"
        "<style>"
        "body { font-family: Arial; margin: 20px; }"
        ".header { background: #f0f0f0; padding: 15px; margin-bottom: 20px; }"
        ".product-detail { max-width: 800px; }"
        ".product-image { float: left; margin-right: 30px; }"
        ".product-image img { max-width: 300px; max-height: 300px; border: 1px solid #ddd; }"
        ".product-info { overflow: hidden; }"
        ".product-name { font-size: 28px; margin: 0 0 10px 0; }"
        ".price { color: #e44; font-size: 32px; font-weight: bold; margin: 10px 0; }"
        ".brief { color: #666; font-size: 16px; margin: 15px 0; }"
        ".desc-title { font-size: 20px; margin: 25px 0 10px 0; border-bottom: 2px solid #007bff; }"
        ".description { line-height: 1.6; margin: 15px 0; }"
        ".action-buttons { margin-top: 30px; clear: both; }"
        ".action-btn { display: inline-block; padding: 10px 25px; margin-right: 15px; background: #007bff; color: white; text-decoration: none; border-radius: 4px; }"
        ".back-btn { background: #6c757d; }"
        ".continue-btn { background: #28a745; }"
        "</style></head><body>"
        "<div class='header'><h1>商品详情</h1><a href='/search.html'>返回搜索</a></div>"
        "<div class='product-detail'>", name);

    if (strlen(img_path) > 0) {
        char img_section[512];
        snprintf(img_section, sizeof(img_section),
            "<div class='product-image'><img src='/%s' alt='%s'></div>",
            img_path, name);
        strcat(html, img_section);
    }
    char info_section[20480];
    snprintf(info_section, sizeof(info_section),
        "<div class='product-info'>"
        "<h1 class='product-name'>%s</h1>"
        "<div class='price'>¥%.2f</div>"
        "<div class='brief'><strong>商品简介:</strong> %s</div>"
        "<h3 class='desc-title'>详细描述</h3>"
        "<div class='description'>%s</div>"
        "</div>"
        "<div class='action-buttons'>"
        "<a href='javascript:history.back()' class='action-btn back-btn'>返回列表</a>"
        "<a href='/search.html' class='action-btn continue-btn'>继续购物</a>"
        "</div></div></body></html>",
        name, price, brief, desc);
    strcat(html, info_section);
    send_http_response(conn, "text/html", html);
    free(name);
    free(desc);
    free(brief);
}

void get_url_param(char *url, const char *name, char *output, int size) {
    char search[256];
    snprintf(search, sizeof(search), "%s=", name);
    char *start = strstr(url, search);
    if (!start) {
        output[0] = '\0';
        return;
    }
    start += strlen(search);
    char *end = strchr(start, '&');
    if (!end) end = strchr(start, ' ');
    if (!end) end = strchr(start, '\n');
    if (!end) end = start + strlen(start);
    int len = end - start;
    if (len >= size) len = size - 1;
    strncpy(output, start, len);
    output[len] = '\0';
    urldecode(output);
}

int main() {
    init_database();
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock < 0) {
        perror("创建socket失败");
        return 1;
    }
    int opt = 1;
    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    struct sockaddr_in addr = {0};
    addr.sin_family = AF_INET;
    addr.sin_port = htons(8080);
    addr.sin_addr.s_addr = INADDR_ANY;
    if (bind(sock, (SA)&addr, sizeof(addr)) < 0) {
        perror("绑定端口失败");
        close(sock);
        return 1;
    }
    listen(sock, 5);
    printf("服务器运行在: http://localhost:8080\n");

    while (1) {
        struct sockaddr_in client;
        socklen_t len = sizeof(client);
        int conn = accept(sock, (SA)&client, &len);
        if (conn < 0) continue;
        char buf[8192];
        int bytes = recv(conn, buf, sizeof(buf) - 1, 0);
        if (bytes <= 0) {
            close(conn);
            continue;
        }
        buf[bytes] = '\0';
        char *method = strtok(buf, " ");
        char *url = strtok(NULL, " ");
        if (!method || !url) {
            close(conn);
            continue;
        }
        printf("请求: %s %s\n", method, url);

        if (strcmp(url, "/") == 0 || strcmp(url, "/login.html") == 0) {
            send_file(conn, "html/login.html");
        }
        else if (strcmp(url, "/search.html") == 0) {
            send_file(conn, "html/search.html");
        }
        else if (strcmp(url, "/register.html") == 0) {
            send_file(conn, "html/register.html");
        }
        else if (strncmp(url, "/images/", 8) == 0) {
            send_file(conn, url + 1);
        }
        else if (strncmp(url, "/login?", 7) == 0) {
            char username[256], password[256];
            get_url_param(url, "username", username, sizeof(username));
            get_url_param(url, "password", password, sizeof(password));
            if (check_login(username, password)) {
                char *redirect = "HTTP/1.1 302 Found\r\nLocation: /search.html\r\n\r\n";
                send(conn, redirect, strlen(redirect), 0);
            } else {
                send_file(conn, "html/error.html");
            }
        }
        else if (strncmp(url, "/search?", 8) == 0) {
            char keyword[256];
            get_url_param(url, "keyword", keyword, sizeof(keyword));
            handle_search(conn, keyword);
        }
        else if (strncmp(url, "/detail/", 8) == 0) {
            int id = atoi(url + 8);
            handle_detail(conn, id);
        }
        else if (strstr(url, ".html")) {
            char filepath[256];
            snprintf(filepath, sizeof(filepath), "html%s", url);
            send_file(conn, filepath);
        }
        else if (strstr(url, ".ico")) {
            char *resp = "HTTP/1.1 204 No Content\r\n\r\n";
            send(conn, resp, strlen(resp), 0);
        }
        else {
            send_file(conn, "html/error.html");
        }
        close(conn);
    }
    sqlite3_close(db);
    close(sock);
    return 0;
}
2.2 url2.c - URL编码/解码工具
2.2.1 URL编码函数
cpp 复制代码
void urlencode(char url[]) {
    // 将特殊字符转换为%XX格式
    // 保留字母、数字、斜杠和点号
    // 例如:"测试" -> "%E6%B5%8B%E8%AF%95"
}
2.2.2 URL解码函数
cpp 复制代码
void urldecode(char url[]) {
    // 将%XX格式转换回原始字符
    // 例如:"%E6%B5%8B%E8%AF%95" -> "测试"
}
2.2.3 代码
cpp 复制代码
#include <stdio.h>
#include <string.h>

#define BURSIZE 2048

int hex2dec(char c)
{
    if ('0' <= c && c <= '9')
    {
        return c - '0';
    }
    else if ('a' <= c && c <= 'f')
    {
        return c - 'a' + 10;
    }
    else if ('A' <= c && c <= 'F')
    {
        return c - 'A' + 10;
    }
    else
    {
        return -1;
    }
}

char dec2hex(short int c)
{
    if (0 <= c && c <= 9)
    {
        return c + '0';
    }
    else if (10 <= c && c <= 15)
    {
        return c + 'A' - 10;
    }
    else
    {
        return -1;
    }
}


//编码一个url
void urlencode(char url[])
{
    int i = 0;
    int len = strlen(url);
    int res_len = 0;
    char res[BURSIZE];
    for (i = 0; i < len; ++i)
    {
        char c = url[i];
        if (    ('0' <= c && c <= '9') ||
                ('a' <= c && c <= 'z') ||
                ('A' <= c && c <= 'Z') ||
                c == '/' || c == '.')
        {
            res[res_len++] = c;
        }
        else
        {
            int j = (short int)c;
            if (j < 0)
                j += 256;
            int i1, i0;
            i1 = j / 16;
            i0 = j - i1 * 16;
            res[res_len++] = '%';
            res[res_len++] = dec2hex(i1);
            res[res_len++] = dec2hex(i0);
        }
    }
    res[res_len] = '\0';
    strcpy(url, res);
}

// 解码url
void urldecode(char url[])
{
    int i = 0;
    int len = strlen(url);
    int res_len = 0;
    char res[BURSIZE];
    for (i = 0; i < len; ++i)
    {
        char c = url[i];
        if (c != '%')
        {
            res[res_len++] = c;
        }
        else
        {
            char c1 = url[++i];
            char c0 = url[++i];
            int num = 0;
            num = hex2dec(c1) * 16 + hex2dec(c0);
            res[res_len++] = num;
        }
    }
    res[res_len] = '\0';
    strcpy(url, res);
}
#if 0
int main(int argc, char *argv[])
{
/*
    char url[100] = "http://'测试/@mike";
    urlencode(url); //编码后
    printf("http://'测试/@mike  ----> %s\n", url);

    char buf[100] = "http%3A//%27%E6%B5%8B%E8%AF%95/%40mike";
    urldecode(buf); //解码后
    printf("http%%3A//%%27%%E6%%B5%%8B%%E8%%AF%%95/%%40mike  ----> %s\n", buf);
*/

  /*
    char buf[100]="三1星";
    urlencode(buf);
    printf("encode %s\n",buf);
    */
    
    //char url[100]="%E4%B8%891%E6%98%9F";
    char url[100]="%E4%B8%89%E6%98%9F";
    urldecode(url);
    printf("decode %s\n",url);
    return 0;
}
#endif
2.3 url2.h - URL处理头文件
cpp 复制代码
// url2.h
#ifndef URL2_H
#define URL2_H

// URL编码函数
void urlencode(char url[]);

// URL解码函数
void urldecode(char url[]);

#endif

简单的函数声明头文件。

三、数据库设计

表结构推测

  1. admin_user表(用户表)

    • user_id:用户ID(主键)

    • user_name:用户名

    • password:密码

  2. goods表(商品表)

    • goods_id:商品ID(主键)

    • goods_name:商品名称

    • shop_price:商品价格

    • goods_brief:商品简介

    • goods_desc:商品详细描述

四、目录结构

复制代码
项目根目录/
├── ser.c              # 主服务器程序
├── url2.c             # URL编码解码
├── url2.h             # URL处理头文件
├── 123.db             # SQLite数据库
├── html/              # HTML模板目录
│   ├── login.html
│   ├── search.html
│   ├── detail.html
│   ├── list.html
│   ├── register.html
│   └── error.html
└── images/            # 图片资源目录
    ├── 200905/
    │   ├── thumb_img/
    │   ├── goods_img/
    │   └── source_img/
    └── no_picture.gif  # 默认图片

流程图

五、功能特点

1. 安全性考虑

  • 使用SQLite预编译语句防止SQL注入

  • URL参数解码处理

  • 文件存在性检查

2. 性能优化

  • 静态文件缓存发送(分块读取发送)

  • 数据库查询限制结果数量(LIMIT 50)

  • 连接复用设置(SO_REUSEADDR)

3. 用户体验

  • 响应式HTML设计(内联CSS)

  • 图片懒加载(多种尺寸图片目录)

  • 友好的错误页面

  • 热门搜索推荐

4. 扩展性

  • 模块化路由处理

  • 易于添加新的页面和处理函数

  • 支持多种文件类型

六、编译运行

编译命令

复制代码
gcc -o server ser.c url2.c -lsqlite3 -lpthread

运行服务器

复制代码
./server

服务器将监听8080端口:http://localhost:8080

七、改进建议

  1. 安全性增强

    • 添加密码哈希存储(当前为明文)

    • 实现Session管理

    • 增加请求频率限制

  2. 功能扩展

    • 添加商品分类筛选

    • 实现购物车功能

    • 添加分页支持

  3. 代码优化

    • 分离路由配置到独立文件

    • 添加配置文件支持

    • 实现日志记录系统

  4. 性能提升

    • 添加HTTP Keep-Alive支持

    • 实现图片缓存机制

    • 数据库连接池

八、总结

这是一个功能完整的C语言Web服务器项目,展示了:

  • 基本的HTTP服务器实现

  • 数据库操作与SQLite集成

  • 动态HTML页面生成

  • 文件系统操作

  • URL编码解码处理

相关推荐
ALex_zry2 分钟前
CMake变量传递与宏定义技术详解:从问题到解决方案
开发语言·spring·cmake·条件编译
lang201509282 分钟前
彻底理解CountDownLatch
java·开发语言
Eaxker8 分钟前
前端工程化
前端
moonsims9 分钟前
波士顿动力Auto-Connect-复杂环境下机器人连接解决方案
服务器·无人机
Jelena1577958579211 分钟前
实战解析:京东关键词搜索 item_search_pro —— 按关键字搜索商品
开发语言·数据库·python
北方的流星11 分钟前
华为交换机MSTP和VRRP综合应用配置
运维·网络·华为
web小白成长日记17 分钟前
前端三个月速成,是否靠谱?
前端·react.js·前端框架·html·reactjs·webkit·scss
2501_9418705618 分钟前
从日志泛滥到结构化可观测体系落地的互联网系统工程实践随笔与多语言语法思考
开发语言·python
PBitW18 分钟前
Electron 初体验 —— AI辅助上手,确实不难(๑•̀ㅂ•́)و✧
前端·electron
山沐与山20 分钟前
【MQ】MQ消息队列幂等性设计与踩坑实战
java·开发语言·数据库·rocketmq