简易在线商城制作

1.背景描述

在网络购物日益普及的场景下,用户需要一个轻量、高效的在线商城系统,实现商品浏览、搜

索、登录验证等核心功能。当前部分简易商城系统存在功能冗余、运行依赖复杂等问题,而本系统

基于 C 语言开发,无需复杂运行环境,可快速部署在 Linux 平台。

该系统将整合 Web 服务器、SQLite 数据库查询、静态页面展示等功能,支持用户登录验证、商

品搜索、商品详情查看等核心操作,让用户通过浏览器即可完成基础购物相关查询操作,满足简易

场景下的商城使用需求。

2.主要功能实现

(1)登录商城(没有账号可以进行注册)

(2)登陆后进入商品页,可以搜索商品

(3)点击商品查看商品详情。

3.结构框图

4.代码实现

(1)设计登录界面,注册界面,搜索界面以及商品详情页四个html文件

可根据

html 复制代码
<style>
      body{
        background-image: url(5.jpg);
      }
    </style>

为不同界面添加背景图。

登陆界面
html 复制代码
<body>
    <div class="login-box">
        <h2 align=center>用户登录</h2>
        <form action="http://127.0.0.1:80/login" method="GET">
            <div class="form-item">
                <h3 align=center><label for="username">用户名</label>
                <input type="text" id="username" name="username" placeholder="请输入用户名" required></h3>
            </div>
            <div class="form-item">
                <h3 align=center><label for="password">密&nbsp&nbsp&nbsp&nbsp码</label>
                <input type="password" id="password" name="password" placeholder="请输入密码" required></h3>
            </div>
            <h3 align=center><button type="submit" class="login-btn">登录</button></h3>
        </form>
        <div class="register-link">
            <h3 align=center>还没有账号?<a href="http://127.0.0.1:80/register.html">立即注册</a></h3>
        </div>
    </div>
</body>
注册界面
html 复制代码
<body>
    <div class="register-box">
        <h2 align="center">用户注册</h2>
        <form action="http://127.0.0.1:80/register_submit" method="GET">
            <div class="form-item">
                <h3 align="center"><label for="username">用&nbsp&nbsp户&nbsp&nbsp名</label>
                <input type="text" id="username" name="username" placeholder="请输入用户名" required></h3>
            </div>
            <div class="form-item">
                <h3 align="center"><label for="password">密&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp码</label>
                <input type="password" id="password" name="password" placeholder="请输入密码" required></h3>
            </div>
            <div class="form-item">
                <h3 align="center"><label for="password2">确认密码</label>
                <input type="password" id="password2" name="password2" placeholder="请再次输入密码" required></h3>
            </div>
            <h3 align="center"><button type="submit" class="register-btn">注册</button></h3>
        </form>
    </div>
</body>
搜索界面
html 复制代码
<body>
    <div class="nav">
        <a href="http://127.0.0.1:80/01.html">返回商品首页</a> | 
        <a href="http://127.0.0.1:80/">退出登录</a>
    </div>

    <div class="search-box">
        <input type="text" placeholder="请输入商品名称搜索...">
        <button>搜索</button>
    </div>

    <h2 class="result-title">暂无搜索结果,请返回商品首页浏览</h2>
</body>
商品详情页
html 复制代码
<body>
    <div class="nav">
        <a href="http://127.0.0.1:80/">退出登录</a>
    </div>

    <!-- 搜索框区域 -->
    <div class="search-container">
        <input type="text" id="search-input" placeholder="请输入商品名称">
        <button onclick="searchGoods()">搜索</button>
    </div>

    <!-- 无结果提示 -->
    <div class="no-result" id="no-result">暂无商品</div>

    <!-- 商品列表区域 -->
    <div class="goods-list" id="goods-list">
        <div class="goods-item show" data-name="苹果">
            <img src="http://127.0.0.1:80/1.jpg" alt="苹果">
            <h3>苹果</h3>
            <p class="price">¥4399.00</p>
            <a href="http://127.0.0.1:80/goods?id=1">查看详情</a>
        </div>
        //可根据以上描述添加其他物品信息

    <script>
        // 搜索商品函数
        function searchGoods() {
            // 获取搜索关键词
            const keyword = document.getElementById('search-input').value.trim().toLowerCase();
            const goodsItems = document.querySelectorAll('.goods-item');
            const noResult = document.getElementById('no-result');
            let hasMatch = false;

            // 遍历所有商品
            goodsItems.forEach(item => {
                // 获取商品名称并转为小写
                const goodsName = item.getAttribute('data-name').toLowerCase();
                
                // 如果关键词为空,显示所有商品
                if (keyword === '') {
                    item.classList.add('show');
                    hasMatch = true;
                } 
                // 否则匹配关键词
                else if (goodsName.includes(keyword)) {
                    item.classList.add('show');
                    hasMatch = true;
                } 
                // 不匹配则隐藏
                else {
                    item.classList.remove('show');
                }
            });

            // 显示/隐藏无结果提示
            noResult.style.display = hasMatch ? 'none' : 'block';
        }

        // 支持回车搜索
        document.getElementById('search-input').addEventListener('keypress', function(e) {
            if (e.key === 'Enter') {
                searchGoods();
            }
        });

        // 页面加载时显示所有商品
        window.onload = function() {
            searchGoods();
        };
    </script>
</body>

可以添加不同的商品信息。

(2)相关代码示例

cpp 复制代码
#include <fcntl.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

typedef struct sockaddr*(SA);

// 文件类型枚举
typedef enum {
    FILE_HTML,
    FILE_PNG,
    FILE_JPG
} FILE_TYPE;

// 模拟用户存储
typedef struct {
    char username[32];
    char password[32];
} User;
User users[100];
int user_count = 0;

// 检查用户
int check_user(const char* name, const char* pass) {
    for (int i = 0; i < user_count; i++) {
        if (strcmp(users[i].username, name) == 0 && strcmp(users[i].password, pass) == 0) {
            return i;
        }
    }
    return -1;
}

// 注册用户
int register_user(const char* name, const char* pass) {
    for (int i = 0; i < user_count; i++) {
        if (strcmp(users[i].username, name) == 0) {
            return -1;
        }
    }
    if (user_count < 100) {
        strncpy(users[user_count].username, name, 31);
        strncpy(users[user_count].password, pass, 31);
        user_count++;
        return 0;
    }
    return -2;
}

// 发送文件
int send_file(int conn, char* filename, FILE_TYPE type) {
    char content_type[64];
    if (type == FILE_HTML) strcpy(content_type, "text/html;charset=utf-8");
    else if (type == FILE_PNG) strcpy(content_type, "image/png");
    else if (type == FILE_JPG) strcpy(content_type, "image/jpeg");

    int fd = open(filename, O_RDONLY);
    if (fd == -1) {
        char resp[512];
        sprintf(resp, "HTTP/1.1 404 Not Found\r\nContent-Type: text/html\r\nConnection: close\r\nContent-Length: %ld\r\n\r\n<h1>文件不存在</h1>", strlen("<h1>文件不存在</h1>"));
        send(conn, resp, strlen(resp), 0);
        return 1;
    }

    // 获取文件大小
    long size = lseek(fd, 0, SEEK_END);
    lseek(fd, 0, SEEK_SET);

    // 发送响应头
    char header[512];
    sprintf(header, "HTTP/1.1 200 OK\r\nContent-Type: %s\r\nContent-Length: %ld\r\nConnection: close\r\n\r\n", content_type, size);
    send(conn, header, strlen(header), 0);

    // 发送文件内容
    char buf[4096];
    ssize_t n;
    while ((n = read(fd, buf, sizeof(buf))) > 0) {
        send(conn, buf, n, 0);
    }
    close(fd);
    return 0;
}

// 发送文本响应
void send_text(int conn, const char* status, const char* content) {
    char resp[2048];
    sprintf(resp, "HTTP/1.1 %s\r\nContent-Type: text/html;charset=utf-8\r\nConnection: close\r\nContent-Length: %ld\r\n\r\n%s", status, strlen(content), content);
    send(conn, resp, strlen(resp), 0);
}
void send_goods_page(int conn, int goods_id) {
    // 商品数据
    const char* names[4] = {"苹果", "小米", "oppo", "三星"};
    float prices[4] = {4399.00, 2999.00, 3299.00, 2599.00};
    int stocks[4] = {1000, 800, 500, 1200};
    const char* colors[4] = {"白色/黑色/灰色", "蓝色/黑色/浅蓝", "白色/黑色/米色", "黑色/白色/灰色"};

    // 确保ID有效
    if (goods_id < 1 || goods_id > 4) goods_id = 1;
    int idx = goods_id - 1;

    // 硬编码商品详情页HTML
    char html[8192];
    sprintf(html,
        "<!DOCTYPE html>"
        "<html lang='zh-CN'>"
        "<head>"
        "<meta charset='UTF-8'>"
        "<title>%s - 商品商城</title>"
        "<style>"
        "* {margin:0;padding:0;box-sizing:border-box;}"
        "body {font-family:'Microsoft YaHei';margin:50px;background:#f9f9f9;}"
        ".nav {margin-bottom:30px;padding-bottom:10px;border-bottom:1px solid #eee;}"
        "a {color:#0088ff;text-decoration:none;margin-right:10px;}"
        "a:hover {text-decoration:underline;}"
        ".goods-detail {display:flex;gap:30px;padding:20px;background:white;border-radius:8px;box-shadow:0 2px 8px rgba(0,0,0,0.08);}"
        ".goods-img img {width:300px;height:220px;object-fit:cover;border:1px solid #eee;border-radius:4px;}"
        ".thumb-img {margin-top:10px;}"
        ".thumb-img img {width:80px;height:60px;margin-right:5px;object-fit:cover;cursor:pointer;border:1px solid #eee;border-radius:4px;}"
        ".goods-info h2 {margin-bottom:15px;color:#e64340;font-size:20px;}"
        ".goods-info p {margin:10px 0;font-size:16px;color:#555;}"
        ".goods-info .price {font-size:22px;color:#e64340;font-weight:bold;}"
        ".goods-info button {padding:10px 25px;background:#e64340;color:white;border:none;border-radius:4px;cursor:pointer;margin-right:10px;margin-top:15px;}"
        "</style>"
        "</head>"
        "<body>"
        "<div class='nav'>"
        "<a href='http://127.0.0.1:80/01.html'>返回商品首页</a> | "
        "<a href='http://127.0.0.1:80/'>退出登录</a>"
        "</div>"
        "<div class='goods-detail'>"
        "<div class='goods-img'>"
        "<img src='http://127.0.0.1:80/%d.jpg' alt='商品图片' id='main-img'>"
        "</div>"
        "<div class='goods-info'>"
        "<h2>%s</h2>"
        "<p class='price'>价格:¥%.2f</p>"
        "<p>库存:%d</p>"
        "<p>颜色:%s</p>"
        "</div>"
        "</div>"
        "<script>"
        "function changeImg(n){document.getElementById('main-img').src='http://127.0.0.1:80/'+n+'.jpg';}"
        "</script>"
        "</body>"
        "</html>",
        names[idx], goods_id, names[idx], prices[idx], stocks[idx],colors[idx]
    );

    // 发送响应
    char resp[8192];
    sprintf(resp, "HTTP/1.1 200 OK\r\nContent-Type: text/html;charset=utf-8\r\nConnection: close\r\nContent-Length: %ld\r\n\r\n%s", strlen(html), html);
    send(conn, resp, strlen(resp), 0);
}

int main() {
    // 初始化测试用户
    strcpy(users[0].username, "zhangsan");
    strcpy(users[0].password, "123");
    user_count = 1;

    // 创建套接字
    int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (listen_fd == -1) {
        perror("socket error");
        return 1;
    }

    // 端口复用
    int opt = 1;
    setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    // 绑定地址
    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;
    server_addr.sin_port = htons(80);

    if (bind(listen_fd, (SA)&server_addr, sizeof(server_addr)) == -1) {
        perror("bind error");
        close(listen_fd);
        return 1;
    }

    // 监听
    if (listen(listen_fd, 10) == -1) {
        perror("listen error");
        close(listen_fd);
        return 1;
    }
    printf("账号:zhangsan / 123\n");

    // 主循环
    while (1) {
        struct sockaddr_in client_addr;
        socklen_t client_len = sizeof(client_addr);
        int conn_fd = accept(listen_fd, (SA)&client_addr, &client_len);
        if (conn_fd == -1) {
            perror("accept error");
            continue;
        }

        // 读取请求
        char buf[2048];
        ssize_t n = read(conn_fd, buf, sizeof(buf)-1);
        if (n <= 0) {
            close(conn_fd);
            continue;
        }
        buf[n] = '\0';

        // 解析请求路径
        char* method = strtok(buf, " ");
        char* path = strtok(NULL, " ");
        if (!method || !path) {
            close(conn_fd);
            continue;
        }

        // 路由处理
        if (strcmp(method, "GET") == 0) {
            // 登录页
            if (strcmp(path, "/") == 0 || strcmp(path, "/03.html") == 0) {
                send_file(conn_fd, "./03.html", FILE_HTML);
            }
            // 登录请求
            else if (strncmp(path, "/login", 6) == 0) {
                char* name = strstr(path, "username=");
                char* pass = strstr(path, "password=");
                if (name && pass) {
                    name += 9; pass += 9;
                    char* end = strchr(name, '&'); if (end) *end = '\0';
                    end = strchr(pass, '&'); if (end) *end = '\0';
                    if (check_user(name, pass) != -1) {
                        send_file(conn_fd, "./01.html", FILE_HTML);
                    } else {
                        send_file(conn_fd, "./04.html", FILE_HTML);
                    }
                } else {
                    send_text(conn_fd, "400 Bad Request", "<h1>参数错误</h1>");
                }
            }
            // 注册页
            else if (strcmp(path, "/register.html") == 0) {
                send_file(conn_fd, "./register.html", FILE_HTML);
            }
            // 注册请求
            else if (strncmp(path, "/register_submit", 15) == 0) {
                char* name = strstr(path, "username=");
                char* pass = strstr(path, "password=");
                char* pass2 = strstr(path, "password2=");
                if (name && pass && pass2) {
                    name += 9; pass += 9; pass2 += 10;
                    char* end = strchr(name, '&'); if (end) *end = '\0';
                    end = strchr(pass, '&'); if (end) *end = '\0';
                    end = strchr(pass2, '&'); if (end) *end = '\0';
                    if (strcmp(pass, pass2) != 0) {
                        send_text(conn_fd, "200 OK", "<h1>注册失败:两次密码不一致</h1><a href='http://127.0.0.1:80/register.html'>重新注册</a>");
                    } else if (register_user(name, pass) == -1) {
                        send_text(conn_fd, "200 OK", "<h1>注册失败:用户名已存在</h1><a href='http://127.0.0.1:80/register.html'>重新注册</a>");
                    } else {
                        send_text(conn_fd, "200 OK", "<h1>注册成功!</h1><a href='http://127.0.0.1:80/'>立即登录</a>");
                    }
                } else {
                    send_text(conn_fd, "400 Bad Request", "<h1>参数错误</h1>");
                }
            }
            // 商品首页(包含搜索功能)
            else if (strcmp(path, "/01.html") == 0) {
                send_file(conn_fd, "./01.html", FILE_HTML);
            }
            // 登录失败页
            else if (strcmp(path, "/04.html") == 0) {
                send_file(conn_fd, "./04.html", FILE_HTML);
            }
            // 搜索页(重定向到商品首页)
            else if (strcmp(path, "/search.html") == 0 || strncmp(path, "/search", 7) == 0) {
                send_file(conn_fd, "./01.html", FILE_HTML);
            }
            // 商品详情页
            else if (strncmp(path, "/goods", 6) == 0) {
                int goods_id = 1;
                char* id_str = strstr(path, "id=");
                if (id_str) goods_id = atoi(id_str + 3);
                send_goods_page(conn_fd, goods_id);
            }
            // 图片资源
            else if (strstr(path, ".jpg") || strstr(path, ".png")) {
                char file_path[256];
                sprintf(file_path, ".%s", path);
                send_file(conn_fd, file_path, strstr(path, ".png") ? FILE_PNG : FILE_JPG);
            }
            // 404
            else {
                send_text(conn_fd, "404 Not Found", "<h1>页面不存在</h1><a href='http://127.0.0.1:80/'>返回首页</a>");
            }
        }

        close(conn_fd);
    }

    close(listen_fd);
    return 0;
}

5.结果展示

在线商城

相关推荐
yanlou23313 小时前
[C++/Linux HTTP项目] HTTP服务器基于muduo高性能服务器搭载【深入详解】
运维·服务器·http·muduo库·http高性能服务器
不许哈哈哈14 小时前
HTTP协议基础(运维开发面试版)
http·面试·运维开发
王锋(oxwangfeng)15 小时前
Nginx 四层 TCP 与七层 HTTP 转发实战指南
tcp/ip·nginx·http
码农水水16 小时前
传音Java面试被问:HTTP/2的多路复用和头部压缩实现
java·开发语言·spring boot·后端·http·面试·职场和发展
她说..17 小时前
Java Web 开发:请求头、请求体、响应体 数据获取全攻略(附实战示例)
java·开发语言·spring boot·spring·http·spring mvc
奋斗者1号17 小时前
【MCP深度解析】Stdio vs SSE vs Streamable HTTP:三大通信机制全景对比与选型指南
网络·网络协议·http
宁雨桥17 小时前
HTTP 协议全景解析:无状态、明文与安全演进之路
网络协议·安全·http
zhaotiannuo_19981 天前
渗透测试之HTTP\HTTPS协议和BP抓包
网络协议·http·https
zzu123zsw1 天前
Chisel 实战指南:一条命令打通 HTTP 隧道
网络·网络协议·http
Julian.zhou1 天前
HTTP/1.1到HTTP/3全面演进指南:收益、实战成本与迁移策略
网络·网络协议·http