慕尼黑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;
#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_
// 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);
#endif // SERVER_H
//server.c
#include "server.h"
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;
}
analyse_http_request(buffer,&request);
switch(request.type)
{
case A_SNAPSHOT:
break;
case A_STREAM:
break;
case A_FILE:
send_file(sockfd,request.parm);
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;
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_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 "server.h"
int main(int argc, char *argv[]) {
int sockfd = init_tcp(8080);
if (sockfd < 0) {
perror("init_tcp");
return -1;
}
pthread_t c_tid;
struct sockaddr_in c_addr;
socklen_t c_addr_len = sizeof(c_addr);
printf("Server started 192.168.0.191:8080\n");
printf("Hello, World!\n");
while (1) {
int new_sockfd = accept(sockfd, (struct sockaddr *)&c_addr, &c_addr_len);
if (new_sockfd < 0) {
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);
}
close(sockfd);
return 0;
}