大禹123 的学生作业:
debug.h
#ifndef _DEBUG_H_
#define _DEBUG_H_
/*
__FILE__: 输出文件名
__FUNCTION__: 输出函数名
__LINE__: 输出行号
*/
// 使用 snprintf 代替 sprintf,防止缓冲区溢出
#define DEBUG_INFO(fmt, ...) do { \
char buf[1024]; \
snprintf(buf, sizeof(buf), fmt, ##__VA_ARGS__); \
fprintf(stderr, "[%s, %s, %d] : %s\n", __FILE__, __FUNCTION__, __LINE__, buf); \
} while (0)
#endif // _DEBUG_H_
server.h
#ifndef __SERVER_H__
#define __SERVER_H__
#include /* See NOTES */
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "global.h"
#include
#include
#include
#include
#include
#define BACKBLOG 10
typedef enum
{
A_SNAPSHOT,
A_STREAM,
A_FILE
} answer_t;
typedef struct
{
answer_t type;
char *parm;
/* data */
}request_t;
#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"
extern int init_tcp(const char *ip, const unsigned short port, int backlog);
extern void *client_thread(void *arg);
extern int analyse_http_request(const char *buf, request_t *req);
extern void send_file(int sockfd, char *filename);
#endif
global.h
#ifndef __GLOBAL_H_
#define __GLOBAL_H_
#define MAX(a, b) ((a) > (b)) ? (a) : (b)
#define MIN(a, b) ((a) < (b)) ? (a) : (b)
#endif
server.c
#include "server.h"
#include "debug.h"
#define WEB_DIR "./www/"
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 char *ip, const unsigned short port, int backlog)
{
int sfd, on = 1;
// 創建套接字
sfd = socket(AF_INET, SOCK_STREAM, 0);
if (sfd == -1)
{
DEBUG_INFO("[ERROR] socket():%s\n", strerror(errno));
return -1;
}
if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
{
DEBUG_INFO("[ERROR] setsockopt():%s\n", strerror(errno));
}
struct sockaddr_in saddr;
memset(&saddr, 0, sizeof(struct sockaddr_in));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(port);
saddr.sin_addr.s_addr = inet_addr(ip);
if (bind(sfd, (const struct sockaddr *)&saddr, sizeof(struct sockaddr_in)) < 0)
{
DEBUG_INFO("[ERROR] bind():%s\n", strerror(errno));
return -1;
}
listen(sfd, backlog);
printf("listen fd %d\n", sfd);
printf("server ip is %s port %d\n", inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
return sfd;
}
void *client_thread(void *arg)
{
int rbytes;
int cfd = (int)arg;
char buf[1024] = {0}; // 线程内栈空间大小有限,不能占太大内存
rbytes = recv(cfd, buf, sizeof(buf) - 1, 0);
if (rbytes < 0)
{
DEBUG_INFO("[ERROR]:recv() failure%s\n", strerror(errno));
pthread_exit(NULL);
}
request_t req;
if (analyse_http_request(buf, &req) < 0)
{
DEBUG_INFO("[ERROR]:analyse_http_request() failure\n");
pthread_exit(NULL);
}
switch (req.type)
{
case A_FILE:
send_file(cfd, req.parm);
break;
}
close(cfd);
pthread_exit(NULL);
}
int analyse_http_request(const char *buf, request_t *req)
{
// 如果找到字符串,则返回指向字符串的第一个字符的地址
char *url = strstr(buf, "GET /");
if (NULL == url)
{
DEBUG_INFO("[ERROR]:http request fail\n");
return -1;
}
url += strlen("GET /");
// 有效的url字母序列
char arr[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ._-1234567890";
int len = MIN(MAX(strspn(url, arr), 0), 100);
req->parm = (char *)calloc(1, len + 1);
memcpy(req->parm, url, len);
printf("req->parm:%s\n", req->parm);
req->type = A_FILE;
return 0;
}
void send_file(int sockfd, char *filename)
{
int bytes, fd, i;
char buf[1024] = {0};
char *externsion = NULL, *mimetype = NULL;
if (filename == NULL || strlen(filename) == 0)
{
filename = "index.html";
}
// 定位到扩展名
if (NULL == (externsion = strstr(filename, ".")))
{
return;
}
for (int i = 0; i < sizeof(mimetypes) / sizeof(mimetypes[0]); i++)
{
if (0 == strcmp(mimetypes[i].dot_extension, externsion))
{
externsion = (char *)mimetypes[i].dot_extension;
mimetype = (char *)mimetypes[i].mimetype;
break;
}
}
if (NULL == externsion)
{
return;
}
// 将格式化的数据写入一个字符串缓冲区
sprintf(buf, "%s%s", WEB_DIR, filename);
printf("pathname:%s", buf);
if ((fd = open(buf, O_RDONLY)) < 0)
{
DEBUG_INFO("[ERROR]:open() fail%s\n", strerror(errno));
return;
}
memset(buf, 0, sizeof(buf));
sprintf(buf, "HTTP/1.0 200 OK\r\n"
"Content-type: %s\r\n" STD_HEADER "\r\n",
mimetype); // 注意这里会多出个空行.
bytes = strlen(buf);
do
{
if (0 > send(sockfd, buf, bytes, 0))
{
DEBUG_INFO("[ERROR]:send() fail%s\n", strerror(errno));
}
memset(buf, 0, sizeof(buf));
} while ((bytes = read(fd, buf, sizeof(buf))) > 0);
close(fd);
return;
}
main.c
#include "debug.h"
#include "server.h"
#include
#include
#include
#include
#include
int main(int argc, char const *argv[])
{
#if 0
int cam_fd;
cam_fd = init_camera("/dev/video0");
if (-1 == cam_fd)
{
DEBUG_INFO("[ERROR]:init_camera() failure");
return -1;
}
cam_fd = init_mmap(cam_fd);
if (-1 == cam_fd)
{
DEBUG_INFO("[ERROR]:init_camera() failure");
return -1;
}
cam_fd = start_camera(cam_fd);
if (-1 == cam_fd)
{
DEBUG_INFO("[ERROR]:init_camera() failure");
return -1;
}
cam_fd = wait_camera(cam_fd);
if (-1 == cam_fd)
{
DEBUG_INFO("[ERROR]:wait_camera() failure");
return -1;
}
#endif
struct sockaddr_in c_addr;
int cfd; // 用于和客户端通信的套接字
int ret;
socklen_t addr_len = sizeof(struct sockaddr_in);
pthread_t tid;
int sfd = init_tcp("127.0.0.1", 8080, BACKBLOG);
memset(&c_addr, 0, addr_len);
while (1)
{
printf("addr_len = %d\n", addr_len);
cfd = accept(sfd, (struct sockaddr *)&c_addr, &addr_len);
printf("cfd = %d\n", cfd);
if (-1 == cfd)
{
DEBUG_INFO("[ERROR]:accept() failure%s\n",strerror(errno));
continue;
}
printf("cfd = %d\n", cfd);
ret = pthread_create(&tid, NULL, client_thread, (void *)cfd); //此处不能传地址,因为循环的速度有可能大于线程创建的速度
if (ret != 0)
{
DEBUG_INFO("[ERROR]:pthread_create() failure%s\n",strerror(errno));
continue;
}
pthread_detach(tid);
printf("loop again\n");
}
return 0;
}
result【图片】