HTTP协议是以ASCII码传输,建立在TCP/IP协议之上的应用层规范。
规范把HTTP请求分为三个部分:状态行、请求头和请求主体。
- 状态行
GET 请求报文示例:
Host User-Agent Connection:keep-Alive
POST 请求报文示例
HOST User-Agent Content-Type: application/x-www-form-urlencode Connection:Keep-Alive Content-Length:40
- 响应报文
HTTP 响应与 HTTP 请求相似。
HTTP响应也由3个部分构成,分别是:状态行、响应头和响应主体。
200正常 400客户端语法错误 500内部服务器错误。
- 持久连接
我们知道 HTTP 协议采用“请求-应答”模式,当使用普通模式,即非 Keep-Alive 模式时,每个请求/应答客户和服务器都要新建一个连接,完成之后立即断开连接(HTTP协议为无连接的协议);当使用 Keep-Alive 模式(又称持久连接、连接重用)时,Keep-Alive 功能使客户端到服务器端的连接持续有效,当出现对服务器的后继请求时,Keep-Alive 功能避免了建立或者重新建立连接。当客户端发送另外一个请求时,就使用这条已经建立的连接。
HTTP Keep-Alive 简单说就是保持当前的TCP连接,避免了重新建立连接。
HTTP 长连接不可能一直保持,例如Keep-Alive: timeout=5, max=100,表示这个TCP通道可以保持5秒,max=100,表示这个长连接最多接收100次请求就断开
四种常见的POST提交数据方式
- application/x-www-form-urlencoded (默认)
- multipart/form-data
- application/json
- text/xml
application/x-www-form-urlencoded
很多时候,我们用 Ajax 提交数据时,也是使用这种方式。例如 JQuery 和 QWrap 的 Ajax,Content-Type 默认值都是「application/x-www-form-urlencoded;charset=utf-8」。
multipart/form-data
主要用于上传文件
POST http://www.example.com HTTP/1.1
Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryrGKCBY7qhFd3TrwA
------WebKitFormBoundaryrGKCBY7qhFd3TrwA
Content-Disposition: form-data; name="text"
title
------WebKitFormBoundaryrGKCBY7qhFd3TrwA
Content-Disposition: form-data; name="file"; filename="chrome.png"
Content-Type: image/png
PNG ... content of chrome.png ...
------WebKitFormBoundaryrGKCBY7qhFd3TrwA--
首先生成了一个 boundary 用于分割不同的字段,为了避免与正文内容重复,boundary 很长很复杂。然后 Content-Type 里指明了数据是以 multipart/form-data 来编码,本次请求的 boundary 是什么内容。消息主体里按照字段个数又分为多个结构类似的部分,每部分都是以 –boundary 开始,紧接着是内容描述信息,然后是回车,最后是字段具体内容(文本或二进制)。如果传输的是文件,还要包含文件名和文件类型信息。消息主体最后以 –boundary– 标示结束。
application/json
用来告诉服务端消息主体是序列化后的 JSON 字符串。由于 JSON 规范的流行,除了低版本 IE 之外的各大浏览器都原生支持 JSON.stringify,服务端语言也都有处理 JSON 的函数,使用 JSON 不会遇上什么麻烦。
JSON 格式支持比键值对复杂得多的结构化数据,这一点也很有用。
POST http://www.example.com HTTP/1.1
Content-Type: application/json;charset=utf-8
{"title":"test","sub":[1,2,3]}
这种方案,可以方便的提交复杂的结构化数据,特别适合 RESTful 的接口。各大抓包工具如 Chrome 自带的开发者工具、Firebug、Fiddler,都会以树形结构展示 JSON 数据,非常友好。但也有些服务端语言还没有支持这种方式,例如 php 就无法通过 $_POST 对象从上面的请求中获得内容。这时候,需要自己动手处理下:在请求头中 Content-Type 为 application/json 时,从 php://input 里获得原始输入流,再 json_decode 成对象。一些 php 框架已经开始这么做了。
当然 AngularJS 也可以配置为使用 x-www-form-urlencoded 方式提交数据。如有需要,可以参考这篇文章。
关于 text/xml
典型的 XML-RPC 请求是这样的:
POST http://www.example.com HTTP/1.1
Content-Type: text/xml
<?xml version="1.0"?>
<methodCall>
<methodName>examples.getStateName</methodName>
<params>
<param>
<value><i4>41</i4></value>
</param>
</params>
</methodCall>
XML-RPC 协议简单、功能够用,各种语言的实现都有。它的使用也很广泛,如 WordPress 的 XML-RPC Api,搜索引擎的 ping 服务等等。JavaScript 中,也有现成的库支持以这种方式进行数据交互,能很好的支持已有的 XML-RPC 服务。不过,我个人觉得 XML 结构还是过于臃肿,一般场景用 JSON 会更灵活方便。
服务器端使用xmlrpc_server_create函数产生一个服务器端
PHP中集成了XML-RPC和SOAP两种协议的访问,都是集中在xmlrpc扩展当中。而且该扩展跟xmlrpc扩展无关,能够独立实现XML-RPC的协议交互,如果没有xmlrpc扩展,建议使用PEAR::XML-RPC扩展。
XML-RPC 服务端
/**
* 函数:提供给RPC客户端调用的函数
* 参数:
* $method 客户端需要调用的函数
* $params 客户端需要调用的函数的参数数组
* 返回:返回指定调用结果
*/
function rpc_server_func($method, $params)
{
$parameter = $params[0];
if ($parameter == "get") {
$return = 'This data by get method';
} else {
$return = 'Not specify method or params';
}
return $return;
}
//产生一个XML-RPC的服务器端
$xmlrpc_server = xmlrpc_server_create();
//注册一个服务器端调用的方法rpc_server,实际指向的是rpc_server_func函数
xmlrpc_server_register_method($xmlrpc_server, "rpc_server", "rpc_server_func");
//接受客户端POST过来的XML数据
$request = $HTTP_RAW_POST_DATA;
//执行调用客户端的XML请求后获取执行结果
$xmlrpc_response = xmlrpc_server_call_method($xmlrpc_server, $request, null);
//把函数处理后的结果XML进行输出
header('Content - Type: text/xml');
echo $xmlrpc_response;
//销毁XML-RPC服务器端资源
xmlrpc_server_destroy($xmlrpc_server);
XML-RPC 客户端
/**
* 函数:提供给客户端进行连接XML-RPC服务器端的函数
* 参数:
* $host 需要连接的主机
* $port 连接主机的端口
* $rpc_server XML-RPC服务器端文件
* $request 封装的XML请求信息
* 返回:连接成功成功返回由服务器端返回的XML信息,失败返回false
*/
function rpc_client_call($host, $port, $rpc_server, $request)
{
//打开指定的服务器端
$fp = fsockopen($host, $port);
//构造需要进行通信的XML-RPC服务器端的查询POST请求信息
$query = "POST $rpc_server HTTP/1.0\nUser_Agent: XML-RPC Client\nHost: " . $host . "\nContent-Type: text/xml\nContent-Length: " . strlen($request) . "\n\n" . $request . "\n";
//把构造好的HTTP协议发送给服务器,失败返回false
if (!fputs($fp, $query, strlen($query))) {
$errstr = "Write error";
return false;
}
//获取从服务器端返回的所有信息,包括HTTP头和XML信息
$contents = '';
while (!feof($fp)) {
$contents .= fgets($fp);
}
//关闭连接资源后返回获取的内容
fclose($fp);
return $contents;
}
//构造连接RPC服务器端的信息
$host = 'localhost';
$port = 80;
$rpc_server = '/rpc_server.php';
//把需要发送的XML请求进行编码成XML,需要调用的方法是rpc_server,参数是get
$request = xmlrpc_encode_request('rpc_server', 'get');
//调用rpc_client_call函数把所有请求发送给XML-RPC服务器端后获取信息
$response = rpc_client_call($host, $port, $rpc_server, $request);
//分析从服务器端返回的XML,去掉HTTP头信息,并且把XML转为PHP能识别的字符串
$split = '';
$xml = explode($split, $response);
$xml = $split . array_pop($xml);
$response = xmlrpc_decode($xml);
//输出从RPC服务器端获取的信息
print_r($response);
Yar 鸟哥开发的一个PHP扩展的, RPC框架, 和现有的RPC框架(xml-rpc, soap)不同, 这是一个轻量级的框架, 支持多种打包协议(msgpack, json, php), 并且最重要的一个特点是, 它是可并行化的..
从xml-rpc请求方式,引申到ajax,关于ajax 设置请求头 http header的问题:
ajax 设置header头部有两种方法:
1.headers : {‘Authorization’:’Basic bmVvd2F5Oe4lb3dheQ==’}
多个头部用逗号‘,’分隔开
2.定义beforeSend方法
beforeSend: function(xhr) {
xhr.setRequestHeader(“Authorization”, “Basic bmVvd2F5Oe4lb3dheQ==”);
}
引申:
JSON 与 JSONP 区别:
JSON是一种数据交换格式,而JSONP是一种依靠开发人员的聪明才智创造出的一种非官方跨域数据交互协议。ajax的核心是通过XmlHttpRequest获取非本页内容,而jsonp的核心则是动态添加
ajax设置
$.ajax({
type: "post",
url:"http://abc.cc/qrcode3/index.php/home/index/testpost",
dataType: "jsonp",
jsonp: "callbackparam",//传递给请求处理程序或页面的,用以获得jsonp回调函数名的参数名(默认为:callback),
jsonpCallback:"success_jsonpCallback",//自定义的jsonp回调函数名称,默认为jQuery自动生成的随机函数名,然后会自动调用success方法。写死的话会调用js中指定的function
data: {"key":"value"},
// headers : {'Authorization':'Basic bmVvd2F5Oe4lb3dheQ=='},
success: function(data){ alert(data); },
error: function(data){ alert("error"); } ,
});
//PHP服务端代码
//关于为什么这边要接受callback参数?
//为了更好的兼容性,此函数可以作为基类函数,只要此处写了callback,然后可以在客户端实现调用不同的js的效果
public function testpost() {
$data = $_GET['key']?$_GET['key']:"123456";
$callback = $_GET['callback'];
if(!preg_match("/^[0-9a-zA-Z_.]+$/",$callback)){
exit('参数错误!');
}
echo $callback.'('.json_encode($data).')';
exit;
}
关于JSONP
之前写过一个小的html页面,然后用于监控一个币的价格波动,其中涉及到的一个问题就是JSOP跨域请求,无法请求的问题。然后了解了下JSONP 请求。
总结
1. url 中不用指定 callback 参数。对于 jQuery 中的 jsonp 来说,callback 参数是自动添加的
默认情况下,jQuery 生成的 jsonp 请求中 callback 参数是形如 callback=jQuery180099599698651582_1393992892572 这种根据看似随机的名字,对应的就是 success 那个处理函数,所以一般不用特意处理。
2. 服务端返回的格式应该返回字符串数据格式:test(JSON数据)
3. 如果响应内容中是写死了 callback 名的话,那么可以对 jQuery 的 jsonp 指定 callback 方法
4. ajax跨域是不能设置http header的,为了能够设置header,需要在服务器php代码中添加header(‘Access-Control-Allow-Origin:http://abc.cn’); 允许http://abc.cn访问服务器abc.cc,再通过设置header(‘Access-Control-Allow-Headers:Authorization,lpy’);把需要设置的http header属性添加进去,在jquery代码中通过header来设置响应的属性值,在http的请求头中就可以看到 Authorization和lpy的请求头,服务器php端通过$header = apache_request_headers()获取http header的数组,继而可以获取到lpy的请求头的值。
关于本地测试代码,如何解决浏览器跨域不支持问题:
我的解决方案:在谷歌浏览器中下载 Allow-Control-Allow-Origin: * 插件,可实现直接使用json,实现浏览器跨域请求的问题