Commit 731707c8 authored by xiezhi's avatar xiezhi

Initial commit: C 12.2.0 HTTP server template

- Add hello_world.c with HTTP server implementation
- Add Dockerfile for containerization
- Add docker-entrypoint.sh for deployment
- Add Makefile for build automation
- Add .eazy configuration file
- Add .resource directory with dev config
- Add comprehensive README.md
- Support environment variable port configuration
- Ready for production deployment
parents
{
"version": "1.0.0",
"name": "C 12.2.0 HTTP服务器",
"tags": ["c", "http", "web", "server", "native"],
"description": "基于C 12.2.0的简单HTTP服务器应用",
"init_commands": ["make install"],
"start_commands": ["make run"],
"stop_commands": ["make stop"],
"mapping_ports": {
"APP_PORT_1": 8000,
"APP_PORT_2": 8001,
"APP_PORT_3": 8002,
"APP_PORT_4": 8003
},
"deploy": [
{
"image_name": "c12.2.0-hello-world",
"app_access_entry": true,
"ports": [
{
"name": "APP_PORT_1",
"main_port": true,
"inner_access_env_key": "API_URL",
"open_access": true,
"open_access_env_key": "API_DOMAIN"
},
{
"name": "APP_PORT_2",
"main_port": false,
"inner_access_env_key": null,
"open_access": false,
"open_access_env_key": null
}
],
"docker_file_dir": "./",
"deploy_resources": {
"cpu_limit": 0.5,
"memory_limit": 2,
"storage_limit": 1
}
}
]
}
# 依赖文件
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# 环境变量文件
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
# 构建输出
dist/
build/
out/
# 日志文件
*.log
# 运行时数据
pids
*.pid
*.seed
*.pid.lock
# 覆盖率目录
coverage/
.nyc_output
# 依赖目录
jspm_packages/
# 可选的npm缓存目录
.npm
# 可选的eslint缓存
.eslintcache
# 可选的REPL历史
.node_repl_history
# 输出目录
*.tgz
# Yarn完整性文件
.yarn-integrity
# dotenv环境变量文件
.env
# 编辑器目录和文件
.idea/
*.swp
*.swo
*~
# 操作系统生成的文件
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
# 临时文件
*.tmp
*.temp
# 备份文件
*.bak
*.backup
{
"recommendations": [
"pomdtr.excalidraw-editor",
"editorconfig.editorconfig",
"lokalise.i18n-ally",
"ms-vscode.cpptools",
"ms-vscode.cpptools-extension-pack",
"ms-vscode.cpptools-themes",
"formulahendry.code-runner"
]
}
\ No newline at end of file
# 使用C12.2.0作为基础镜像
FROM hb.eazytec-cloud.com/eazytec/eazydevelop-c:ubuntu22.04-c12.2.0-latest
# 设置工作目录
WORKDIR /app
# 复制源代码
COPY . ./
# 设置启动脚本权限
RUN chmod +x docker-entrypoint.sh
# 设置环境变量
ENV APP_PORT_1=8000
# 暴露端口
EXPOSE $APP_PORT_1
# 运行程序(让entrypoint脚本处理编译和运行)
ENTRYPOINT ["./docker-entrypoint.sh", "production"]
# 变量定义
APP_NAME = c12.2.0-hello-world
C_VERSION = 12.2.0
BUILD_DIR = build
BINARY_FILE = $(BUILD_DIR)/hello_world
APP_PORT = $(or $(APP_PORT_1),8000)
LOG_FILE = $(abspath $(dir $(lastword $(MAKEFILE_LIST))))/.logs/app.log
PID_FILE = $(abspath $(dir $(lastword $(MAKEFILE_LIST))))/.logs/app.pid
# C编译器设置
CC = gcc
CFLAGS = -Wall -Wextra -std=c11 -O2
LDFLAGS = -pthread
# 默认目标
.PHONY: all
all: install
# 安装命令
.PHONY: install
install:
@echo "检查 C 编译器版本..."
@$(CC) --version
@echo "检查项目是否已编译..."
@if [ ! -f "$(BINARY_FILE)" ]; then \
echo "正在编译C源代码..."; \
mkdir -p $(BUILD_DIR); \
$(CC) $(CFLAGS) -o $(BINARY_FILE) hello_world.c $(LDFLAGS); \
echo "✅ C源代码编译完成"; \
else \
echo "✅ C源代码已编译"; \
fi
@echo "✅ 安装完成!C项目已准备就绪"
# 运行命令(后台运行HTTP服务器)
.PHONY: run
run: install
@echo "正在启动 C HTTP 服务器..."
@mkdir -p $(dir $(LOG_FILE))
@if [ -f "$(PID_FILE)" ]; then \
echo "服务器已在运行中,PID: $$(cat $(PID_FILE))"; \
exit 1; \
fi
@echo "启动服务器在端口 $(APP_PORT)..."
@APP_PORT_1=$(APP_PORT) nohup $(BINARY_FILE) > $(LOG_FILE) 2>&1 & echo $$! > $(PID_FILE)
@echo "✅ C HTTP 服务器已启动,PID: $$(cat $(PID_FILE))"
@echo "服务器运行在: http://localhost:$(APP_PORT)/"
@echo "查看日志: tail -f $(LOG_FILE)"
# 停止命令
.PHONY: stop
stop: kill
# 强制杀死所有相关C进程
.PHONY: kill
kill:
@echo "正在查找并杀死所有相关的C进程..."
@PIDS=$$(ps aux | grep -E "hello_world|$(APP_NAME)" | grep -v grep | awk '{print $$2}'); \
if [ -n "$$PIDS" ]; then \
echo "找到相关进程: $$PIDS"; \
for PID in $$PIDS; do \
echo "正在杀死进程 $$PID..."; \
kill -9 $$PID 2>/dev/null || true; \
done; \
sleep 1; \
echo "✅ 所有相关C进程已被强制停止"; \
else \
echo "未找到相关的C进程"; \
fi
@rm -f $(PID_FILE)
@echo "✅ 强制停止操作完成"
# 重启命令
.PHONY: restart
restart: stop run
# 查看状态
.PHONY: status
status:
@if [ -f "$(PID_FILE)" ]; then \
PID=$$(cat $(PID_FILE)); \
if ps -p $$PID > /dev/null 2>&1; then \
echo "✅ 服务器正在运行,PID: $$PID"; \
echo "端口: $(APP_PORT)"; \
echo "日志: $(LOG_FILE)"; \
else \
echo "❌ 服务器未运行(PID文件存在但进程不存在)"; \
rm -f $(PID_FILE); \
fi; \
else \
echo "❌ 服务器未运行"; \
fi
# 开发模式运行
.PHONY: dev
dev:
@echo "开发模式运行C程序..."
@APP_PORT_1=$(APP_PORT) $(BINARY_FILE)
# 调试模式编译
.PHONY: debug
debug:
@echo "调试模式编译C程序..."
@mkdir -p $(BUILD_DIR)
@$(CC) $(CFLAGS) -g -DDEBUG -o $(BINARY_FILE) hello_world.c $(LDFLAGS)
@echo "✅ 调试版本编译完成"
# 清理编译文件
.PHONY: clean
clean:
@echo "清理编译文件..."
rm -rf $(BUILD_DIR)
@echo "清理完成!"
# 显示帮助信息
.PHONY: help
help:
@echo "C 12.2.0 HTTP 服务器项目"
@echo ""
@echo "可用命令:"
@echo " make install - 安装依赖并编译项目"
@echo " make run - 启动HTTP服务器(后台运行)"
@echo " make stop - 停止HTTP服务器(优雅停止)"
@echo " make kill - 强制杀死所有相关C进程"
@echo " make restart - 重启HTTP服务器"
@echo " make status - 查看服务器状态"
@echo " make dev - 开发模式运行"
@echo " make debug - 调试模式编译"
@echo " make clean - 清理编译文件"
@echo " make help - 显示此帮助信息"
@echo ""
@echo "示例:"
@echo " make run # 启动服务器"
@echo " make status # 查看状态"
@echo " make stop # 优雅停止服务器"
@echo " make kill # 强制停止所有相关进程"
@echo " make restart # 重启服务器"
@echo " make dev # 开发模式运行"
@echo " make debug # 编译调试版本"
@echo ""
@echo "环境变量:"
@echo " APP_PORT_1 - 服务器端口(默认: 8000)"
# C 12.2.0 HTTP 服务器模板
一个基于 C 12.2.0 的简单 HTTP 服务器应用模板,支持环境变量配置和 Docker 容器化部署。
## 🚀 项目特性
- **C 12.2.0**: 使用最新的 C 语言版本
- **HTTP 服务器**: 提供简单的 HTTP 服务
- **环境变量配置**: 支持通过环境变量配置端口
- **Docker 支持**: 完整的容器化部署方案
- **Makefile 构建**: 便捷的构建和运行命令
- **生产就绪**: 支持开发和生产环境
- **原生性能**: 高性能的 C 语言实现
## 📁 项目结构
```
c12.2.0-template/
├── hello_world.c # 主程序文件
├── Dockerfile # Docker 镜像构建文件
├── docker-entrypoint.sh # Docker 启动脚本
├── Makefile # 构建脚本
├── .eazy # 应用配置文件
├── .resource/
│ ├── resource_dev.json # 开发环境资源配置
│ └── resource_deploy.json # 部署环境资源配置
├── .vscode/ # VS Code 配置
└── README.md # 项目说明文档
```
## 🛠️ 环境要求
- C 12.2.0 或更高版本 (GCC)
- Docker (可选,用于容器化部署)
- Make (可选,用于使用 Makefile 命令)
## 📦 快速开始
### 1. 克隆项目
```bash
git clone <repository-url>
cd c12.2.0-template
```
### 2. 使用 Makefile 运行
```bash
# 安装依赖并编译
make install
# 启动服务器(后台运行)
make run
# 开发模式运行
make dev
# 调试模式编译
make debug
# 查看服务器状态
make status
# 停止服务器
make stop
# 重启服务器
make restart
# 清理编译文件
make clean
# 查看帮助
make help
```
### 3. 直接使用 GCC 命令
```bash
# 编译程序
gcc -Wall -Wextra -std=c11 -O2 -o hello_world hello_world.c -pthread
# 运行程序
./hello_world
```
## ⚙️ 配置说明
### 环境变量
| 变量名 | 默认值 | 说明 |
|--------|--------|------|
| `APP_PORT_1` | 8000 | HTTP 服务器监听端口 |
| `PORT` | 8000 | 兼容旧版本的环境变量 |
### 端口配置
应用支持多个端口配置:
- `APP_PORT_1`: 主端口 (默认: 8000)
- `APP_PORT_2`: 备用端口 (默认: 8001)
- `APP_PORT_3`: 备用端口 (默认: 8002)
- `APP_PORT_4`: 备用端口 (默认: 8003)
## 🐳 Docker 部署
### 构建镜像
```bash
docker build -t c12.2.0-hello-world .
```
### 运行容器
```bash
# 使用默认端口
docker run -p 8000:8000 c12.2.0-hello-world
# 使用自定义端口
docker run -p 9000:9000 -e APP_PORT_1=9000 c12.2.0-hello-world
```
### 开发环境运行
```bash
# 开发模式(直接运行源码)
docker run -p 8000:8000 c12.2.0-hello-world development
```
## 🔧 开发指南
### 项目结构说明
- `hello_world.c`: 主程序入口,包含 HTTP 服务器逻辑
- `docker-entrypoint.sh`: Docker 启动脚本,支持开发和生产环境
- `Makefile`: 构建和运行脚本
- `.eazy`: 应用配置文件,包含部署信息
### 添加新功能
1.`hello_world.c` 中添加新的路由处理函数
2. 修改 HTTP 响应处理逻辑
3. 重新编译和运行
示例:
```c
// 添加新的响应处理
char *custom_response = "HTTP/1.1 200 OK\r\n"
"Content-Type: text/html\r\n"
"Content-Length: 20\r\n"
"\r\n"
"Custom Response!";
```
### 环境变量使用
在代码中获取环境变量:
```c
char* env_port = getenv("APP_PORT_1");
if (env_port != NULL) {
int port = atoi(env_port);
// 使用端口
}
```
## 📊 监控和日志
### 查看日志
```bash
# 使用 Makefile 查看日志
tail -f .logs/app.log
# 查看服务器状态
make status
```
### 健康检查
访问以下端点进行健康检查:
- `GET /`: 返回 "Hello, World from Development Server!"
## 🚀 部署到生产环境
### 使用 Docker
1. 构建生产镜像:
```bash
docker build -t c12.2.0-hello-world:latest .
```
2. 运行生产容器:
```bash
docker run -d \
--name c-app \
-p 8000:8000 \
-e APP_PORT_1=8000 \
c12.2.0-hello-world:latest
```
### 使用 Makefile
```bash
# 生产环境运行
make run
```
## 🔍 故障排除
### 常见问题
1. **端口被占用**
```bash
# 查看端口占用
lsof -i :8000
# 停止占用端口的进程
make stop
```
2. **编译错误**
```bash
# 清理并重新编译
make clean
make install
```
3. **Docker 构建失败**
```bash
# 检查 Dockerfile 语法
docker build --no-cache -t test .
```
### 调试模式
```bash
# 调试模式编译
make debug
# 开发模式运行(显示详细日志)
make dev
# 直接运行 C 程序
./hello_world
```
## 📝 API 文档
### 端点列表
| 方法 | 路径 | 描述 | 响应 |
|------|------|------|------|
| GET | `/` | 健康检查 | "Hello, World from Development Server!" |
### 响应格式
所有响应都是纯文本格式。
## 🎯 性能特性
- **原生性能**: 使用 C 语言实现,性能优异
- **内存效率**: 低内存占用
- **并发处理**: 支持多线程处理请求
- **信号处理**: 支持优雅关闭
## 🤝 贡献指南
1. Fork 项目
2. 创建功能分支 (`git checkout -b feature/AmazingFeature`)
3. 提交更改 (`git commit -m 'Add some AmazingFeature'`)
4. 推送到分支 (`git push origin feature/AmazingFeature`)
5. 打开 Pull Request
## 📄 许可证
本项目采用 MIT 许可证 - 查看 [LICENSE](LICENSE) 文件了解详情。
## 📞 支持
如果您遇到任何问题或有任何建议,请:
1. 查看 [故障排除](#故障排除) 部分
2. 创建 [Issue](../../issues)
3. 联系维护者
---
**注意**: 这是一个模板项目,您可以根据实际需求进行修改和扩展。
#!/bin/bash
# 设置错误时退出
set -e
# 获取环境变量,默认为production
app_env=${1:-production}
# 定义构建目标
build_target="hello_world"
# 获取端口号,优先使用环境变量
APP_PORT=${APP_PORT_1:-8000}
# 开发环境命令
dev_commands() {
echo "🚀 运行开发环境命令..."
echo "📝 使用端口: $APP_PORT"
echo "🔧 直接运行C程序..."
APP_PORT_1=$APP_PORT ./$build_target
}
# 生产环境命令
prod_commands() {
echo "🚀 运行生产环境命令..."
echo "📝 使用端口: $APP_PORT"
# 检查是否已经编译过
if [ ! -f "$build_target" ]; then
echo "🔨 编译C程序..."
gcc -Wall -Wextra -std=c11 -O2 -o $build_target hello_world.c -pthread
echo "✅ 编译完成"
else
echo "✅ 程序已编译,直接运行"
fi
# 运行编译后的程序
echo "🎯 启动HTTP服务器..."
APP_PORT_1=$APP_PORT ./$build_target
}
# 显示启动信息
echo "=========================================="
echo "🔧 C 12.2.0 HTTP 服务器启动脚本"
echo "=========================================="
echo "环境: $app_env"
echo "端口: $APP_PORT"
echo "时间: $(date)"
echo "=========================================="
# 根据环境变量决定运行模式
if [ "$app_env" = "production" ] || [ "$app_env" = "prod" ]; then
echo "🏭 检测到生产环境"
prod_commands
else
echo "🛠️ 检测到开发环境"
dev_commands
fi
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <signal.h>
#include <time.h>
#include <errno.h>
#define BUFFER_SIZE 1024
// Global variable for graceful shutdown
volatile sig_atomic_t keep_running = 1;
// Signal handler function
void handle_signal(int sig) {
keep_running = 0;
}
// Get current time string
void get_time_string(char* buffer, size_t size) {
time_t now = time(NULL);
strftime(buffer, size, "%Y-%m-%d %H:%M:%S", localtime(&now));
}
/**
* 从环境变量获取端口号
* 优先使用APP_PORT_1,如果不存在则使用PORT,最后使用默认值8000
* @return 端口号
*/
int get_port() {
char* env_port = getenv("APP_PORT_1");
if (env_port != NULL) {
return atoi(env_port);
}
// 兼容旧的PORT环境变量
env_port = getenv("PORT");
if (env_port != NULL) {
return atoi(env_port);
}
// 默认端口8000
return 8000;
}
int main() {
int server_fd, new_socket;
struct sockaddr_in address;
int addrlen = sizeof(address);
char buffer[BUFFER_SIZE] = {0};
char time_buffer[64];
// Setup signal handlers
signal(SIGINT, handle_signal);
signal(SIGTERM, handle_signal);
int port = get_port();
// Set response content based on environment
char *http_response;
http_response = "HTTP/1.1 200 OK\r\n"
"Content-Type: text/html\r\n"
"Content-Length: 37\r\n"
"\r\n"
"Hello, World from Development Server!";
// Create socket
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("Socket creation failed");
exit(EXIT_FAILURE);
}
// Set socket options to reuse address and port
int opt = 1;
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {
perror("setsockopt failed");
exit(EXIT_FAILURE);
}
// Configure socket address
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY; // Equivalent to 0.0.0.0
address.sin_port = htons(port);
// Bind socket to port
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {
perror("Bind failed");
exit(EXIT_FAILURE);
}
// Start listening
if (listen(server_fd, 10) < 0) {
perror("Listen failed");
exit(EXIT_FAILURE);
}
printf("Server started successfully, listening on 0.0.0.0:%d\n", port);
// Main loop
while (keep_running) {
// Set accept timeout
struct timeval tv;
tv.tv_sec = 1;
tv.tv_usec = 0;
setsockopt(server_fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv);
new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen);
if (new_socket < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
continue; // Timeout, continue loop
}
perror("Accept failed");
continue;
}
// Get client IP
char client_ip[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &(address.sin_addr), client_ip, INET_ADDRSTRLEN);
// Log access
get_time_string(time_buffer, sizeof(time_buffer));
printf("[%s] Request from %s\n", time_buffer, client_ip);
read(new_socket, buffer, BUFFER_SIZE);
write(new_socket, http_response, strlen(http_response));
close(new_socket);
}
// Cleanup resources
close(server_fd);
printf("\nServer shutting down gracefully...\n");
return 0;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment