网页服务器进阶
http请求
// http请求处理
void http_request(const char* request, int cfd)
{
// 拆分http请求行 ---- GET / http/1.1
char method[12], path[1024], protocol[12];
sscanf(request, "%[^ ] %[^ ] %[^ ]", method, path, protocol);
// path = “/” ---> path+1 = 0m == file
printf("method = %s, path = %s, protocol = %s\n", method, path, protocol);
// 转码 将不能识别的中文乱码 -> 中文
// 解码 %23 %34 %5f
decode_str(path, path);
char* file = path+1; // 去掉path中的/ 获取访问文件名
// 如果没有指定访问的资源, 默认显示资源目录中的内容
if(strcmp(path, "/") == 0) {
// file的值, 资源目录的当前位置
file = "./";
}
// 获取文件属性
struct stat st;
int ret = stat(file, &st);
if(ret == -1) {
send_error(cfd, 404, "Not Found", "NO such file or direntry");
return;
}
// 判断是目录还是文件
if(S_ISDIR(st.st_mode)) { // 目录
// 发送头信息
send_respond_head(cfd, 200, "OK", get_file_type(".html"), -1);
// 发送目录信息
send_dir(cfd, file);
} else if(S_ISREG(st.st_mode)) { // 文件
// 发送消息报头
send_respond_head(cfd, 200, "OK", get_file_type(file), st.st_size);
// 发送文件内容
send_file(cfd, file);
}
}
解码
void decode_str(char *to, char *from)
{
for ( ; *from != '\0'; ++to, ++from ) {
if (from[0] == '%' && isxdigit(from[1]) && isxdigit(from[2])) {
*to = hexit(from[1])*16 + hexit(from[2]);
from += 2;
} else {
*to = *from;
}
}
*to = '\0';
}
发送响应头
// 发送响应头
void send_respond_head(int cfd, int no, const char* desp, const char* type, long len)
{
char buf[1024] = {0};
// 状态行
sprintf(buf, "http/1.1 %d %s\r\n", no, desp);
send(cfd, buf, strlen(buf), 0);
// 消息报头
sprintf(buf, "Content-Type:%s\r\n", type);
sprintf(buf+strlen(buf), "Content-Length:%ld\r\n", len);
send(cfd, buf, strlen(buf), 0);
// 空行
send(cfd, "\r\n", 2, 0);
}
发送目录信息
// 发送目录信息
void send_dir(int cfd, const char* dirname)
{
int i, ret;
// 拼一个html页面<table></table>
char buf[4094] = {0};
sprintf(buf, "<html><head><title>目录名: %s</title></head>", dirname);
sprintf(buf+strlen(buf), "<body><h1>当前目录: %s</h1><table>", dirname);
char enstr[1024] = {0};
char path[1024] = {0};
// 目录项二级指针
struct dirent** ptr;
int num = scandir(dirname, &ptr, NULL, alphasort);
// 遍历
for(i = 0; i < num; ++i) {
char* name = ptr[i]->d_name;
// 拼接文件的完整路径
sprintf(path, "%s/%s", dirname, name);
printf("path = %s ===================\n", path);
struct stat st;
stat(path, &st);
encode_str(enstr, sizeof(enstr), name);
// 如果是文件
if(S_ISREG(st.st_mode)) {
sprintf(buf+strlen(buf),
"<tr><td><a href=\"%s\">%s</a></td><td>%ld</td></tr>",
enstr, name, (long)st.st_size);
} else if(S_ISDIR(st.st_mode)) { // 如果是目录
sprintf(buf+strlen(buf),
"<tr><td><a href=\"%s/\">%s/</a></td><td>%ld</td></tr>",
enstr, name, (long)st.st_size);
}
ret = send(cfd, buf, strlen(buf), 0);
if (ret == -1) {
if (errno == EAGAIN) {
perror("send error:");
continue;
} else if (errno == EINTR) {
perror("send error:");
continue;
} else {
perror("send error:");
exit(1);
}
}
memset(buf, 0, sizeof(buf));
// 字符串拼接
}
sprintf(buf+strlen(buf), "</table></body></html>");
send(cfd, buf, strlen(buf), 0);
printf("dir message send OK!!!!\n");
#if 0
// 打开目录
DIR* dir = opendir(dirname);
if(dir == NULL)
{
perror("opendir error");
exit(1);
}
// 读目录
struct dirent* ptr = NULL;
while( (ptr = readdir(dir)) != NULL )
{
char* name = ptr->d_name;
}
closedir(dir);
#endif
}
加解码
void encode_str(char* to, int tosize, const char* from)
{
int tolen;
for (tolen = 0; *from != '\0' && tolen + 4 < tosize; ++from) {
if (isalnum(*from) || strchr("/_.-~", *from) != (char*)0) {
*to = *from;
++to;
++tolen;
} else {
sprintf(to, "%%%02x", (int) *from & 0xff);
to += 3;
tolen += 3;
}
}
*to = '\0';
}
// 通过文件名获取文件的类型
const char *get_file_type(const char *name)
{
char* dot;
// 自右向左查找‘.’字符, 如不存在返回NULL
dot = strrchr(name, '.');
if (dot == NULL)
return "text/plain; charset=utf-8";
if (strcmp(dot, ".html") == 0 || strcmp(dot, ".htm") == 0)
return "text/html; charset=utf-8";
if (strcmp(dot, ".jpg") == 0 || strcmp(dot, ".jpeg") == 0)
return "image/jpeg";
if (strcmp(dot, ".gif") == 0)
return "image/gif";
if (strcmp(dot, ".png") == 0)
return "image/png";
if (strcmp(dot, ".css") == 0)
return "text/css";
if (strcmp(dot, ".au") == 0)
return "audio/basic";
if (strcmp( dot, ".wav" ) == 0)
return "audio/wav";
if (strcmp(dot, ".avi") == 0)
return "video/x-msvideo";
if (strcmp(dot, ".mov") == 0 || strcmp(dot, ".qt") == 0)
return "video/quicktime";
if (strcmp(dot, ".mpeg") == 0 || strcmp(dot, ".mpe") == 0)
return "video/mpeg";
if (strcmp(dot, ".vrml") == 0 || strcmp(dot, ".wrl") == 0)
return "model/vrml";
if (strcmp(dot, ".midi") == 0 || strcmp(dot, ".mid") == 0)
return "audio/midi";
if (strcmp(dot, ".mp3") == 0)
return "audio/mpeg";
if (strcmp(dot, ".ogg") == 0)
return "application/ogg";
if (strcmp(dot, ".pac") == 0)
return "application/x-ns-proxy-autoconfig";
return "text/plain; charset=utf-8";
}