1、CGI
1、什么是CGI?
通用网关接口(Common Gateway Interface,CGI)描述了客户端与服务器程序之间传输数据的一种标准,可以让一个客户端,从网页浏览器向执行在网络服务器上的程序请求数据。
CGI独立于任何一种语言,CGI可以使用任何脚本语言或者是完全独立编程语言实现,只要这个语言可以在这个系统上运行。Unix shell script、Python、Ruby、PHP、perl、Tel、C/C++和Visual Basic都可以用来编写CGI程序。
最初,CGI 是在 1993 年由美国国家超级电脑应用中心(NCSA)为 NCSA HTTPd Web 服务器开发的。这个 Web 服务器使用了 UNIX shell 环境变量来保存从 Web 服务器传递出去的参数,然后生成一个运行 CGI 的独立的进程。
2、CGI处理流程
1、简单处理流程
流程分析
http://localhost/login?user=zhang3&passwd=123456&age=12&sex=man
- 用户通过浏览器访问服务器, 发送了一个请求, 请求的url如上
- 服务器接收数据, 对接收的数据进行解析
- nginx对于一些登录数据不知道如何处理, nginx将数据发送给了cgi程序
- 服务器端会创建一个cgi进程
- CGI进程执行
- 加载配置, 如果有需求加载配置文件获取数据
- 连接其他服务器: 比如数据库
- 逻辑处理:
- 得到结果, 将结果发送给服务器
- 退出
- 服务器将cgi处理结果发送给客户端
在服务器端CGI进程会被频繁的创建销毁
- 服务器开销大, 效率低
上图流程图虚线部分就表示的是服务器对于每一个请求都创建一个CGI进程,造成服务器开销大,效率低
2、深入了解CGI处理流程
流程
- web服务器收到客户端(浏览器)的请求Http Request,启动CGI程序,并通过环境变量、标准输入传递数据
- CGI进程启动解析器、加载配置(如业务相关配置)、连接其它服务器(如数据库服务器)、逻辑处理等
- CGI进程将处理结果通过标准输出、标准错误,传递给web服务器
- web服务器收到CGI返回的结果,构建Http Response返回给客户端,并杀死CGI进程
web服务器与CGI通过环境变量、标准输入、标准输出、标准错误互相传递数据。在遇到用户连接请求:
- 先要创建CGI子进程,然后CGI子进程处理请求,处理完事退出这个子进程:fork-and-execute
- CGI方式是客户端有多少个请求,就开辟多少个子进程,每个子进程都需要启动自己的解释器、加载配置,连接其他服务器等初始化工作,这是CGI进程性能低下的主要原因。当用户请求非常多的时候,会占用大量的内存、cpu等资源,造成性能低下。
CGI使外部程序与Web服务器之间交互成为可能。CGI程序运行在独立的进程中,并对每个Web请求建立一个进程,这种方法非常容易实现,但效率很差,难以扩展。面对大量请求,进程的大量建立和消亡使操作系统性能大大下降。此外,由于地址空间无法共享,也限
制了资源重用。
3、环境变量
GET请求,它将数据打包放置在环境变量 QUERY_STRING 中,CGI从环境变量 QUERY_STRING 中获取数据。
常见的环境变量如下表所示:
环境变量 | 含义 |
---|---|
AUTH_TYPE | 存取认证类型 |
CONTENT_LENGTH | 由标准输入传递给CGI程序的数据长度,以bytes或字元数来计算 |
CONTENT_TYPE | 请求的MIME类型 |
GATEWAY_INTERFACE | 服务器的CGI版本编号 |
HTTP_ACCEPT | 浏览器能直接接收的Content-types, 可以有HTTP Accept header定义 |
HTTP_USER_AGENT | 递交表单的浏览器的名称、版本和其他平台性的附加信息 |
HTTP_REFERER | 递交表单的文本的URL,不是所有的浏览器都发出这个信息,不要依赖它 |
PATH_INFO | 传递给CGI程序的路径信息 |
QUERY_STRING | 传递给CGI程序的请求参数,也就是用"?"隔开,添加在URL后面的字串 |
REMOTE_ADDR | client端的host名称 |
REMOTE_HOST | client端的IP位址 |
REMOTE_USER | client端送出来的使用者名称 |
REMOTE_METHOD | client端发出请求的方法(如get、post) |
SCRIPT_NAME | CGI程序所在的虚拟路径,如/cgi-bin/echo |
SERVER_NAME | server的host名称或IP地址 |
SERVER_PORT | 收到request的server端口 |
SERVER_PROTOCOL | 所使用的通讯协定和版本编号 |
SERVER_SOFTWARE | server程序的名称和版本 |
4、标准输入
环境变量的大小是有一定的限制的,当需要传送的数据量大时,储存环境变量的空间可能会不足,造成数据接收不完全,甚至无法执行CGI程序。
因此后来又发展出另外一种方法:POST,也就是利用I/O重新导向的技巧,让CGI程序可以由stdin和stdout直接跟浏览器沟通。
当我们指定用这种方法传递请求的数据时,web服务器收到数据后会先放在一块输入缓冲区中,并且将数据的大小记录在CONTENT_LENGTH这个环境变量,然后调用CGI程序并将CGI程序的stdin指向这块缓冲区,于是我们就可以很顺利的通过stdin和环境变数CONTENT_LENGTH得到所有的信息,再没有信息大小的限制了。
2、 FastCGI
1、什么是FastCGI?
快速通用网关接口(Fast Common Gateway Interface/FastCGI)是通用网关接口(CGI)的改进,描述了客户端和服务器程序之间传输数据的一种标准。
FastCGI致力于减少Web服务器与CGI程序之间互动的开销,从而使服务器可以同时处理更多的Web请求。与为每个请求创建一个新的进程不同,FastCGI使用持续的进程来处理一连串的请求。这些进程由FastCGI进程管理器管理,而不是web服务器。
2、FastCGI处理流程
1、简单处理流程
http://localhost/login?user=zhang3&passwd=123456&age=12&sex=man
- 用户通过浏览器访问服务器, 发送了一个请求, 请求的url如上
- 服务器接收数据, 对接收的数据进行解析
- nginx对于一些登录数据不知道如何处理, nginx将数据发送给了fastcgi程序
- 通过本地套接字
- 网络通信的方式
- fastCGI程序如何启动
- 不是由web服务器直接启动
- 通过一个fastCGI进程管理器启动
- fastcgi启动
- 加载配置 - 可选
- 连接服务器 - 数据库
- 循环
- 服务器有请求 -> 处理
- 将处理结果发送给服务器
- 本地套接字
- 网络通信
- 将处理结果发送给服务器
- 没有请求 -> 阻塞
- 服务器有请求 -> 处理
- 服务器将fastCGI的处理结果发送给客户端
2、深入了解FastCGI处理流程
1.Web 服务器启动时载入初始化FastCGI执行环境。 例如IIS、ISAPI、apache mod_fastcgi、nginx ngx_http_fastcgi_module、lighttpd mod_fastcgi。
2.FastCGI进程管理器自身初始化,启动多个CGI解释器进程并等待来自Web服务器的连接。启动FastCGI进程时,可以配置以ip和UNIX 域socket两种方式启动。
3.当客户端请求到达Web 服务器时, Web 服务器将请求采用socket方式转发FastCGI主进程,FastCGI主进程选择并连接到一个CGI解释器。Web 服务器将CGI环境变量和标准输入发送到FastCGI子进程。
4.FastCGI子进程完成处理后将标准输出和错误信息从同一socket连接返回Web 服务器。当FastCGI子进程关闭连接时,请求便处理完成。
5.FastCGI子进程接着等待并处理来自Web 服务器的下一个连接。
由于FastCGI程序并不需要不断的产生新进程,可以大大降低服务器的压力并且产生较高的应用效率。它的速度效率最少要比CGI 技术提高 5 倍以上。它还支持分布式的部署,即FastCGI 程序可以在web 服务器以外的主机上执行。
3、FastCGI和CGI的区别
CGI 是所谓的短生存期应用程序,FastCGI 是所谓的长生存期应用程序。
FastCGI像是一个常驻(long-live)型的CGI,它可以一直执行着,不会每次都要花费时间去fork一次(这是CGI最为人诟病的fork-and-execute 模式)。
4、FastCGI的安装
安装fastCGI
./configure
make
- fcgio.cpp:50:14: error: 'EOF' was not declared in this scope
- 没有包含对应的头文件:
- stdio.h - c
- cstdio -> c++
使用 find ./ -name fcgio.cpp 所在位置
然后给该文件添加头文件 #include <stdio.h>
再次使用 make 即可
sudo make install
# 使用 find ./ -name *.so 可以查看目录中隐藏的动态库文件
5、进程管理器管理:spawn-fcgi
什么是 spawn-dcgi?
Nginx不能像Apache那样直接执行外部可执行程序,但Nginx可以作为代理服务器,将请求转发给后端服务器,这也是Nginx的主要作用之一。其中Nginx就支持FastCGI代理,接收客户端的请求,然后将请求转发给后端FastCGI进程。
由于FastCGI进程由FastCGI进程管理器管理,而不是Nginx。这样就需要一个FastCGI进程管理器,管理我们编写FastCGI程序。
spawn-fcgi是一个通用的FastCGI进程管理器,简单小巧,原先是属于lighttpd的一部分,后来由于使用比较广泛,所以就迁移出来作为独立项目。
spawn-fcgi使用pre-fork 模型,功能主要是打开监听端口,绑定地址,然后fork-and-exec创建我们编写的FastCGI应用程序进程,退出完成工作。FastCGI应用程序初始化,然后进入死循环侦听socket的连接请求。
安装spawn-fcgi
-
安装
tar -zxvf spawn-fcgi-1.6.4.tar.gz cd spawn-fcgi-1.6.4/ ./configure make sudo make install
如果遇到以下错误:
./autogen.sh: x: autoreconf: not found
因为没有安装automake工具,ubuntu用下面的命令安装即可:
sudo apt-get install autoconf automake libtoo
spawn-fcgi的帮助信息可以通过man spawn-fcgi或spawn-fcgi -h获得,下面是部分常用
spawn-fcgi参数信息:
参数 | 含义 |
---|---|
f | 指定调用FastCGI的进程的执行程序位置 |
-a | 绑定到地址addr |
-p | 绑定到端口port |
-s
| 绑定到unix domain socket |
-C | 指定产生的FastCGI的进程数,默认为5(仅用于PHP) |
-P
| 指定产生的进程的PID文件路径 |
-F | 指定产生的FastCGI的进程数(C的CGI用这个) |
-u和-g FastCGI | 使用什么身份(-u用户、-g用户组)运行,CentOS下可以使用apache用户,其他的根据情况配置,如nobody、www-data等 |
3、nginx && fastcgi
nginx 不能像apache那样直接执行外部可执行程序,但nginx可以作为代理服务器,将请求转发给后端服务器,这也是nginx的主要作用之一。其中nginx就支持FastCGI代理,接收客户端的请求,然后将请求转发给后端fastcgi进程。下面介绍如何使用C/C++编写cgi/fastcgi,并部署到nginx中。
通过前面的介绍知道,fastcgi进程由FastCGI进程管理器管理,而不是nginx。这样就需要一个FastCGI管理,管理我们编写fastcgi程序。我们使用spawn-fcgi作为FastCGI进程管理器。
spawn-fcgi是一个通用的FastCGI进程管理器,简单小巧,原先是属于lighttpd的一部分,后来由于使用比较广泛,所以就迁移出来作为独立项目了。spawn-fcgi使用pre-fork 模型,功能主要是打开监听端口,绑定地址,然后fork-and-exec创建我们编写的fastcgi应用程序进程,退出完成工作。fastcgi应用程序初始化,然后进入死循环侦听socket的连接请求。
Nginx-spawn-fcgi-FastCGI结构图
过程解析
http://localhost/login?user=zhang3&passwd=123456&age=12&sex=man
- 客户端访问, 发送请求
- nginx web服务器, 无法处理用户提交的数据
- spawn-fcgi - 通信过程中的服务器角色
- 被动接收数据
- 在spawn-fcgi启动的时候给其绑定IP和端口
- fastCGI程序
- 程序猿写的 -> login.c -> 可执行程序( login )
- 使用 spawn-fcgi 进程管理器启动 login 程序, 得到一进程
-
nginx的数据转发 - 需要修改nginx的配置文件 nginx.conf
通过请求的url http://localhost/login?user=zhang3&passwd=123456&age=12&sex=man 转换为一个指令: - 去掉协议 - 去掉域名/IP + 端口 - 如果尾部有文件名 去掉 - 去掉 ? + 后边的字符串 - 剩下的就是服务器要处理的指令: /login location /login { # 转发这个数据, fastCGI进程 fastcgi_pass 地址信息:端口; # fastcgi.conf 和nginx.conf在同一级目录: /usr/local/nginx/conf # 这个文件中定义了一些http通信的时候用到环境变量, nginx赋值的 include fastcgi.conf; } 地址信息: - localhost - 127.0.0.1 - 192.168.1.100 端口: 找一个空闲的没有被占用的端口即可
-
spawn-fcgi如何启动
# 前提条件: 程序猿的fastCGI程序已经编写完毕 -> 可执行文件 login spawn-fcgi -a IP地址 -p 端口 -f fastcgi可执行程序 - IP地址: 应该和nginx的 fastcgi_pass 配置项对应 - nginx: localhost -> IP: 127.0.0.1 - nginx: 127.0.0.1 -> IP: 127.0.0.1 - nginx: 192.168.1.100 -> IP: 192.168.1.100 - 端口: 应该和nginx的 fastcgi_pass 中的端口一致
-
fastCGI程序怎么写
// http://localhost/login?user=zhang3&passwd=123456&age=12&sex=man // 要包含的头文件 #include "fcgi_config.h" // 可选 #include "fcgi_stdio.h" // 必须的, 编译的时候找不到这个头文件, find->path , gcc -I // 编写代码的流程 int main() { // FCGI_Accept()是一个阻塞函数, nginx给fastcgi程序发送数据的时候解除阻塞 while (FCGI_Accept() >= 0) { // 1. 接收数据 // 1.1 get方式提交数据 - 数据在请求行的第二部分 // user=zhang3&passwd=123456&age=12&sex=man char *text = getenv("QUERY_STRING"); // 1.2 post方式提交数据 char *contentLength = getenv("CONTENT_LENGTH"); // 根据长度大小判断是否需要循环 // 2. 按照业务流程进行处理 // 3. 将处理结果发送给nginx // 数据回发的时候, 需要告诉nginx处理结果的格式 - 假设是html格式 printf("Content-type: text/html\r\n"); printf("<html>处理结果</html>"); } }
-
strtol 函数使用
// 将数字类型的字符串 -> 整形数 long int strtol(const char *nptr, char **endptr, int base); - 参数nptr: 要转换的字符串 - 数字类型的字符串: "123", "0x12", "0776" - 参数endptr: 测试时候使用, 一般指定为NULL - 参数base: 进制的指定 - 10 , nptr = "123456", 如果是"0x12"就会出错 - 8 , nptr = "0345" - 16, nptr = "0x1ff" char* p = "123abc"; char* pt = NULL; strtol(p, &pt, 10); - 打印pt的值: "abc"
4、FastCGI环境变量-fastcgi.conf
fastcgi.conf 文件与 nginx.conf在同一目录,都是在 /usr/local/nginx/conf 目录里
环境变量 | 说明 |
---|---|
SCRIPT_FILENAME | 脚本文件请求的路径 |
QUERY_STRING | 请求的参数;如?app=123 |
REQUEST_METHOD | 请求的动作(GET,POST) |
CONTENT_TYPE | 请求头中的Content-Type字段 |
CONTENT_LENGTH | 请求头中的Content-length字段 |
SCRIPT_NAME | 脚本名称 |
REQUEST_URI | 请求的地址不带参数 |
DOCUMENT_URI | 与$uri相同 |
DOCUMENT_ROOT | 网站的根目录。在server配置中root指令中指定的值 |
SERVER_PROTOCOL | 请求使用的协议,通常是HTTP/1.0或HTTP/1.1 |
GATEWAY_INTERFACE | cgi 版本 |
SERVER_SOFTWARE | nginx 版本号,可修改、隐藏 |
REMOTE_ADDR | 客户端IP |
REMOTE_PORT | 客户端端口 |
SERVER_ADDR | 服务器IP地址 |
SERVER_PORT | 服务器端口 |
SERVER_NAME | 服务器名,域名在server配置中指定的server_name |
5、FastCGI测试
1、先在nginx配置文件中配置zyFile2资源目录,默认页面是 upload.html 页面,然后使用 sudo -s reload 命令,重新启动nginx web服务器,会看的如下页面,我们选择一个图片进行提交:
2、提交后页面显示404错误,404错误是因为在nginx配置文件中没有找到提交后的资源路径或者指令,我们可以查看静态页面的源代码,找到对应的指令或者需要跳转的路径,在Nginx服务器的配置文件中添加这个location,由于我们是fastcgi的测试,所以需要使用刚刚学习的spawn-fcgi进程管理器,将请求资源转发给 spawn-fcgi ,然后spawn-fcgi进程管理器去启动一个fastcgi进程去处理这个请求资源
3、在Nginx配置文件中配置如下指令,然后重新提交图片,会看到 An error occurred 错误,这就是nginx服务器没有启动FastCGI进程,需要我们手动去启动一个处理这个请求的FastCGI进程。
4、我们可以在fastcgi的源码目录里的 examples 目录里找到 echo 程序作为FastCGI进程,它是由 echo.c 编译生成的可执行文件,我们用它来处理客户端发来的提交请求。
echo.c源码
/*
* echo.c --
*
* Produce a page containing all FastCGI inputs
*
*
* Copyright (c) 1996 Open Market, Inc.
*
* See the file "LICENSE.TERMS" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
*/
#ifndef lint
static const char rcsid[] = "$Id: echo.c,v 1.5 1999/07/28 00:29:37 roberts Exp $";
#endif /* not lint */
#include "fcgi_config.h"
#include <stdlib.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef _WIN32
#include <process.h>
#else
extern char **environ;
#endif
#include "fcgi_stdio.h"
static void PrintEnv(char *label, char **envp)
{
printf("%s:<br>\n<pre>\n", label);
for ( ; *envp != NULL; envp++) {
printf("%s\n", *envp);
}
printf("</pre><p>\n");
}
int main ()
{
char **initialEnv = environ;
int count = 0;
while (FCGI_Accept() >= 0) {
char *contentLength = getenv("CONTENT_LENGTH");
int len;
printf("Content-type: text/html\r\n"
"\r\n"
"<title>FastCGI echo</title>"
"<h1>FastCGI echo</h1>\n"
"Request number %d, Process ID: %d<p>\n", ++count, getpid());
if (contentLength != NULL) {
len = strtol(contentLength, NULL, 10);
}
else {
len = 0;
}
if (len <= 0) {
printf("No data from standard input.<p>\n");
}
else {
int i, ch;
printf("Standard input:<br>\n<pre>\n");
for (i = 0; i < len; i++) {
if ((ch = getchar()) < 0) {
printf("Error: Not enough bytes received on standard input<p>\n");
break;
}
putchar(ch);
}
printf("\n</pre><p>\n");
}
PrintEnv("Request environment", environ);
PrintEnv("Initial environment", initialEnv);
} /* while */
return 0;
}
6、使用 spawn-fcgi 进程管理器启动 FastCGI 进程,IP和地址与配置文件中的信息一一对应。
spawn-fcgi -a 127.0.0.1 -p 8000 -f ./echo
注意:
我们这次能成功是因为 echo 程序是已经编译好的,并且可以直接使用的,但是我们一般都是自己编写 FastCGI 进程,在往往会出现一个错误
spawn-fcgi:child existed with:127
解决方法:
这是由于找不到 libfcgi.so.0 库文件,我们先使用 sudo find / -name “libfcgi.so.0” 找到该资源所在的路径,一般在 /usr/local/lib/libfcgi.so.0 ,这时我们可以添加一个软链接到 /usr/lib 目录:
sudo ln -s /usr/local/lib/libfcgi.so.0 /usr/lib/libfcgi.so.0
7、启动 fastcgi 进程后再次进行提交,则就输出了 echo 程序里的内容,即打印出fastcgi.conf里的环境变量和系统里的环境变量
中间是图片的二进制内容,可以看到这里有两块内容,它们之间使用 一行随机字符串分割,各个区间表示是一个独立的内容
echo 程序中打印的 http协议 环境变量,在fastcgi.conf中
打印当期系统的环境变量
表单 get/post 提交方式测试
随便编辑一个表单,将该表单地址注册到nginx配置文件中,用户访问 /info/资源目录就会默认显示info.html页面
get方式
修改配置文件,将提交的数据转发给FastCGI进程去处理该请求
使用 echo 作为fastCGI 进程,然后利用 spawn-fcgi 进程管理器启动这个进程
提交后的内容,get方式会将提交的数据打印在地址栏
修改为post方式后,地址栏没信息,而是在最后一部分
分析上图,找到get方式和post方式的环境变量有什么不同
6、Nginx作为web服务器处理请求
-
静态请求
客户端访问服务器的静态网页, 不涉及任何数据的处理, 如下面的URL:
http://localhsot/login.html
-
动态请求
客户端会将数据提交给服务器
# 使用get方式提交数据得到的url http://localhost/login?user=zhang3&passwd=123456&age=12&sex=man - http: 协议 - localhost: 域名 - /login: 服务器端要处理的指令 - ? : 连接符, 后边的内容是客户端给服务器提交的数据 - & : 分隔符 动态的url如何找服务器端处理的指令? - 去掉协议 - 去掉域名/IP - 去掉端口 - 去掉?和它后边的内容 # 如果看到的是请求行, 如何找处理指令? POST /upload/UploadAction HTTP/1.1 GET /?username=tom&phone=123&email=hello%40qq.com&date=2018-01-01&sex=male&class=3&rule=on HTTP/1.1 1. 找请求行的第二部分 - 如果是post, 处理指令就是请求行的第二部分 - 如果是get, 处理指令就是请求行的第二部分, ? 以前的内容
7、http协议
-
请求消息(Request) - 客户端(浏览器)发送给服务器的数据格式
四部分: 请求行, 请求头, 空行, 请求数据
- 请求行: 说明请求类型, 要访问的资源, 以及使用的http版本
- 请求头: 说明服务器要使用的附加信息
- 空行: 空行是必须要有的, 即使没有请求数据
- 请求数据: 也叫主体, 可以添加任意的其他数据
-
Get方式提交数据
第一行: 请求行
第2-9行: 请求头(键值对)
第10行: 空行
get方式提交数据, 没有第四部分, 提交的数据在请求行的第二部分, 提交的数据会全部显示在地址栏中
GET /?username=tom&phone=123&email=hello%40qq.com&date=2018-01-01&sex=male&class=3&rule=on HTTP/1.1 Host: 192.168.26.52:6789 Connection: keep-alive Cache-Control: max-age=0 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8 Accept-Encoding: gzip, deflate Accept-Language: zh,zh-CN;q=0.9,en;q=0.8
-
Post方式提交数据
第一行: 请求行
第2 -12行: 请求头 (键值对)
第13行: 空行
第14行: 提交的数据
POST / HTTP/1.1 Host: 192.168.26.52:6789 Connection: keep-alive Content-Length: 84 #这个长度就是最后一行提交的数据的长度,Get方式由于没有第四部分,所以也就没有这个长度 Cache-Control: max-age=0 Upgrade-Insecure-Requests: 1 Origin: null Content-Type: application/x-www-form-urlencoded User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8 Accept-Encoding: gzip, deflate Accept-Language: zh,zh-CN;q=0.9,en;q=0.8 username=tom&phone=123&email=hello%40qq.com&date=2018-01-01&sex=male&class=3&rule=on
-
响应消息(Response) -> 服务器给客户端发送的数据
- 四部分: 状态行, 消息报头, 空行, 响应正文
- 状态行: 包括http协议版本号, 状态码, 状态信息
- 消息报头: 说明客户端要使用的一些附加信息
- 空行: 空行是必须要有的
- 响应正文: 服务器返回给客户端的文本信息
第一行:状态行
第2 -11行: 响应头(消息报头)
第12行: 空行
第13-18行: 服务器给客户端回复的数据
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 Location:url 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; }
- 四部分: 状态行, 消息报头, 空行, 响应正文
-
http状态码
状态代码有三位数字组成,第一个数字定义了响应的类别,共分五种类别:
- 1xx:指示信息–表示请求已接收,继续处理
- 2xx:成功–表示请求已被成功接收、理解、接受
- 3xx:重定向–要完成请求必须进行更进一步的操作
- 4xx:客户端错误–请求有语法错误或请求无法实现
- 5xx:服务器端错误–服务器未能实现合法的请求
8、Post提交数据常用的方式
Http协议规定 POST 提交的数据必须放在消息主体(entity-body)中,但协议并没有规定数据必须使用什么编码方式。
开发者完全可以自己决定消息主体的格式
数据发送出去,还要服务端解析成功才有意义, 服务端通常是根据请求头(headers)中的
Content-Type 字段来获知请求中的消息主体是用何种方式编码,再对主体进行解析。
常用的四种方式
-
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&user=kevin&passwd=32222
-
application/json
POST / HTTP/1.1 Content-Type: application/json;charset=utf-8 {"title":"test","sub":[1,2,3]}
-
text/xml
POST / HTTP/1.1 Content-Type: text/xml <?xml version="1.0" encoding="utf8"?> <methodcall> <methodname color="red">examples.getStateName</methodname> <params> <value><i4>41</i4></value> </params> </methodcall> <font color="red">nihao, shijie</font>
-
multipart/form-data
用于发送大量的数据,每段数据上下都有一行同一样的随机字符串,用于区分不同的数据,每一段数据都是独立的,里面包括了数据具体的信息,可以自己设置
POST / HTTP/1.1 Content-Type: multipart/form-data # 发送的数据 ------WebKitFormBoundaryPpL3BfPQ4cHShsBz \r\n Content-Disposition: form-data; name="file"; filename="qw.png" Content-Type: image/png\r\n; md5="xxxxxxxxxx" \r\n .............文件内容................ .............文件内容................ ------WebKitFormBoundaryPpL3BfPQ4cHShsBz-- Content-Disposition: form-data; name="file"; filename="qw.png" Content-Type: image/png\r\n; md5="xxxxxxxxxx" \r\n .............文件内容................ .............文件内容................ ------WebKitFormBoundaryPpL3BfPQ4cHShsBz--
http协议里面的Content-Type,不同的文件类型对应不同的Content-Type
网址:http://tool.oschina.net/
部分内容展示