慕尼黑0001808 的学生作业:
【图片】
// global.h
#ifndef _GLOBAL_H_
#define _GLOBAL_H_
#include
#include
typedef struct {
bool capture; //0表示不采集视频数据,1表示采集视频数据.
void *start; //存放视频数据的起始地址.
int length; //视频数据的长度.
pthread_mutex_t update_lock; //线程锁.
pthread_cond_t update_cond; //线程条件变量.
} global_t;
extern global_t global;
extern void init_global(void);
extern void cleanup_global(void);
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#define LENGTH_OF(x) (sizeof(x)/sizeof(x[0]))
#define PICTURE_SIZE (1024 * 1024 * 1024) //预分配图片的大小:1GB.
#define BUFFER_SIZE 1024
#endif //_GLOBAL_H_
//global.c
#include "global.h"
#include
#include
#include
global_t global;
void init_global()
{
global.capture = true;
global.length = 0;
// 初始化互斥锁
if (pthread_mutex_init(&global.update_lock, NULL) != 0) {
perror("pthread_mutex_init");
exit(1);
}
// 初始化条件变量
if (pthread_cond_init(&global.update_cond, NULL) != 0) {
perror("pthread_cond_init");
exit(1);
}
// 分配内存
global.start = malloc(PICTURE_SIZE);
if (global.start == NULL) {
perror("malloc");
exit(1);
}
}
void cleanup_global(void)
{
// 停止采集
global.capture = false;
// 唤醒所有等待的线程
pthread_cond_broadcast(&global.update_cond);
// 释放内存
if (global.start != NULL) {
free(global.start);
global.start = NULL;
}
// 销毁条件变量和互斥锁
pthread_cond_destroy(&global.update_cond);
pthread_mutex_destroy(&global.update_lock);
printf("Global resources cleaned up\n");
}
// camera.h
#include
#include
#include
#include
#include
#include
#include
#include
#include
/* 希望摄像头输出的分辨率 */
#define IMG_WIDTH 640
#define IMG_HEIGHT 480
/* 防止 RGB 超过 0~255 的限幅宏 */
#define CLIP(x) ((x)255?255:(x)))
/* 申请 4 块内核 DMA 视频缓冲 */
#define BUFFER_COUNT 4
/* 摄像头真实输出宽高 */
static int width, height;
/* 摄像头设备文件描述符 */
static int fd_cam;
/* mmap 映射到用户空间的内核帧缓冲 */
static struct {
void *start; // 内存首地址
size_t length; // 内存长度
} buffers[BUFFER_COUNT];
extern int init_camera(const char *dev);
extern void cleanup_camera(void);
extern int yuyv_to_jpeg_memory(unsigned char *yuyv, int w, int h,
unsigned char **jpeg_data, unsigned long *jpeg_size);
extern int read_frame();
extern void *start_capturing(void *arg);
// camera.c
#include "camera.h"
#include
#include
#include "global.h"
#include
#include
#include
#include
static int n_buffer = 0;
/* 打开并初始化摄像头(YUYV 原始流) */
int init_camera(const char *dev)
{
// 打开摄像头设备节点 /dev/video0
fd_cam = open(dev, O_RDWR);
if (fd_cam < 0) { perror("open"); return -1; }
/* ---------- 告诉驱动:我要什么格式 ---------- */
struct v4l2_format fmt = {0}; // 清零结构体
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; // 表示这是采集设备
fmt.fmt.pix.width = IMG_WIDTH; // 希望的宽度
fmt.fmt.pix.height = IMG_HEIGHT; // 希望的高度
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; // YUYV 原始视频流
fmt.fmt.pix.field = V4L2_FIELD_NONE; // 不使用隔行扫描
// ioctl 把这个格式请求发给内核摄像头驱动
ioctl(fd_cam, VIDIOC_S_FMT, &fmt);
// 驱动可能会调整分辨率,这里读取真实的值
width = fmt.fmt.pix.width;
height = fmt.fmt.pix.height;
struct v4l2_requestbuffers req = {0};
req.count = BUFFER_COUNT; // 要 4 块帧缓冲
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; // 用于视频采集
req.memory = V4L2_MEMORY_MMAP; // 采用 mmap 共享内存方式
// 请求驱动在内核里分配 4 块 DMA 内存
if (ioctl(fd_cam, VIDIOC_REQBUFS, &req) < 0) {
perror("VIDIOC_REQBUFS");
close(fd_cam);
return -1;
}
// 检查实际分配的缓冲区数量
if (req.count < 1) {
fprintf(stderr, "Not enough buffers allocated: %d\n", req.count);
close(fd_cam);
return -1;
}
for (int i = 0; i < BUFFER_COUNT; i++) {
struct v4l2_buffer buf = {0};
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i; // 第 i 块缓冲
// 查询该缓冲区在内核中的偏移和大小
if (ioctl(fd_cam, VIDIOC_QUERYBUF, &buf) < 0) {
perror("VIDIOC_QUERYBUF");
close(fd_cam);
return -1;
}
// mmap 把这块 DMA 内存映射到进程地址空间
buffers[i].length = buf.length;
buffers[i].start = mmap(NULL, buf.length,
PROT_READ | PROT_WRITE,
MAP_SHARED,
fd_cam,
buf.m.offset);
if (buffers[i].start == MAP_FAILED) {
perror("mmap");
close(fd_cam);
return -1;
}
// 把这块缓冲区加入驱动的采集队列
if (ioctl(fd_cam, VIDIOC_QBUF, &buf) < 0) {
perror("VIDIOC_QBUF");
close(fd_cam);
return -1;
}
}
enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
// 通知驱动开始真正采集视频数据
if (ioctl(fd_cam, VIDIOC_STREAMON, &type) < 0) {
perror("VIDIOC_STREAMON");
close(fd_cam);
return -1;
}
printf("Camera ready: %dx%d YUYV\n", width, height);
n_buffer = req.count;
return fd_cam;
}
/* 清理摄像头资源 */
void cleanup_camera(void)
{
if (fd_cam < 0) {
return;
}
// 停止视频流
enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl(fd_cam, VIDIOC_STREAMOFF, &type);
// 取消 mmap 映射
for (int i = 0; i < n_buffer && i < BUFFER_COUNT; i++) {
if (buffers[i].start != NULL && buffers[i].start != MAP_FAILED) {
munmap(buffers[i].start, buffers[i].length);
buffers[i].start = NULL;
}
}
// 关闭摄像头设备
close(fd_cam);
fd_cam = -1;
printf("Camera resources cleaned up\n");
}
/* YUYV → JPEG 转换并存储到内存 */
int yuyv_to_jpeg_memory(unsigned char *yuyv, int w, int h,
unsigned char **jpeg_data, unsigned long *jpeg_size)
{
struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
JSAMPROW row[1];
unsigned char *rgb = NULL;
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_compress(&cinfo);
// 使用内存目标
jpeg_mem_dest(&cinfo, jpeg_data, jpeg_size);
cinfo.image_width = w;
cinfo.image_height = h;
cinfo.input_components = 3;
cinfo.in_color_space = JCS_RGB;
jpeg_set_defaults(&cinfo);
jpeg_set_quality(&cinfo, 80, TRUE); // 质量设为 80,平衡文件大小和画质
jpeg_start_compress(&cinfo, TRUE);
rgb = malloc(w * 3);
if (rgb == NULL) {
jpeg_destroy_compress(&cinfo);
return -1;
}
int stride = w * 2; // YUYV 每行字节数
while (cinfo.next_scanline < h) {
unsigned char *p = yuyv + cinfo.next_scanline * stride;
int j = 0;
// 每 4 字节表示 2 个像素
for (int i = 0; i < stride; i += 4) {
int y0 = p[i];
int u = p[i+1];
int y1 = p[i+2];
int v = p[i+3];
// YUV → RGB 颜色空间转换
int c = y0 - 16, d = u - 128, e = v - 128;
rgb[j++] = CLIP((298*c + 409*e + 128)>>8);
rgb[j++] = CLIP((298*c -100*d -208*e +128)>>8);
rgb[j++] = CLIP((298*c +516*d +128)>>8);
c = y1 - 16;
rgb[j++] = CLIP((298*c + 409*e + 128)>>8);
rgb[j++] = CLIP((298*c -100*d -208*e +128)>>8);
rgb[j++] = CLIP((298*c +516*d +128)>>8);
}
row[0] = rgb;
jpeg_write_scanlines(&cinfo, row, 1);
}
jpeg_finish_compress(&cinfo);
jpeg_destroy_compress(&cinfo);
free(rgb);
return 0;
}
/* 取一帧并转换为 JPEG 存储到全局缓冲区 */
int read_frame()
{
int ret;
struct v4l2_buffer buf = {0};
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
// 注意:VIDIOC_DQBUF 不需要设置 index,驱动会填充
ret = ioctl(fd_cam, VIDIOC_DQBUF, &buf); // 从驱动队列取出一帧
if (ret < 0) {
if (errno != EAGAIN) { // EAGAIN 表示暂时没有数据,不是错误
perror("VIDIOC_DQBUF");
}
return -1;
}
// 检查缓冲区索引是否有效
if (buf.index >= n_buffer || buf.index < 0) {
fprintf(stderr, "Invalid buffer index: %d (max: %d)\n", buf.index, n_buffer);
// 即使索引无效,也要归还缓冲区
ioctl(fd_cam, VIDIOC_QBUF, &buf);
return -1;
}
// 将 YUYV 转换为 JPEG
unsigned char *jpeg_data = NULL;
unsigned long jpeg_size = 0;
if (yuyv_to_jpeg_memory((unsigned char *)buffers[buf.index].start,
width, height, &jpeg_data, &jpeg_size) < 0) {
fprintf(stderr, "Failed to convert YUYV to JPEG\n");
ioctl(fd_cam, VIDIOC_QBUF, &buf);
return -1;
}
pthread_mutex_lock(&global.update_lock);
// 存储 JPEG 数据到全局缓冲区
if (jpeg_size > PICTURE_SIZE) {
global.start = realloc(global.start, jpeg_size);
if (global.start == NULL) {
perror("realloc");
pthread_mutex_unlock(&global.update_lock);
free(jpeg_data);
ioctl(fd_cam, VIDIOC_QBUF, &buf);
return -1;
}
}
global.length = jpeg_size;
memcpy(global.start, jpeg_data, jpeg_size);
pthread_mutex_unlock(&global.update_lock);
// 释放临时 JPEG 数据
free(jpeg_data);
//唤醒等待的线程
pthread_cond_broadcast(&global.update_cond);
// 还回队列继续采集
ret = ioctl(fd_cam, VIDIOC_QBUF, &buf);
if (ret < 0) {
perror("VIDIOC_QBUF");
return -1;
}
return 1;
}
void *start_capturing(void *arg)
{
int camera_fd = (int)(intptr_t)arg;
fd_set fds;
struct timeval tv = {
.tv_sec = 5,
.tv_usec = 0,
};
while(global.capture) {
FD_ZERO(&fds);
FD_SET(camera_fd, &fds);
int r = select(camera_fd+1, &fds, NULL, NULL, &tv);
if(-1 == r) {
if(EINTR == errno)
continue;
perror("Fail to select");
break;
}
if(0 == r) {
//fprintf(stderr, "select Timeout\n");
continue;
}
if(-1 == read_frame())
continue;
}
pthread_exit(NULL);
}
// server.h
#ifndef SERVER_H
#define SERVER_H
#include
#include
#include
#include // 用于 intptr_t 类型
#include
#include
#include
#include
#include
#include
#include
#include
#include "global.h"
#define STD_HEADER "Connection: close\r\n" \
"Server: MJPG-Streamer/0.2\r\n" \
"Cache-Control: no-store, no-cache, must-revalidate, pre-check=0, post-check=0, max-age=0\r\n" \
"Pragma: no-cache\r\n" \
"Expires: Mon, 3 Jan 2013 12:34:56 GMT\r\n"
#define BOUNDARY "cyg-boundary"
#define WEB_DIR "www"
typedef enum
{
A_SNAPSHOT,
A_STREAM,
A_FILE
} answer_t;
//HTTP 请求
typedef struct {
answer_t type;
char *parm;
} request_t;
// TCP 服务器初始化函数
// 参数: port - 要监听的端口号
// 返回: 成功返回 socket 文件描述符,失败返回 -1
extern int init_tcp(const unsigned short port);
extern void *handle_thread(void *arg);
extern int analyse_http_request(const char *buf, request_t *req);
extern void send_file(int sockfd, char *pathfile);
extern void send_snapshot(int sockfd);
extern void send_stream(int sockfd);
#endif // SERVER_H
//server.c
#include "server.h"
#include "global.h"
#include
#include
static const struct {
const char *dot_extension;
const char *mimetype;
} mimetypes[] = {
{ ".html", "text/html" },
{ ".htm", "text/html" },
{ ".css", "text/css" },
{ ".js", "text/javascript" },
{ ".txt", "text/plain" },
{ ".jpg", "image/jpeg" },
{ ".jpeg", "image/jpeg" },
{ ".png", "image/png"},
{ ".gif", "image/gif" },
{ ".ico", "image/x-icon" },
{ ".swf", "application/x-shockwave-flash" },
{ ".cab", "application/x-shockwave-flash" },
{ ".jar", "application/java-archive" }
};
int init_tcp(const unsigned short port) {
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("socket");
return -1;
}
// 设置 SO_REUSEADDR 选项,允许重用地址
int opt = 1;
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
perror("setsockopt");
close(sockfd);
return -1;
}
int ret = 0;
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = INADDR_ANY;
ret = bind(sockfd, (struct sockaddr *)&addr, sizeof(addr));
if (ret < 0) {
close(sockfd);
perror("bind");
return -1;
}
ret = listen(sockfd, 10);
if (ret < 0) {
perror("listen");
close(sockfd);
return -1;
}
return sockfd;
}
void *handle_thread(void *arg)
{
// 将指针转换回 int 值(使用 intptr_t 确保类型安全)
int sockfd = (int)(intptr_t)arg;
int ret = 0;
char buffer[1024];
request_t request = {
.type = A_SNAPSHOT,
.parm = NULL,
};
ret = recv(sockfd, buffer, sizeof(buffer), 0);
if (ret < 0) {
perror("recv");
goto end;
}
//printf("recv: %s\n", buffer);
analyse_http_request(buffer,&request);
switch (request.type){
case A_FILE:
send_file(sockfd, request.parm);
break;
case A_SNAPSHOT:
send_snapshot(sockfd);
break;
case A_STREAM:
send_stream(sockfd);
break;
}
// TODO: 在这里处理客户端连接
// 例如:读取数据、处理请求、发送响应等
end:
// 关闭连接
close(sockfd);
if(NULL != request.parm)
{
free(request.parm);
request.parm = NULL;
}
return NULL;
}
int analyse_http_request(const char *buf, request_t *req)
{
char *url = NULL;
char *url_end = NULL;
if (NULL != strstr(buf, "GET /?action=snapshot"))
{
req->type = A_SNAPSHOT;
return 0;
}
else if (NULL != strstr(buf, "GET /?action=stream"))
{
req->type = A_STREAM;
return 0;
}else{
url = strstr(buf,"GET /");
if (url == NULL) {
return -1;
}
url += 5; // 跳过 "GET /"
// 查找 URL 结束位置(空格、?、换行符等)
url_end = url;
while(*url_end && *url_end != ' ' && *url_end != '?' && *url_end != '\r' && *url_end != '\n'){
url_end++;
}
int len = url_end - url;
if(len == 0){
// 根路径,使用默认文件
len = strlen("index.html");
req->parm = (char *)malloc(len + 1);
if (req->parm == NULL) {
return -1;
}
strcpy(req->parm, "index.html");
} else {
len = MIN(len, 100);
req->parm = (char *)malloc(len + 1);
if (req->parm == NULL) {
return -1;
}
memset(req->parm, 0, len + 1);
memcpy(req->parm, url, len);
}
req->type = A_FILE;
}
return 0;
}
void send_snapshot(int sockfd)
{
int length;
char *frame = NULL;
char buf[BUFFER_SIZE];
printf("send_snapshot(%d)\n", sockfd);
pthread_mutex_lock(&global.update_lock);
pthread_cond_wait(&global.update_cond, &global.update_lock);
// 检查是否应该停止
if (!global.capture) {
pthread_mutex_unlock(&global.update_lock);
return;
}
//获得视频数据
length = global.length;
frame = (char *)malloc(global.length);
if (frame == NULL) {
perror("malloc");
pthread_mutex_unlock(&global.update_lock);
return;
}
memcpy(frame, global.start, global.length);
pthread_mutex_unlock(&global.update_lock);
//添加http头
memset(buf, 0, sizeof(buf));
sprintf(buf, "HTTP/1.0 200 OK\r\n" \
"Content-type: image/jpeg\r\n" \
STD_HEADER \
"\r\n");
//发送http头
if(0 > send(sockfd, buf, strlen(buf), 0)) {
if (errno == EPIPE || errno == ECONNRESET) {
printf("Client disconnected (fd=%d)\n", sockfd);
} else {
printf("send_snapshot() send http head failure\n");
}
free(frame);
return;
}
//发送视频数据
if(0 > send(sockfd, frame, length, 0)){
if (errno == EPIPE || errno == ECONNRESET) {
printf("Client disconnected (fd=%d)\n", sockfd);
} else {
printf("send_snapshot() send frame failure\n");
}
}
free(frame);
return;
}
void send_stream(int sockfd)
{
int length;
char buf[BUFFER_SIZE];
printf("send_stream(%d)\n", sockfd);
sprintf(buf, "HTTP/1.1 200 OK\r\n" \
STD_HEADER \
"Content-Type: multipart/x-mixed-replace;boundary=" BOUNDARY "\r\n" \
"\r\n" \
"--" BOUNDARY "\r\n");
if(send(sockfd, buf, strlen(buf), 0) < 0) {
perror("send_stream() fail to send http head");
return;
}
char *frame = NULL;
while (global.capture)
{
pthread_mutex_lock(&global.update_lock);
pthread_cond_wait(&global.update_cond, &global.update_lock);
// 检查是否应该停止(capture 被设置为 false)
if (!global.capture) {
pthread_mutex_unlock(&global.update_lock);
break;
}
length = global.length;
frame = (char *)malloc(global.length);
if (frame == NULL) {
perror("malloc");
pthread_mutex_unlock(&global.update_lock);
break;
}
memcpy(frame, global.start, global.length);
pthread_mutex_unlock(&global.update_lock);
/* print the individual mimetype and the length
* sending the content-length fixes random stream disruption observed
* with firefox
*/
sprintf(buf, "Content-Type: image/jpeg\r\n" \
"Content-Length: %d\r\n" \
"\r\n", length);
if(0 > send(sockfd, buf, strlen(buf), 0)) {
// 检测客户端断开连接
if (errno == EPIPE || errno == ECONNRESET) {
printf("Client disconnected (fd=%d)\n", sockfd);
} else {
perror("send_stream() Fail to send http header");
}
free(frame);
break;
}
if(0 > send(sockfd, frame, length, 0)) {
// 检测客户端断开连接
if (errno == EPIPE || errno == ECONNRESET) {
printf("Client disconnected (fd=%d)\n", sockfd);
} else {
perror("send_stream() Fail to send camera frame");
}
free(frame);
break;
}
sprintf(buf, "\r\n--" BOUNDARY "\r\n");
if (0 > send(sockfd, buf, strlen(buf), 0)) {
// 检测客户端断开连接
if (errno == EPIPE || errno == ECONNRESET) {
printf("Client disconnected (fd=%d)\n", sockfd);
}
free(frame);
break;
}
free(frame);
frame = NULL;
usleep(100);
}
if (frame != NULL) {
free(frame);
}
printf("send_stream(%d) ended\n", sockfd);
return;
}
void send_file(int sockfd, char *pathfile)
{
char *extension;
const char *mimetype = NULL;
char buffer[1024];
char filepath[1024];
if(NULL == pathfile || strlen(pathfile) == 0){
pathfile = "index.html";
}
extension = strrchr(pathfile,'.');
if(NULL == extension)
return;
for(int i = 0; i < sizeof(mimetypes)/sizeof(mimetypes[0]); i++){
if(strcmp(extension,mimetypes[i].dot_extension) == 0){
mimetype = mimetypes[i].mimetype;
break;
}
}
if(NULL == mimetype){
return;
}
sprintf(filepath,"%s/%s",WEB_DIR,pathfile);
printf("send_file() pathfile: %s\n",filepath);
int fp = open(filepath,O_RDONLY);
if(0 > fp){
perror("open() Fail to open file");
// 发送 404 错误响应
sprintf(buffer,"HTTP/1.1 404 Not Found\r\nContent-Type: text/html\r\n%s\r\n404 Not Found\r\n",STD_HEADER);
send(sockfd, buffer, strlen(buffer), 0);
return;
}
// 构建并发送 HTTP 响应头
sprintf(buffer,"HTTP/1.1 200 OK\r\nContent-Type: %s\r\n%s\r\n",mimetype,STD_HEADER);
int header_len = strlen(buffer);
if(send(sockfd, buffer, header_len, 0) < 0){
perror("send_file() Fail to send header");
close(fp);
return;
}
// 读取并发送文件内容
int n;
while((n = read(fp, buffer, sizeof(buffer))) > 0){
if(send(sockfd, buffer, n, 0) < 0){
perror("send_file() Fail to send file content");
break;
}
}
close(fp);
}
//main.c
#include
#include
#include
#include
#include "global.h"
#include "server.h"
#include "camera.h"
// 全局变量用于信号处理
static int server_sockfd = -1;
static pthread_t camera_tid;
static volatile sig_atomic_t running = 1;
/* 信号处理函数:优雅关闭服务器 */
void signal_handler(int sig)
{
if (sig == SIGINT || sig == SIGTERM) {
printf("\nReceived signal %d, shutting down gracefully...\n", sig);
running = 0;
// 停止采集
global.capture = false;
pthread_cond_broadcast(&global.update_cond);
// 关闭服务器 socket,使 accept 返回
if (server_sockfd >= 0) {
close(server_sockfd);
server_sockfd = -1;
}
}
}
/* 清理所有资源 */
void cleanup_resources(void)
{
printf("Cleaning up resources...\n");
// 停止采集并清理全局资源
cleanup_global();
// 等待摄像头线程结束
pthread_join(camera_tid, NULL);
// 清理摄像头资源
cleanup_camera();
printf("All resources cleaned up\n");
}
int main(int argc, char *argv[]) {
// 注册信号处理函数
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
// 先初始化全局变量,再启动摄像头线程
init_global();
int camera_fd = init_camera("/dev/video0");
if (camera_fd < 0) {
printf("打开摄像头失败\n");
cleanup_global();
return -1;
}
if (pthread_create(&camera_tid, NULL, start_capturing, (void *)(intptr_t)camera_fd) != 0) {
perror("pthread_create");
cleanup_camera();
cleanup_global();
return -1;
}
// 注意:不 detach,以便后续 join
server_sockfd = init_tcp(8080);
if (server_sockfd < 0) {
perror("init_tcp");
cleanup_resources();
return -1;
}
pthread_t c_tid;
struct sockaddr_in c_addr;
socklen_t c_addr_len = sizeof(c_addr);
printf("Server started on port 8080\n");
printf("Press Ctrl+C to stop the server\n");
while (running) {
int new_sockfd = accept(server_sockfd, (struct sockaddr *)&c_addr, &c_addr_len);
if (new_sockfd < 0) {
if (!running) {
// 服务器正在关闭
break;
}
perror("accept");
continue;
}
printf("New connection accepted\n");
// 将 int 值直接转换为指针传递,避免 malloc/free
// 使用 intptr_t 确保类型转换安全
if (pthread_create(&c_tid, NULL, handle_thread, (void *)(intptr_t)new_sockfd) != 0) {
perror("pthread_create");
close(new_sockfd);
continue;
}
// 分离线程,让线程结束后自动清理资源
pthread_detach(c_tid);
}
// 清理资源
cleanup_resources();
return 0;
}