一, http协议
http协议 - 应用层
- 请求消息(Request) - 浏览器给服务器发
○ 四部分: 请求行, 请求头, 空行, 请求数据
§ 请求行: 说明请求类型, 要访问的资源, 以及使用的http版本
§ 请求头: 说明服务器要使用的附加信息
§ 空行: 空行是必须要有的, 即使没有请求数据
§ 请求数据: 也叫主体, 可以添加任意的其他数据
○ 例:
GET /3.txt HTTP/1.1
/: 资源目录的根目录
三部分内容由空格间隔
Host: localhost:2222
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:24.0) Gecko/201001 01 Firefox/24.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: keep-alive
If-Modified-Since: Fri, 18 Jul 2014 08:36:36 GMT
请求头:由键值对构成的
请求数据
□ get: 肯定没有
□ post : 有数据
换行:\r\n
- 响应消息(Response) - 服务器给浏览器发
○ 四部分: 状态行, 消息报头, 空行, 响应正文
§ 状态行: 包括http协议版本号, 状态码, 状态信息
§ 消息报头: 说明客户端要使用的一些附加信息
§ 空行: 空行是必须要有的
§ 响应正文: 服务器返回给客户端的文本信息
○ 例:
HTTP/1.1 200 Ok
Server: micro_httpd
Date: Fri, 18 Jul 2014 14:34:26 GMT
Content-Type: text/plain; charset=iso-8859-1 (必选项)
告诉浏览器发送的数据是什么类型
Content-Length: 32
发送的数据的长度
Content-Language: zh-CN
Last-Modified: Fri, 18 Jul 2014 08:36:36 GMT
Connection: close
#include <stdio.h>
int main(void)
{
printf("hello world!\n");
return 0;
}
- HTTP1.1的五种请求方法
○ GET
§ 请求指定的页面信息,并返回实体主体。
○ POST
§ 向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST请求可能会导致新的资源的建立和/或已有资源的修改。
○ HEAD
§ 类似于get请求,只不过返回的响应中没有具体的内容,用于获取报头
○ PUT
§ 从客户端向服务器传送的数据取代指定的文档的内容。
○ DELETE
§ 请求服务器删除指定的页面。
○ CONNECT
§ HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。
○ OPTIONS
§ 允许客户端查看服务器的性能。
○ TRACE
§ 回显服务器收到的请求,主要用于测试或诊断。
- HTTP常用状态码
状态代码有三位数字组成,第一个数字定义了响应的类别,共分五种类别:
○ 1xx:指示信息--表示请求已接收,继续处理
○ 2xx:成功--表示请求已被成功接收、理解、接受
○ 3xx:重定向--要完成请求必须进行更进一步的操作
○ 4xx:客户端错误--请求有语法错误或请求无法实现
○ 5xx:服务器端错误--服务器未能实现合法的请求
○ 常见状态码:
§ 200 OK 客户端请求成功
§ 400 Bad Request 客户端请求有语法错误,不能被服务器所理解
§ 401 Unauthorized 请求未经授权,这个状态代码必须和WWW-Authenticate报头域一起使用
§ 403 Forbidden 服务器收到请求,但是拒绝提供服务
§ 404 Not Found 请求资源不存在,eg:输入了错误的URL
§ 500 Internal Server Error 服务器发生不可预期的错误
§ 503 Server Unavailable 服务器当前不能处理客户端的请求,一段时间后可能恢复正常
二, http四种格式
1. http请求协议, 请求 的时候使用的Post方法
2. 概述
○ HTTP 请求分为四个部分:请求行、请求头、空行、主体
○ 协议规定 POST 提交的数据必须放在消息主体(entity-body)中,但协议并没有规定数据必须使用什么编码方式。
○ 开发者完全可以自己决定消息主体的格式
○ 数据发送出去,还要服务端解析成功才有意义, 服务端通常是根据请求头(headers)中的 Content-Type 字段来获知请求中的消息主体是用何种方式编码,再对主体进行解析。
3. 四种方式
○ application/x-www-form-urlencoded
POST http://www.example.com HTTP/1.1
Content-Type: application/x-www-form-urlencoded;charset=utf-8
title=test&sub%5B%5D=1&sub%5B%5D=2&sub%5B%5D=3
○ application/json
POST http://www.example.com HTTP/1.1
Content-Type: application/json;charset=utf-8
{"title":"test","sub":[1,2,3]}
○ text/xml
POST http://www.example.com HTTP/1.1
Content-Type: text/xml
<!
<methodcall>
<methodname>examples.getStateName</methodname>
<params>
<value><i4>41</i4></value>
</params>
</methodcall>
○ multipart/form-data
Content-Disposition: form-data; name="file"; filename="qw.png"
Content-Type: image/png\r\n
\r\n
PNG
...................................................................................
...................................................................................
Content-Disposition: form-data; name="tailor"
false
○ 解析数据块
§ 把绿色的数据取出来, 保存到文件中
§ 先读第一行 : 分界线 - 存储到内存
□ 遇到第二个同样的分界线, 说明文件内容结束了.
□ char *p = strstr(big, sub)
○ 注意事项:
§ boundary是一个字符串,用来切分数据
§ 在HTML协议中换行使用的是:"\r\n"
§ BODY里面的bounday比HEADER里面的前面都多了“
三, 解析http格式
#include "fcgi_config.h"
#include "fcgi_stdio.h"
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include "fdfs_api.h"
#include "deal_mysql.h"
#include "make_log.h"
#include "upload.h"
char* memstr(char* full_data, int full_data_len, char* substr)
{
if (full_data == NULL || full_data_len <= 0 || substr == NULL)
{
return NULL;
}
if (*substr == '\0')
{
return NULL;
}
int sublen = strlen(substr);
char* cur = full_data;
int last_possible = full_data_len - sublen + 1;
for (int i = 0; i < last_possible; i++)
{
if (*cur == *substr)
{
if (memcmp(cur, substr, sublen) == 0)
{
return cur;
}
}
cur++;
}
return NULL;
}
int get_file_content(char* begin, char* end, int len, char* filename)
{
char *p = NULL;
char boundary[256] = {0};
p = strstr(begin, "\r\n");
strncpy(boundary, begin, p-begin);
p += 2;
len -= (p-begin);
printf("boundary: %s", boundary);
begin = p;
p = strstr(begin, "\r\n");
p += 2;
len -= (p-begin);
char* pt = strstr(begin, "filename=");
pt += strlen("filename=");
pt ++;
char* q = strchr(pt, '"');
strncpy(filename, pt, q-pt);
printf("<br>filename: %s<br>", filename);
begin = p;
p = strstr(begin, "\r\n");
p += 4;
len -= (p-begin);
begin = p;
p = memstr(begin, len, boundary);
if(p == NULL)
{
p = end - 2;
}
else
{
p -= 2;
}
int fd = open(filename, O_CREAT|O_WRONLY, 0664);
write(fd, begin, p-begin);
close(fd);
return 0;
}
int store_data(char* filename, char* fileid)
{
MYSQL* conn = NULL;
conn = msql_conn("root", "root", "test");
if(conn == NULL)
{
LOG("upload_file", "mysql", "数据库连接失败!");
return -1;
}
mysql_query(conn, "set names utf8");
char buf[1024];
sprintf(buf, "insert into file (name, fileid) values ('%s', '%s')",
filename, fileid);
printf("<br> sql: %s<br>\n", buf);
if ( mysql_query (conn, buf) != 0 )
{
LOG("upload_file", "mysql", "数据插入失败");
return -1;
}
mysql_close(conn);
return 0;
}
int main ()
{
while (FCGI_Accept() >= 0)
{
int len = 0;
char *contentLength = getenv("CONTENT_LENGTH");
printf("Content-type: text/html\r\n"
"\r\n");
if (contentLength != NULL)
{
len = strtol(contentLength, NULL, 10);
}
if (len <= 0)
{
printf("No data from standard input.<p>\n");
}
else
{
int ch;
char filename[128] = {0};
char* file_data = (char*)malloc(len);
char *begin, *end, *p;
end = NULL;
begin = p = file_data;
for (int i = 0; i < len; i++)
{
if ((ch = getchar()) < 0)
{
printf("Error: Not enough bytes received on standard input<p>\n");
break;
}
*p = ch;
p++;
}
end = p;
get_file_content(begin, end, len, filename);
char fileid[1024] = {0};
fdfs_upload_file(filename, fileid);
printf("<br>fileid: %s\n<br>", fileid);
store_data(filename, fileid);
free(file_data);
unlink(filename);
}
}
return 0;
}