应用——基于C语言实现的简易Web服务器开发

基于C语言实现的简易Web服务器开发

一、项目概述

本项目是一个基于C语言实现的多功能简易Web服务器,支持HTTP/1.1协议,能够处理HTML页面、图片文件请求,并实现基本的登录验证功能。

二、项目文件结构

复制代码
项目目录/
├── 01ser.c          # 主服务器程序
├── 01.html          # 首页HTML文件
├── 02.html          # 测试页面HTML文件
├── 03.html          # 登录页面HTML文件
├── 04.html          # 登录失败页面HTML文件
├── 1.jpg            # 图片资源
└── 2.png            # 图片资源(淘宝Logo)

三、核心源代码分析

3.1 主服务器程序 (01ser.c)

3.1.1 头文件和宏定义
复制代码
#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 <time.h>
#include <unistd.h>

typedef struct sockaddr*(SA);  // 简化socket地址类型

// 文件类型枚举
typedef enum {
    FILE_HTML,  // HTML文件
    FILE_PNG,   // PNG图片
    FILE_JPG    // JPG图片
} FILE_TYPE;
3.1.2 辅助函数

获取文件大小函数:

复制代码
long file_size(char* file) {
    int fd = open(file, O_RDONLY);
    if (-1 == fd) {
        perror("file size open error");
        fprintf(stderr, "filename is %s\n", file);
        return 0;
    }
    long size = lseek(fd, 0, SEEK_END);
    return size;
}

发送HTTP响应头函数:

复制代码
int send_head(int conn, char* file, FILE_TYPE type) {
    char buf[256] = {0};
    char* http_cmd[7] = {NULL};
    
    // HTTP响应头构造
    http_cmd[0] = "HTTP/1.1 200 OK\r\n";
    http_cmd[1] = "Date: Wed, 31 Dec 2025 01:58:31 GMT\r\n";
    
    // 根据文件类型设置Content-Type
    switch (type) {
        case FILE_HTML:
            http_cmd[2] = "Content-Type: text/html;charset=utf-8\r\n";
            break;
        case FILE_PNG:
            http_cmd[2] = "Content-Type: image/png\r\n";
            break;
        case FILE_JPG:
            http_cmd[2] = "Content-Type: image/jpeg\r\n";
            break;
        default:
            http_cmd[2] = "Content-Type: text/html;charset=utf-8\r\n";
    }
    
    http_cmd[3] = buf;
    sprintf(buf, "content-length: %ld\r\n", file_size(file));  // 设置内容长度
    http_cmd[4] = "Connection: keep-closed\r\n";
    http_cmd[5] = "Server: MYWEBSer\r\n";
    http_cmd[6] = "Content-Language: zh-CN\r\n\r\n";  // 空行表示头部结束

    // 发送所有HTTP头部信息
    int i = 0;
    for (i = 0; i < 7; i++) {
        send(conn, http_cmd[i], strlen(http_cmd[i]), 0);
    }
    return 0;
}

发送文件内容函数:

复制代码
int send_file(int conn, char* filename, FILE_TYPE type) {
    send_head(conn, filename, type);  // 先发送HTTP头部
    
    int fd = open(filename, O_RDONLY);
    if (-1 == fd) {
        perror("open");
        return 1;
    }
    
    // 分块读取并发送文件内容
    while (1) {
        char buf[4096] = {0};
        int rd_ret = read(fd, buf, sizeof(buf));
        if (rd_ret <= 0) {
            break;  // 读取完毕
        }
        send(conn, buf, rd_ret, 0);
    }
    close(fd);
    return 0;
}
3.1.3 主函数流程
cpp 复制代码
int main(int argc, char** argv) {
    // 1. 创建监听套接字(用于三次握手)
    int listfd = socket(AF_INET, SOCK_STREAM, 0);
    if (-1 == listfd) {
        perror("socket");
        return 1;
    }
    
    // 2. 绑定服务器地址和端口
    struct sockaddr_in ser, cli;
    bzero(&ser, sizeof(ser));
    bzero(&cli, sizeof(cli));
    ser.sin_family = AF_INET;
    ser.sin_port = htons(80);  // 使用HTTP默认端口80
    ser.sin_addr.s_addr = INADDR_ANY;  // 监听所有网络接口
    
    int ret = bind(listfd, (SA)&ser, sizeof(ser));
    if (-1 == ret) {
        perror("bind");
        return 1;
    }
    
    // 3. 开始监听连接请求
    listen(listfd, 3);  // 最大等待队列为3
    
    socklen_t len = sizeof(cli);
    
    // 4. 主循环:接受并处理客户端请求
    while (1) {
        int conn = accept(listfd, (SA)&cli, &len);
        if (-1 == conn) {
            perror("accept");
            continue;
        }
        
        // 接收HTTP请求
        char buf[1024] = {0};
        int ret = recv(conn, buf, sizeof(buf), 0);
        if (ret <= 0) {
            close(conn);
            continue;
        }
        
        printf("收到请求:\n%s", buf);  // 打印请求信息
        fflush(stdout);
        
        // 解析HTTP请求行
        char* method = NULL;
        char* url = NULL;
        char* ver = NULL;
        method = strtok(buf, " ");
        url = strtok(NULL, " ");
        ver = strtok(NULL, "\r");
        
        // 5. 根据URL分发请求
        // 处理根目录请求
        if (0 == strcmp(url, "/")) {
            send_file(conn, "./03.html", FILE_HTML);
        }
        // 处理登录请求
        else if (0 == strncmp(url, "/login", 6)) {
            char* name = NULL;
            char* pass = NULL;
            char* end = NULL;
            
            // 解析URL中的用户名和密码参数
            name = strchr(url, '=');
            name += 1;
            end = strchr(name, '&');
            *end = '\0';
            pass = strchr(end + 1, '=');
            pass += 1;
            
            // 简单验证(用户名:zhangsan,密码:123)
            if (0 == strcmp(name, "zhangsan") && 0 == strcmp(pass, "123")) {
                send_file(conn, "./01.html", FILE_HTML);  // 登录成功
            } else {
                send_file(conn, "./04.html", FILE_HTML);  // 登录失败
            }
        }
        // 处理图片文件请求
        else if (strlen(url) > 4 && 0 == strcmp(&url[strlen(url) - 4], ".jpg")) {
            send_file(conn, url + 1, FILE_JPG);  // 跳过开头的'/'
        }
        else if (strlen(url) > 4 && 0 == strcmp(&url[strlen(url) - 4], ".ico")) {
            send_file(conn, "1.png", FILE_PNG);  // 用1.png替代favicon.ico
        }
        else if (strlen(url) > 4 && 0 == strcmp(&url[strlen(url) - 4], ".png")) {
            send_file(conn, url + 1, FILE_PNG);
        }
        
        close(conn);  // 关闭当前连接
    }
    
    close(listfd);  // 关闭监听套接字
    return 0;
}
点击并拖拽以移动

四、HTML页面分析

4.1 主页 (01.html)

复制代码
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8"> <!-- 设置中文编码 -->
    <title>中文测试。。。。</title>
    <style>
    body {
        background-color: #E6E6FA;  /* 淡紫色背景 */
    }
    </style>
</head>
<body>
    <!-- 基础HTML元素演示 -->
    这里是测试body测试内容。。。
    <h1>h1字体</h1>
    <h3 align='center'>h3字体</h3>  <!-- 居中对齐 -->
    <h6 align='right'>h6字体</h6>   <!-- 右对齐 -->
    
    <!-- 段落和换行 -->
    <p><h1>展望2026,中国房地产政&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;策路径已清晰明确...</h1></p>
    <hr>  <!-- 水平分割线 -->
    <p>并首次将"控增量、<br><br>去库存、优供给"协同部署...</p>
    
    <!-- 文本格式化 -->
    <b>加粗字体</b>
    <i>字体倾斜</i>
    <del>删除文字</del>
    <u>下划线</u>
    <small>超小字体</small>
    <br>h<sub>2</sub>0  <!-- 下标 -->
    <br>100m<sup>2</sup>  <!-- 上标 -->
    
    <!-- 注音和标记 -->
    <ruby>二姐 <rt>(er) (jie)<rt></ruby>
    <mark>加黄色背景</mark>
    
    <!-- 链接 -->
    <a href="http://www.baidu.com">baidu</a>
    <a href="02.html">go to 2</a>
    <a href="http://www.taobao.com">
        <img src="2.png">  <!-- 图片链接 -->
    </a>
    
    <!-- 图片 -->
    <img src="1.jpg" alt="美女" width="200" height="200">
    
    <!-- 无序列表 -->
    <ul>
        <li>列表1</li>
        <li>列表2</li>
        <li>列表3</li>
        <li>列表4</li>
    </ul>
    
    <!-- 有序列表 -->
    <ol>
        <li>列表1</li>
        <li>列表2</li>
        <li>列表3</li>
        <li>列表4</li>
    </ol>
</body>
</html>

4.2 登录页面 (03.html)

复制代码
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>登录</title>
</head>
<body>
    <!-- 登录表单,提交到/login路径 -->
    <form action='login'>
        用户名:<input type='text' name='username' required='required' placeholder='请输入qq账号'>
        密码:<input type='password' name=' userpass' required='required' placeholder='QQ的密码'>
        <input type='submit'>  <!-- 提交按钮 -->
    </form>
</body>
</html>

五、技术要点总结

5.1 网络编程核心

  1. Socket编程流程

    • socket()bind()listen()accept()recv()/send()close()
  2. HTTP协议处理

    • 解析请求行:GET /path HTTP/1.1

    • 构造响应头:状态行 + 头部字段 + 空行 + 正文

  3. 文件传输

    • 分块读取大文件(4096字节缓冲区)

    • 根据文件扩展名设置正确的Content-Type

5.2 安全性考虑

  1. 输入验证

    复制代码
    // URL解析时防止缓冲区溢出
    if (strlen(url) > 4 && 0 == strcmp(&url[strlen(url) - 4], ".jpg"))
  2. 路径遍历防护

    复制代码
    send_file(conn, url + 1, FILE_JPG);  // 跳过'/',防止访问上级目录

5.3 性能优化

  1. 短连接模式

    • 每个请求处理完毕后立即关闭连接

    • 头部设置:Connection: keep-closed

  2. 错误处理

    • 使用perror()输出错误信息

    • 失败的连接继续处理下一个请求

六、编译和运行

复制代码
# 编译服务器程序
gcc 01ser.c -o webserver

# 以root权限运行(需要绑定80端口)
sudo ./webserver

# 访问测试
# 浏览器访问:http://localhost/
# 或使用curl:curl http://localhost/login?username=zhangsan&userpass=123

七、学习收获

通过本项目,可以学习到:

  1. HTTP协议:了解请求/响应格式

  2. 网络编程:掌握基本的Socket编程

  3. C语言实践:文件操作、字符串处理、内存管理

  4. Web开发基础:HTML页面设计、表单处理

  5. 服务器架构:请求分发、资源管理

相关推荐
.似水2 小时前
Python Socket
开发语言·python
码农水水2 小时前
京东Java面试被问:系统限流的实现方式
java·开发语言·面试
Sheep Shaun2 小时前
STL中的map和set:红黑树的优雅应用
开发语言·数据结构·c++·后端·c#
宁大小白2 小时前
pythonstudy Day45
开发语言·python·深度学习
week_泽2 小时前
OCR学习笔记,调用免费百度api
笔记·学习·ocr
week_泽2 小时前
离线OCR笔记及代码
笔记·ocr
幺零九零零3 小时前
Docker底层-IPC Namespace(进程间通信隔离)
运维·docker·容器
Yu_iChan3 小时前
Day03 公共字段填充与菜品管理
java·开发语言
独自破碎E3 小时前
如何防止接口被恶意刷量?
java·开发语言