前谷歌设计师,同时也是现 Dropbox 设计副总裁 Nicholas Jitkoff
创造了一种“仅存在于 URL 中的网站”  itty
bitty。

Case I. Web代理的方式 (on Server A)

本文非全部原创,大部分内容来自:

可以先点击这里进去感受一下该网站的特点。

即用户访问 A 网站时所产生的对 B 网站的跨域访问请求均提交到 A
网站的指定页面,由该页面代替用户页面完成交互,从而返回合适的结果。此方案可以解决现阶段所能够想到的多数跨域访问问题,但要求
A 网站提供 Web 代理的支持,因此A网站与 B
网站之间必须是紧密协作的,且每次交互过程,A
网站的服务器负担增加,且无法代用户保存 Session 状态。

http://igoro.com/archive/what-really-happens-when-you-navigate-to-a-url/ 

澳门新葡萄京官网注册 1

Case II. on-Demand方式 (on Server A)

1,在浏览器中输入网址

用户输入网址URL,比如

据介绍,这是一种自包含微型网站,与传统网站最大的区别在于,它不需要服务器对网站进行托管,而是将网页内容压缩、编码到
URL 中,通过该 URL 在用户间(“客户端”间)传播数据。

MYMSN 的门户就用的这种方式,不过 MYMSN
中不涉及跨域访问问题。在页面内动态生成新的 <script>,将其 src
属性指向别的网站的网址,这个网址返回的内容必须是合法的 Javascript
脚本,常用的是 JSON 消息。此方案存在的缺陷是,script 的 src
属性完成该调用时采取的方式时 get 方式,如果请求时传递的字符串过大时,可能会无法正常运行。不过此方案非常适合聚合类门户使用。

2,浏览器查找域名的IP地址

导航的第一步是通过访问的域名找出其IP地址。DNS查找过程如下:

  • 澳门新葡萄京官网注册 ,浏览器缓存 – 浏览器会缓存DNS记录一段时间。
    有趣的是,操作系统没有告诉浏览器储存DNS记录的时间,这样不同浏览器会储存个自固定的一个时间(2分钟到30分钟不等)。
  • 系统缓存 –
    如果在浏览器缓存里没有找到需要的记录,浏览器会做一个系统调用(windows里是gethostbyname)。这样便可获得系统缓存中的记录。
  • 路由器缓存 –
    接着,前面的查询请求发向路由器,它一般会有自己的DNS缓存。
  • ISP DNS 缓存 –
    接下来要check的就是ISP缓存DNS的服务器。在这一般都能找到相应的缓存记录。
  • 递归搜索 –
    你的ISP的DNS服务器从跟域名服务器开始进行递归搜索,从.com顶级域名服务器到Facebook的域名服务器。一般DNS服务器的缓存中会有.com域名服务器中的域名,所以到顶级服务器的匹配过程不是那么必要了。

DNS有一点令人担忧,这就是像wikipedia.org 或者
facebook.com这样的整个域名看上去只是对应一个单独的IP地址。还好,有几种方法可以消除这个瓶颈:

  • 循环
    DNS
     是DNS查找时返回多个IP时的解决方案。举例来说,Facebook.com实际上就对应了四个IP地址。
  • 负载平衡器 是以一个特定IP地址进行侦听并将网络请求转发到集群服务器上的硬件设备。
    一些大型的站点一般都会使用这种昂贵的高性能负载平衡器。
  • 地理
    DNS 
    根据用户所处的地理位置,通过把域名映射到多个不同的IP地址提高可扩展性。这样不同的服务器不能够更新同步状态,但映射静态内容的话非常好。
  • Anycast 是一个IP地址映射多个物理主机的路由技术。
    美中不足,Anycast与TCP协议适应的不是很好,所以很少应用在那些方案中。

大多数DNS服务器使用Anycast来获得高效低延迟的DNS查找。

这个过程就是客户端先检查本地是否有对应的IP地址,若找到则返回响应的IP地址。若没找到则请求上级DNS服务器,直至找到或到根节点。

具体来讲,该机制可以拆分为以下几个步骤:

<html>
<head>
<script language="javascript" type="text/javascript">
function loadContent()
{
    var s=document.createElement('script');
    s.src='http://www.anotherdomain.com/TestCrossJS.aspx?f=setDivContent';
    document.body.appendChild(s);
}



function setDivContent(v)
{
    var dv = document.getElementById("dv");
    dv.innerHTML = v; 
}
</script>


<div id=dv></div>

<input onclick=loadContent() type=button value="Click Me">

3,浏览器给web服务器发送一个HTTP请求

因为像Facebook主页这样的动态页面,打开后在浏览器缓存中很快甚至马上就会过期,毫无疑问他们不能从中读取。

所以,浏览器将把一下请求发送到Facebook所在的服务器:

GET http://facebook.com/ HTTP/1.1
 Accept: application/x-ms-application, image/jpeg, application/xaml+xml, [...]
 User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; [...]
 Accept-Encoding: gzip, deflate
 Connection: Keep-Alive
 Host: facebook.com
 Cookie: datr=1265876274-[...]; locale=en_US; lsd=WW[...]; c_user=2101[...]

GET 这个请求定义了要读取的URL: “
浏览器自身定义 (User-Agent 头), 和它希望接受什么类型的相应
(Accept and Accept-Encoding 头). Connection头要求服务器为了后边的请求不要关闭TCP连接。

请求中也包含浏览器存储的该域名的cookies。可能你已经知道,在不同页面请求当中,cookies是与跟踪一个网站状态相匹配的键值。这样cookies会存储登录用户名,服务器分配的密码和一些用户设置等。Cookies会以文本文档形式存储在客户机里,每次请求时发送给服务器。

用来看原始HTTP请求及其相应的工具很多。作者比较喜欢使用fiddler,当然也有像FireBug这样其他的工具。这些软件在网站优化时会帮上很大忙。

除了获取请求,还有一种是发送请求,它常在提交表单用到。发送请求通过URL传递其参数(e.g.:

像“:
//example.com/folderOrFile”这样的地址,因为浏览器不清楚folderOrFile到底是文件夹还是文件,所以不能自动添加
斜杠。这时,浏览器就不加斜杠直接访问地址,服务器会响应一个重定向,结果造成一次不必要的握手。 

  • itty bitty 使用可以显著减小 HTML
    体积的 Lempel–Ziv–Markov 链算法对网页内容进行压缩。

  • 压缩之后,itty bitty
    对压缩后的内容进行 base64 编码,将其从二进制数据转换为可以安全存储在
    URL 中的字母和数字字符串。

  • 转化的结果被称为 URL
    片段(fragment),此时
    itty bitty 会将该 URL 片段作为后缀附着在
    URL,其中
    Name 表示该页面的 title。

  • 生成的 URL 通过复制或者直接分享到 Twitter
    等方式进行传播,接收者访问该 URL时,Web 浏览器会加载 itty.bitty.site
    以逆转上述过程,从而获取最初的 HTML
    页面内容。值得一提的是,因为没有服务器存在,那么用户访问该 URL
    的时候,其网络请求并不会发往所谓的”服务器“,而是借助于 URL fragment
    的特性“跳转到页面上的某个位置”。

其中的 www.anotherdomain.com/TestCrossJS.aspx 是这样的:

4,facebook服务的永久重定向响应

Facebook服务器发回给浏览器的响应:

HTTP/1.1 301 Moved Permanently
 Cache-Control: private, no-store, no-cache, must-revalidate, post-check=0,
 pre-check=0
 Expires: Sat, 01 Jan 2000 00:00:00 GMT
 Location: http://www.facebook.com/
 P3P: CP="DSP LAW"
 Pragma: no-cache
 Set-Cookie: made_write_conn=deleted; expires=Thu, 12-Feb-2009 05:09:50 GMT;
 path=/; domain=.facebook.com; httponly
 Content-Type: text/html; charset=utf-8
 X-Cnection: close
 Date: Fri, 12 Feb 2010 05:09:51 GMT
 Content-Length: 0

服务器给浏览器响应一个301永久重定向响应,这样浏览器就会访问“
而非“

为什么服务器一定要重定向而不是直接发会用户想看的网页内容呢?这个问题有好多有意思的答案。

其中一个原因跟搜索引擎排名
关。你看,如果一个页面有两个地址,就像

什么意思,这样就会把访问带www的和不带www的地址归到同一个网站排名下。

还有一个是用不同的地址会造成缓存友好性变差。当一个页面有好几个名字时,它可能会在缓存里出现好几次。

itty
bitty 已开源,地址:
不应该用于传输数据,这一点在 GET/POST 的设计上早就有警示。

<script language="C#" runat="server">
void Page_Load(object sender, EventArgs e)
{
  string f = Request.QueryString["f"];
  Response.Clear();
  Response.ContentType = "application/x-javascript";
  Response.Write(String.Format(@"
                   {0}('{1}');", 
                   f,
                   DateTime.Now));
  Response.End();
}
</script> 

5,浏览器跟踪重定向地址

现在,浏览器知道了“

GET http://www.facebook.com/ HTTP/1.1
 Accept: application/x-ms-application, image/jpeg, application/xaml+xml, [...]
 Accept-Language: en-US
 User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; [...]
 Accept-Encoding: gzip, deflate
 Connection: Keep-Alive
 Cookie: lsd=XW[...]; c_user=21[...]; x-referer=[...]
 Host: www.facebook.com

头信息以之前请求中的意义相同。

你怎么看?

点击“Click Me”按钮,生成一个新的 script tag,下载对应的 Javascript
脚本,结束时回调其中的 setDivContent(),从而更新网页上一个 div 的内容。

6,服务器“处理”请求

服务器接收到获取请求,然后处理并返回一个响应。

这表面上看起来是一个顺向的任务,但其实这中间发生了很多有意思的东西-
就像作者博客这样简单的网站,何况像facebook那样访问量大的网站呢!

  • Web 服务器软件 web服务器软件(像IIS和阿帕奇)接收到HTTP请求,然后确定执行什么请求处理来处理它。请求处理就是一个能够读懂请求并且能生成HTML来进行响应的程序(像ASP.NET,PHP,RUBY…)。举个最简单的例子,需求处理可以以映射网站地址结构的文件层次存储。像
    址会映射/httpdocs/folder1/page1.aspx这个文件。web服务器软件可以设置成为地址人工的对应请求处理,这样
    page1.aspx的发布地址就可以是
  • 请求处理 请求处理阅读请求及它的参数和cookies。它会读取也可能更新一些数据,并讲数据存储在服务器上。然后,需求处理会生成一个HTML响应。

所有动态网站都面临一个有意思的难点
-如何存储数据。小网站一半都会有一个SQL数据库来存储数据,存储大量数据和/或访问量大的网站不得不找一些办法把数据库分配到多台机器上。解决方案
有:sharding
(基于主键值讲数据表分散到多个数据库中),复制,利用弱语义一致性的简化数据库。

委托工作给批处理是一个廉价保持数据更新的技术。举例来讲,Fackbook得及时更新新闻feed,但数据支持下的“你可能认识的人”功能只需要每晚更新
(作者猜测是这样的,改功能如何完善不得而知)。批处理作业更新会导致一些不太重要的数据陈旧,但能使数据更新耕作更快更简洁。

(文/开源中国)    

编者注:如果 Ajax 的 js 内容由 B 提供,则 A
可以利用 B 提供的 js 方便地访问 B 的资源。比如 A 中的 js 代码:

7. 服务器发回一个HTML响应

服务器生成并返回的响应:

HTTP/1.1 200 OK
 Cache-Control: private, no-store, no-cache, must-revalidate, post-check=0,
 pre-check=0
 Expires: Sat, 01 Jan 2000 00:00:00 GMT
 P3P: CP="DSP LAW"
 Pragma: no-cache
 Content-Encoding: gzip
 Content-Type: text/html; charset=utf-8
 X-Cnection: close
 Transfer-Encoding: chunked
 Date: Fri, 12 Feb 2010 09:05:55 GMT

 2b3Tn@[...]

整个响应大小为35kB,其中大部分在整理后以blob类型传输。

内容编码头告诉浏览器整个响应体用gzip算法进行压缩。解压blob块后,你可以看到如下期望的HTML:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"    
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
 lang="en" id="facebook" class=" no_js">
 <head>
 <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
 <meta http-equiv="Content-language" content="en" />
 ...

关于压缩,头信息说明了是否缓存这个页面,如果缓存的话如何去做,有什么cookies要去设置(前面这个响应里没有这点)和隐私信息等等。

请注意报头中把Content-type设置为“text/html”。报头让浏览器将该响应内容以HTML形式呈现,而不是以文件形式下载它。浏览器会根据报头信息决定如何解释该响应,不过同时也会考虑像URL扩展内容等其他因素。

<script type="text/javascript" src="B/core.js"></script>

8,浏览器开始显示HTML

在浏览器没有完整接受全部HTML文档时,它就已经开始显示这个页面了。

Case III. iframe 方式 (on Server A)

9,浏览器发送获取嵌入在HTML中的对象

在浏览器显示HTML时,它会注意到需要获取其他地址内容的标签。这时,浏览器会发送一个获取请求来重新获得这些文件。

下面是几个我们访问facebook.com时需要重获取的几个URL:

  • 图片

  • CSS 式样表

  • JavaScript 文件

这些地址都要经历一个和HTML读取类似的过程。所以浏览器会在DNS中查找这些域名,发送请求,重定向等等…


不像动态页面那样,静态文件会允许浏览器对其进行缓存。有的文件可能会不需要与服务器通讯,而从缓存中直接读取。服务器的响应中包含了静态文件保存的期限
信息,所以浏览器知道要把它们缓存多长时间。还有,每个响应都可能包含像版本号一样工作的ETag头(被请求变量的实体值),如果浏览器观察到文件的版本
ETag信息已经存在,就马上停止这个文件的传输。

试着猜猜看“fbcdn.net”在地址中代表什么?聪明的答案是”Facebook内容分发网络”。Facebook利用内容分发网络(CDN)分发像图片,CSS表和JavaScript文件这些静态文件。所以,这些文件会在全球很多CDN的数据中心中留下备份。

静态内容往往代表站点的带宽大小,也能通过CDN轻松的复制。通常网站会使用第三方的CDN。例如,Facebook的静态文件由最大的CDN提供商Akamai来托管。

举例来讲,当你试着ping
static.ak.fbcdn.net的时候,可能会从某个akamai.net服务器上获得响应。有意思的是,当你同样再ping一次的时候,响应的服务器可能就不一样,这说明幕后的负载平衡开始起作用了。

查看过醒来在 javaeye 上的一篇关于跨域访问的帖子,他提到自己已经用 iframe
的方式解决了跨域访问问题。数据提交跟获取,采用iframe这种方式的确可以了,但由于父窗口与子窗口之间不能交互(跨域访问的情况下,这种交互被拒绝),因此无法完成对父窗口效果的影响。

10,浏览器发送异步(AJAX)请求

在Web 2.0伟大精神的指引下,页面显示完成后客户端仍与服务器端保持着联系。


Facebook聊天功能为例,它会持续与服务器保持联系来及时更新你那些亮亮灰灰的好友状态。为了更新这些头像亮着的好友状态,在浏览器中执行的
JavaScript代码会给服务器发送异步请求。这个异步请求发送给特定的地址,它是一个按照程式构造的获取或发送请求。还是在Facebook这个例
子中,客户端发送给
在线的状态信息。

提起这个模式,就必须要讲讲”AJAX”– “异步JavaScript 和
XML”,虽然服务器为什么用XML格式来进行响应也没有个一清二白的原因。再举个例子吧,对于异步请求,Facebook会返回一些JavaScript的代码片段。

除了其他,fiddler这个工具能够让你看到浏览器发送的异步请求。事实上,你不仅可以被动的做为这些请求的看客,还能主动出击修改和重新发送它们。AJAX请求这么容易被蒙,可着实让那些计分的在线游戏开发者们郁闷的了。(当然,可别那样骗人家~)

Facebook聊天功能提供了关于AJAX一个有意思的问题案例:把数据从服务器端推送到客户端。因为HTTP是一个请求-响应协议,所以聊天服务器不能把新消息发给客户。取而代之的是客户端不得不隔几秒就轮询下服务器端看自己有没有新消息。

这些情况发生时长轮询是个减轻服务器负载挺有趣的技术。如果当被轮询时服务器没有新消息,它就不理这个客户端。而当尚未超时的情况下收到了该客户的新消息,服务器就会找到未完成的请求,把新消息做为响应返回给客户端。

在页面内嵌或动态生成指向别的网站的 IFRAME,然后这
2 个网页间可以通过改变对方的 anchor hash fragment
来传输消息。改变一个网页的 anchor hash fragment
并不会使浏览器重新装载网页,所以一个网页的状态得以保持,而网页本身则可以通过一个计时器(timer)来察觉自己
anchor hash 的变化,从而相应改变自己的状态。

  1. :





    请求:

    回复:


两个网页基本相同,第一个网页内嵌一个
IFRAME,在点击“发送”按钮后,会将文本框里的内容通过 hash fragment 传给
IFRAME。点击 IFRAME 里的“发送”按钮后,它会将文本框里的内容通过 hash
fragment 传给父窗口。因为是只改动了 hash fragment,浏览器不会重新 load
网页内容,这里使用了一个计时器来检测 URL
变化,如果变化了,就更新其中一个 div 的内容 。

Case IV. 用户本地转储方式 (local)

IE 本身依附于 Windows 平台的特性为我们提供了一种基于
iframe,利用内存来“绕行”的方案,即两个 Window 之间可以在客户端通过
Windows 剪贴板的方式进行数据传输,只需要在接受数据的一方设置 Interval
进行轮询,获得结果后清除 Interval 即可。FF
的平台独立性决定了它不支持剪贴板这种方式,而以往版本的 FF
中存在的插件漏洞又被 fixed 了,所以 FF
无法通过内存来完成暗渡陈仓。而由于文件操作 FF 也没有提供支持(无法通过
Cookie 跨域完成数据传递),致使这种技巧性的方式只能在 IE 中使用。

Case V: (其实还是在服务端 A 用 iframe
解决了与服务器 B 通信的问题)

要解决的问题:发生在用户提交网页 URL(还包括 Tag, Notes 等)给 Bookmark
服务器时。

关于 URL 的提交至少可以有三种方式:  

  1. 登陆 Bookmark 服务器的提交页面,将要收藏的 URL
    通过该页面提交给服务器。
  2. 安装浏览器插件,通过插件将 URL 提交给服务器。
  3. 从 Bookmark 服务器动态加载 javascript
    小工具到当前页面,通过它来完成提交工作。

第一种方式开发起来最简单,但对用户来讲比较麻烦,每次都需要先登陆
Bookmark
服务器才能完成提交;第二种方式我并不熟悉插件开发,而且用户也不喜欢太多的插件堆满自己的浏览器;第三种方式开发难度小,又避免了每次登陆服务器的麻烦,所以最终采用它。第三种方式中动态加载的
javascript 小工具除了需要生成 UI 供用户填写信息(URL,tag,notes
等),当用户点击提交的时候,还要完成与服务器通信的功能。

跨域访问,简单来说就是 A 网站的 javascript 代码试图访问 B
网站,包括提交内容和获取内容。由于安全原因,跨域访问是被各大浏览器所默认禁止的。写过跨域访问
ajax 的朋友相信都遇到过被告知“没有权限”的情况。通过 XMLHttp 来发送数据给
Bookmark 服务器的尝试失败了。于是,看到网上的一些资料,我又开始尝试用
javascript 小工具在用户网页动态创建一个隐藏的 iframe, iframe 的 src
指向服务器的一个 servlet ,试图通过调用 iframe 中提供的 javascript
来完成与服务器的通信。但不幸的是,用户网页中的 javascript 代码访问
iframe 也被浏览器归为跨域访问(特指 iframe 的 src
指向其它网站的情形),尝试再次失败。

最终,在一篇文章中看到,与 iframe 不同,如果 A 网站从 B 网站加载
javascript , A 网站可以自由的访问该 javascript
的内容,并不会被浏览器认为是跨域访问。模仿刚才 iframe
的思路,当用户点击提交时,可以动态创建一个 javascript 对象,该对象的 src
指向 Bookmark 服务器的一个
servlet,注意:URL、Tag、Notes、User、Password 等信息被作为 src URL
参数传给服务器。请看下面的代码:

var url = "http://localhost:8080/Deeryard/BookmarkServlet?" +
          "url=" + url_source + "&" + "title=" + title + "&" +
          "tag=" + tag + "&" + "notes=" + notes + "&" +
          "user=" + user + "&" + "password=" + password; 

url = encodeURI(url); 
 
//Submit to server with a trick 
var js_obj = document.createElement( "script" ); 
js_obj.type = "text/javascript" ; 
js_obj.setAttribute( "src" , url); 
 
//Get response from server by appending it to document 
document.body.appendChild(js_obj);

上面例子中, js_obj.setArrribute() 将信息作为 src 的 URL 参数提交给了
Bookmark servlet
。那么用户又如何取得服务器的响应信息呢?答案就是最末一行代码, servlet
的输出必须是 javascript 代码,它可以调用用户网页上的其他 javascript
函数,以及操作 dom 对象。下面的 servlet 代码生成了一个 javascript
函数调用:

out.write("onServerResponse(INADEQUATE_INFORMATION);"); 

document.body.appendChild(js_obj) 执行后 onServerResponse(
INADEQUATE_INFORMATION)
就会得到执行,使客户网页响应服务器结果。这样一个完整的通信过程就完成了。

CaseVI:Tomcat + PHP + HTML(含JS)(on Server
A)

服务器 A 上已经装好了 Tomcat, 我们写一个 test.html(含JS),再写一个 PHP
文件(由其来完成跨域通信要求)。

更多,请参考:

相关阅读