如何为Linux下的C语言服务器-客户端程序编写GCC Makefile?
嘿,这个问题其实很常见——用一个Makefile就完全能搞定你的所有需求,而且这也是Linux C项目里最常规、最高效的做法,完全没必要分开写两个。下面我给你详细拆解实现方案,还顺带解决你提到的「仅允许运行一个服务端」的小需求。
一、单Makefile的实现方案
一个Makefile可以同时管理客户端和服务端的编译规则,既方便维护,又能一键生成两个可执行文件。
1. 编写Makefile内容
创建一个名为Makefile的文件,内容如下:
# 定义编译器和编译选项 CC = gcc # -Wall/-Wextra 开启更多警告,-pthread 支持多线程(因为要处理多客户端) CFLAGS = -Wall -Wextra -pthread # 默认目标:同时生成客户端和服务端 all: client server # 编译客户端 client: client.c util.h $(CC) $(CFLAGS) -o $@ $< # 编译服务端 server: server.c util.h $(CC) $(CFLAGS) -o $@ $< # 清理生成的可执行文件 clean: rm -f client server
2. 为什么选单Makefile?
- 管理成本低:所有编译规则集中在一个文件里,修改、查看都更方便;
- 操作高效:只需要执行一次
make就能同时生成两个可执行文件,不用分别处理; - 扩展性好:后续如果新增源文件(比如util.c),直接在规则里添加即可,无需拆分文件。
二、实现「仅允许一个服务端运行」的小技巧
要保证服务端单实例运行,最可靠的方式是用文件锁(flock),它能在进程层面实现互斥,而且进程退出后锁会自动释放,不会留下脏数据。
1. 在server.c中添加单实例校验逻辑
在服务端的main函数开头加入以下代码:
#include <fcntl.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #define PID_FILE "/var/run/my_server.pid" // 存储服务端PID的文件路径 int main() { // 打开或创建PID文件 int pid_fd = open(PID_FILE, O_RDWR | O_CREAT, 0644); if (pid_fd == -1) { perror("Failed to open PID file"); exit(EXIT_FAILURE); } // 尝试加非阻塞排他锁:如果锁失败,说明已有服务端在运行 if (flock(pid_fd, LOCK_EX | LOCK_NB) == -1) { fprintf(stderr, "Error: Server is already running!\n"); exit(EXIT_FAILURE); } // 将当前进程PID写入文件(方便后续排查) char pid_str[20]; snprintf(pid_str, sizeof(pid_str), "%d\n", getpid()); ftruncate(pid_fd, 0); // 清空文件内容 write(pid_fd, pid_str, strlen(pid_str)); // --------------------- // 这里写你的服务端核心逻辑 // 比如监听端口、接受客户端连接、创建线程处理请求等 // --------------------- return 0; }
代码说明:
LOCK_EX:申请排他锁,同一时间只有一个进程能持有;LOCK_NB:非阻塞模式,锁失败时直接返回错误,不会阻塞等待;- PID文件的作用:不仅用于加锁,还能让你通过
cat /var/run/my_server.pid查看当前运行的服务端PID。
三、编译与运行说明
编译项目:在终端进入代码目录,执行:
make执行完成后,目录下会生成
client和server两个可执行文件。清理编译产物:如果需要重新编译,先清理旧文件:
make clean运行服务端:
./server如果已经有一个服务端在运行,会直接输出错误并退出。
运行多客户端:打开多个终端窗口,每个窗口执行:
./client只要你的服务端代码已经实现了多线程(或多路复用)处理客户端连接,就能同时响应多个客户端的请求。
内容的提问来源于stack exchange,提问作者Kelve




