在 Ubuntu 虚拟机中实现 HTML 表单与 C 语言 HTTP 服务器交互

一、环境说明

  • 系统:Ubuntu 虚拟机(已安装基本开发工具,如 GCC)
  • 目标:通过 C 语言服务器托管 HTML 表单页面,并实现数据提交交互

二、核心文件准备

1. 创建 HTML 表单页面(xunfei.html

<!-- 保存路径:~/Linux-HTTP/xunfei.html -->
<html lang="zh-CN">
<head><meta charset="utf-8"><title>讯飞课堂表单</title>
</head>
<body><div style="text-align:center; height:500px"><h2>欢迎来到讯飞课堂!</h2><form action="/commit" method="post">姓名:<input type="text" name="name" required><br><br>年龄:<input type="text" name="age" required><br><br><button type="submit">提交</button></form></div>
</body>
</html>
  • 关键点
    • action="/commit":表单数据通过 POST 请求发送到服务器 /commit 路径
    • method="post":使用 POST 方法提交数据(适合传输敏感或大量数据)

2. 编写 C 语言 HTTP 服务器代码(server.c

// 保存路径:~/Linux-HTTP/server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/epoll.h>
#include <fcntl.h>#define PORT 8080          // 服务器端口
#define BUFFER_SIZE 4096   // 缓冲区大小
#define MAX_EVENTS 1000    // 最大事件数// 设置套接字为非阻塞模式
void set_nonblocking(int fd) {int flags = fcntl(fd, F_GETFL);fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}// 发送 HTTP 响应
void send_response(int client_fd, const char *status, const char *content_type, const char *content, size_t len) {char header[BUFFER_SIZE];snprintf(header, BUFFER_SIZE,"HTTP/1.1 %s\r\n""Content-Type: %s\r\n""Content-Length: %zu\r\n""Connection: close\r\n\r\n",status, content_type, len);write(client_fd, header, strlen(header));write(client_fd, content, len);
}// 处理请求
void handle_request(int client_fd) {char buffer[BUFFER_SIZE] = {0};ssize_t bytes_read = read(client_fd, buffer, BUFFER_SIZE - 1);if (bytes_read <= 0) return;// 解析请求行(Method、Path、Protocol)char *method = strtok(buffer, " ");char *path = strtok(NULL, " ");char *protocol = strtok(NULL, "\r\n");// 处理 GET 请求(返回 HTML 页面)if (strcmp(method, "GET") == 0) {char file_path[256] = "./xunfei.html";if (strcmp(path, "/") == 0) {  // 根路径映射到表单页面FILE *file = fopen(file_path, "r");if (!file) {send_response(client_fd, "404 Not Found", "text/plain", "File Not Found", 14);return;}fseek(file, 0, SEEK_END);long file_size = ftell(file);fseek(file, 0, SEEK_SET);char *content = malloc(file_size);fread(content, 1, file_size, file);fclose(file);send_response(client_fd, "200 OK", "text/html", content, file_size);free(content);}}// 处理 POST 请求(接收表单数据)else if (strcmp(method, "POST") == 0 && strstr(path, "/commit")) {char *body = strstr(buffer, "\r\n\r\n") + 4;  // 提取请求体printf("接收到表单数据:%s\n", body);send_response(client_fd, "200 OK", "text/plain", "提交成功", 9);}else {send_response(client_fd, "501 Not Implemented", "text/plain", "不支持的方法", 12);}close(client_fd);
}int main() {// 创建 socketint server_fd = socket(AF_INET, SOCK_STREAM, 0);if (server_fd < 0) {perror("socket创建失败");exit(1);}// 绑定端口struct sockaddr_in addr = {.sin_family = AF_INET,.sin_port = htons(PORT),.sin_addr.s_addr = INADDR_ANY};if (bind(server_fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {perror("bind失败");close(server_fd);exit(1);}// 监听连接if (listen(server_fd, SOMAXCONN) < 0) {perror("listen失败");close(server_fd);exit(1);}printf("服务器启动,监听端口 %d...\n", PORT);// 使用 epoll 处理并发连接int epoll_fd = epoll_create1(0);struct epoll_event event = {.events = EPOLLIN | EPOLLET, .data.fd = server_fd};epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &event);set_nonblocking(server_fd);struct epoll_event events[MAX_EVENTS];while (1) {int n = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);for (int i = 0; i < n; i++) {if (events[i].data.fd == server_fd) {  // 新连接struct sockaddr_in client_addr;socklen_t addr_len = sizeof(client_addr);int client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &addr_len);if (client_fd < 0) continue;set_nonblocking(client_fd);epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &event);} else {  // 处理请求handle_request(events[i].data.fd);epoll_ctl(epoll_fd, EPOLL_CTL_DEL, events[i].data.fd, NULL);}}}close(server_fd);return 0;
}
  • 核心逻辑
    • GET 请求:访问根路径 / 时返回 xunfei.html 页面
    • POST 请求:处理 /commit 路径的表单提交,打印数据并返回成功提示

三、操作步骤(Ubuntu 虚拟机中执行)

1. 创建项目目录

mkdir ~/Linux-HTTP && cd ~/Linux-HTTP  # 创建并进入项目目录

 2. 编写并保存文件

  • 使用 nano 或 vim 分别创建 xunfei.html 和 server.c,粘贴上述代码并保存。

3. 编译服务器

gcc server.c -o server  # 生成可执行文件

可能问题:若提示 gcc: command not found,需先安装 GCC:

sudo apt update && sudo apt install gcc -y  # (首次编译需执行,后续可忽略)

4. 运行服务器

./server  # 启动服务器

输出提示

服务器启动,监听端口 8080...

5. 访问测试(两种方式)

方式 1:虚拟机内直接访问

打开终端,使用 curl 测试:

curl http://localhost:8080  # 查看 HTML 页面内容
方式 2:宿主机通过浏览器访问
  • 前提:确保虚拟机网络设置为 桥接模式 或 NAT 模式,并开放端口。
  • 在宿主机浏览器输入:
http://虚拟机IP:8080  # 例如:http://192.168.1.100:8080

 输入表单数据并点击 “提交”,观察虚拟机终端输出:

接收到表单数据:name=张三&age=20  # 示例输出

 

四、常见问题与解决

1. 服务器启动失败(端口被占用)

lsof -i :8080  # 查看占用端口的进程
kill -9 <PID>   # 强制终止进程(PID 替换为实际进程号)

2. 无法访问页面(防火墙限制)

sudo ufw allow 8080/tcp  # 开放 8080 端口(Ubuntu 防火墙默认关闭,若启用需执行)

 

3. 表单提交后数据乱码

  • 确保 HTML 头部包含 <meta charset="utf-8">
  • 服务器处理 POST 数据时,需根据编码格式解析(示例代码直接打印原始数据,如需处理可添加 URL 解码逻辑)

五、扩展方向

  1. 优化请求处理
    • 支持更多 HTTP 方法(如 PUT、DELETE)
    • 添加静态文件缓存机制
  2. 数据持久化
    • 将表单数据存入文件或数据库(如 SQLite)
  3. 并发优化
    • 使用线程池替代 epoll 单线程模型
    • 实现长连接(Connection: keep-alive)

通过这个实例,你可以深入理解 HTTP 协议的基本交互流程,并为后续开发更复杂的 Web 服务奠定基础。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/news/906630.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LVS 负载均衡集群应用实战

前提:三台虚拟机,有nginx,要做负载 1. LVS-server 安装lvs管理软件 [root@lvs-server ~]# yum -y install ipvsadm 程序包:ipvsadm(LVS管理工具) 主程序:/usr/sbin/ipvsadm 规则保存工具:/usr/sbin/ipvsadm-save > /path/to/file 配置文件:/etc/sysconfig/ipvsad…

鸿蒙进阶——Framework之Want 隐式匹配机制概述

文章大纲 引言一、Want概述二、Want的类型1、显式Want2、隐式Want3、隐式Want的匹配 三、隐式启动Want 源码概述1、有且仅有一个Ability匹配2、有多个Ability 匹配需要弹出选择对话框3、ImplicitStartProcessor::ImplicitStartAbility3.1、GenerateAbilityRequestByAction3.1.1…

Rules and Monetization

The system creates rules that allow them to monetize. The system doesn’t just enforce rules — it creates them strategically to monetize control. &#x1f527; How It Works: Invent a rule (e.g., “You need a permit to sell food.”)Claim it’s for safety …

java中string类型的list集合放到redis的5种数据类型的那种比较合适呢,可以用StringRedisTemplate实现

在Java中&#xff0c;如何将一个String类型的List集合存储到Redis中&#xff0c;并且应该选择Redis的哪种数据类型。同时&#xff0c;用户还问到是否可以使用StringRedisTemplate来实现。 首先&#xff0c;我需要回忆一下Redis的5种主要数据类型&#xff1a;字符串&#xff08;…

基于DQN的学习资源难度匹配智能体

基于DQN的学习资源难度匹配智能体 下面我将实现一个基于DQN(深度Q网络)的智能体,用于根据用户的学习表现动态匹配适合难度的学习资源。这个系统可以应用于在线教育平台,根据用户的历史表现自动调整推荐资源的难度级别。 1. 环境设置 首先我们需要定义学习环境,这里我创建…

OrangePi Zero2开发指南:从SDK获取到交叉编译全流程详解

一、OrangePi Zero2 SDK说明 SDK 全称 Software Development Kit&#xff0c;即软件开发工具包。一般包括了一些工具&#xff08;如交叉编译工具链&#xff09;、库、文档和示例代码。香橙派的Linux SDK其实指的就是 orangepi-build 这套代码集&#xff0c;orangepibuild 在脚…

MATLAB NLP 工具箱 文本预处理教程

文章目录 前言一、文本预处理核心步骤二、MATLAB 实现示例三、高级预处理技术四、预处理流程整合五、性能优化与注意事项六、实战案例&#xff1a;IMDB 影评预处理 前言 以下是 MATLAB 自然语言处理 (NLP) 工具箱的文本预处理教程&#xff0c;涵盖核心步骤、代码实现及最佳实践…

大模型的量化与双重量化(1)

文章目录 大模型量化的含义和作用什么是量化量化的作用具体示例 双重量化的含义和作用什么是双重量化双重量化的具体实现双重量化的作用具体示例对比实际应用场景 大模型量化的含义和作用 什么是量化 量化是指将神经网络中的参数&#xff08;权重和激活值&#xff09;从高精度…

ES6 新增 API 方法

ES6 新增 API 方法 目录 ES6 新增 API 方法背景介绍数组方法1. Array.from()2. Array.of()3. find/findIndex4. includes5. flat/flatMap 对象方法1. Object.assign()2. Object.keys/values/entries3. Object.getOwnPropertyDescriptors() 字符串方法1. includes/startsWith/en…

vscode使用ssh链接服务器

vscode SSH vscode先下载remote ssh的插件&#xff0c;随后在左边的菜单栏里选择远程。 点击新建连接&#xff0c;输入用户名和地址&#xff0c;-p参数指定端口 ssh ubuntu{ip} -p xxx 随后就可以正常连接了&#xff0c;这里使用普通用户的用户名密码&#xff0c;别用root。 配…

基于FPGA的电子万年历系统开发,包含各模块testbench

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 5.完整工程文件 1.课题概述 基于FPGA的电子万年历系统开发,包含各模块testbench。主要包含以下核心模块&#xff1a; 时钟控制模块&#xff1a;提供系统基准时钟和计时功能。 日历计算模块&#xff1a…

C++ 的 out_ptr 和 inout_ptr

1 问题的起因 1.1 T** 或 T&* ​ C 的智能指针可以通过 get() 和 * 的重载得到原始指针 T*&#xff0c;遇到这样的 C 风格的函数的时候&#xff1a; void Process(Foo *ptr);std::unique_ptr<Foo> sp ...;Process(sp.get()); //调用 Process 函数Process() 函数以…

取消 Conda 默认进入 Base 环境

在安装 Conda 后&#xff0c;每次打开终端时默认会进入 base 环境。可以通过以下方法取消这一默认设置。 方法一&#xff1a;使用命令行修改配置 在终端中输入以下命令&#xff0c;将 auto_activate_base 参数设置为 false&#xff1a; conda config --set auto_activate_ba…

数字计数--数位dp

1.不考虑前导零 2.每一位计数&#xff0c;就是有点“数页码”的意思 P2602 [ZJOI2010] 数字计数 - 洛谷 相关题目&#xff1a;记得加上前导零 数页码--数位dp-CSDN博客 https://blog.csdn.net/2301_80422662/article/details/148160086?spm1011.2124.3001.6209 #include…

Redis学习打卡-Day5-Redis 持久化

单点 Redis 的一些问题 数据丢失&#xff1a;Redis 是内存存储&#xff0c;服务重启可能会丢失数据。solution&#xff1a;实现 Redis 数据持久化。并发能力&#xff1a;单节点 Redis 并发能力虽然不错&#xff0c;但也无法满足如618这样的高并发场景。solution&#xff1a;搭…

飞书知识问答深度体验:企业AI应用落地的典范产品

飞书知识问答深度体验&#xff1a;企业AI应用落地的典范产品 产品介绍-飞书知识问答是什么与常规通用大模型相比有何优点&#xff1f;大模型横行的时代&#xff0c;飞书知识问答对普通人和企业有何影响呢&#xff1f; 场景示例-不同角色可以用飞书知识问答做什么&#xff1f;对…

Python打卡训练营学习记录Day34

知识点回归&#xff1a; CPU性能的查看&#xff1a;看架构代际、核心数、线程数 GPU性能的查看&#xff1a;看显存、看级别、看架构代际 GPU训练的方法&#xff1a;数据和模型移动到GPU device上 类的call方法&#xff1a;为什么定义前向传播时可以直接写作self.fc1(x) CPU性…

Django的请求和响应+template模板

&#x1f31f; 如果这篇文章触动了你的心弦&#xff0c;请不要吝啬你的支持&#xff01; 亲爱的读者&#xff0c; 感谢你花时间阅读这篇分享。希望这里的每一个字都能为你带来启发或是让你会心一笑。如果你觉得这篇文章有价值&#xff0c;或者它解决了你一直以来的一个疑问&a…

Python |GIF 解析与构建(2):状态机解析

Python &#xff5c;GIF 解析与构建&#xff08;2&#xff09;&#xff1a;状态机解析 目录 Python &#xff5c;GIF 解析与构建&#xff08;2&#xff09;&#xff1a;状态机解析 引言 一、状态机概述 状态机的优势与改进方向 总结 引言 在《Python &#xff5c;GIF 解…

PCB设计实践(二十六)贴片电容与插件电容的全面解析:差异、演进与应用场景

一、核心差异&#xff1a;结构与性能对比 物理结构与封装形式 贴片电容&#xff08;Surface Mount Device, SMD&#xff09;采用扁平化设计&#xff0c;外形多为长方体或圆柱体&#xff0c;直接通过焊盘固定在电路板表面。其封装材料通常为陶瓷、聚合物或铝电解层&#xff0c;外…