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
简单的函数声明头文件。
三、数据库设计
表结构推测
-
admin_user表(用户表)
-
user_id:用户ID(主键) -
user_name:用户名 -
password:密码
-
-
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
七、改进建议
-
安全性增强
-
添加密码哈希存储(当前为明文)
-
实现Session管理
-
增加请求频率限制
-
-
功能扩展
-
添加商品分类筛选
-
实现购物车功能
-
添加分页支持
-
-
代码优化
-
分离路由配置到独立文件
-
添加配置文件支持
-
实现日志记录系统
-
-
性能提升
-
添加HTTP Keep-Alive支持
-
实现图片缓存机制
-
数据库连接池
-
八、总结
这是一个功能完整的C语言Web服务器项目,展示了:
-
基本的HTTP服务器实现
-
数据库操作与SQLite集成
-
动态HTML页面生成
-
文件系统操作
-
URL编码解码处理