litjson用法协议的用处和用法是什么?

程序写累了,就来玩玩酷跑小游戏吧,嘿嘿。
雨松MOMO送你一首歌曲,嘿嘿。
Unity3D研究院之C#使用Socket与HTTP连接服务器传输数据包(四十三)
Unity3D研究院之C#使用Socket与HTTP连接服务器传输数据包(四十三)
围观28058次
编辑日期: 字体:
最近比较忙,有段时间没写博客拉。最近项目中需要使用HTTP与Socket,雨松MOMO把自己这段时间学习的资料整理一下。有关Socket与HTTP的基础知识MOMO就不赘述拉,不懂得朋友自己谷歌吧。我们项目的需求是在登录的时候使用HTTP请求,游戏中其它的请求都用Socket请求,比如人物移动同步坐标,同步关卡等等。
Socket不要写在脚本上,如果写在脚本上游戏场景一旦切换,那么这条脚本会被释放掉,Socket会断开连接。场景切换完毕后需要重新在与服务器建立Socket连接,这样会很麻烦。所以我们需要把Socket写在一个单例的类中,不用继承MonoBehaviour。这个例子我模拟一下,主角在游戏中移动,时时向服务端发送当前坐标,当服务器返回同步坐标时角色开始同步服务端新角色坐标。
Socket在发送消息的时候采用的是字节数组,也就是说无论你的数据是 int float short object 都会将这些数据类型先转换成byte[] , 目前在处理发送的地方我使用的是数据包,也就是把(角色坐标)结构体object转换成byte[]发送, 这就牵扯一个问题, 如何把结构体转成字节数组, 如何把字节数组回转成结构体。请大家接续阅读,答案就在后面,哇咔咔。
直接上代码
JFSocket.cs 该单例类不要绑定在任何对象上
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
using UnityEngine;using System.Collections;using System;using System.Threading;using System.Text;using System.Net;using System.Net.Sockets;using System.Collections.Generic;using System.IO;using System.Runtime.InteropServices;using System.Runtime.Serialization;using System.Runtime.Serialization.Formatters.Binary;&public class JFSocket{&&&&&//Socket客户端对象 private Socket clientSocket; //JFPackage.WorldPackage是我封装的结构体, //在与服务器交互的时候会传递这个结构体 //当客户端接到到服务器返回的数据包时,我把结构体add存在链表中。 public List&JFPackage.WorldPackage& worldpackage; //单例模式 private static JFSocket instance;&&&&public static JFSocket GetInstance() {
if (instance == null)
instance = new JFSocket();&&&&&&&&}&&&&&&&&return instance;&&&& }&& & //单例的构造函数 JFSocket() {
//创建Socket对象, 这里我的连接类型是TCP
clientSocket = new Socket (AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
//服务器IP地址
IPAddress ipAddress = IPAddress.Parse ("192.168.1.100"); &&&&//服务器端口 &&&&IPEndPoint ipEndpoint = new IPEndPoint (ipAddress, 10060); &&&&//这是一个异步的建立连接,当连接建立成功时调用connectCallback方法 &&&&IAsyncResult result = clientSocket.BeginConnect (ipEndpoint,new AsyncCallback (connectCallback),clientSocket);
//这里做一个超时的监测,当连接超过5秒还没成功表示超时
bool success = result.AsyncWaitHandle.WaitOne( 5000, true );
if ( !success ) &&
Debug.Log("connect Time Out");
//与socket建立连接成功,开启线程接受服务端数据。
worldpackage = new List&JFPackage.WorldPackage&();
Thread thread = new Thread(new ThreadStart(ReceiveSorket));&&&&&&
thread.IsBackground = true;&&&&&&&& thread.Start();
} }& private void connectCallback(IAsyncResult asyncConnect) {
Debug.Log("connectSuccess"); }& private void ReceiveSorket()&&&&{
//在这个线程中接受服务器返回的数据 &&&&while (true)&&&&&&&&{ &
if(!clientSocket.Connected)
//与服务器断开连接跳出循环
Debug.Log("Failed to clientSocket server.");
clientSocket.Close();
}&&&&&&&&&&&&try&&&&&&&&&&&&{&&&&&&&&&&&& //接受数据保存至bytes当中&&&&&&&&&&&&&&&&byte[] bytes = new byte[4096];&&&&&&&&&&&&&&&&//Receive方法中会一直等待服务端回发消息&&&&&&&&&&&&&&&&//如果没有回发会一直在这里等着。
int i = clientSocket.Receive(bytes);
if(i &= 0)
clientSocket.Close();
//这里条件可根据你的情况来判断。
//因为我目前的项目先要监测包头长度,
//我的包头长度是2,所以我这里有一个判断
if(bytes.Length & 2)
SplitPackage(bytes,0);
Debug.Log("length is not&&&&&2");
}&&&&&&&&&&&&& }&&&&&&&&&&&& catch (Exception e)&&&&&&&&&&&& {
Debug.Log("Failed to clientSocket error." + e);&&&&&&&&&&&&&&&&clientSocket.Close();
break;&&&&&&&&&&&& }&&&&&&&&}&&&&} & private void SplitPackage(byte[] bytes , int index) {
//在这里进行拆包,因为一次返回的数据包的数量是不定的
//所以需要给数据包进行查分。
while(true)
//包头是2个字节
byte[] head = new byte[2];
int headLengthIndex = index + 2;
//把数据包的前两个字节拷贝出来
Array.Copy(bytes,index,head,0,2);
//计算包头的长度
short length = BitConverter.ToInt16(head,0);
//当包头的长度大于0 那么需要依次把相同长度的byte数组拷贝出来
if(length & 0)
byte[] data = new byte[length];
//拷贝出这个包的全部字节数
Array.Copy(bytes,headLengthIndex,data,0,length);
//把数据包中的字节数组强制转换成数据包的结构体
//BytesToStruct()方法就是用来转换的
//这里需要和你们的服务端程序商量,
JFPackage.WorldPackage wp = new JFPackage.WorldPackage();
wp = (JFPackage.WorldPackage)BytesToStruct(data,wp.GetType());
//把每个包的结构体对象添加至链表中。
worldpackage.Add(wp); &&&&&&&&&&&&//将索引指向下一个包的包头
index&&=&&headLengthIndex + length;&
//如果包头为0表示没有包了,那么跳出循环
} } &&&&&//向服务端发送一条字符串&&&&//一般不会发送字符串 应该是发送数据包 public void SendMessage(string str) {
byte[] msg = Encoding.UTF8.GetBytes(str);&
if(!clientSocket.Connected)
clientSocket.Close();
}&&&& try&&&& {&&&&&&&& //int i = clientSocket.Send(msg);
IAsyncResult asyncSend = clientSocket.BeginSend (msg,0,msg.Length,SocketFlags.None,new AsyncCallback (sendCallback),clientSocket);
bool success = asyncSend.AsyncWaitHandle.WaitOne( 5000, true );
if ( !success ) &&
clientSocket.Close();
Debug.Log("Failed to SendMessage server.");
}&&&& }&&&& catch&&&& {&&&&&&&&
Debug.Log("send message error" );&&&& } }&&&&&//向服务端发送数据包,也就是一个结构体对象 public void SendMessage(object obj) {&
if(!clientSocket.Connected)
clientSocket.Close();
}&&&& try&&&& {&&&&
//先得到数据包的长度
short size = (short)Marshal.SizeOf(obj);
//把数据包的长度写入byte数组中
byte [] head = BitConverter.GetBytes(size);
//把结构体对象转换成数据包,也就是字节数组
byte[] data = StructToBytes(obj);&
//此时就有了两个字节数组,一个是标记数据包的长度字节数组, 一个是数据包字节数组,
//同时把这两个字节数组合并成一个字节数组&
byte[] newByte = new byte[head.Length + data.Length];
Array.Copy(head,0,newByte,0,head.Length);
Array.Copy(data,0,newByte,head.Length, data.Length);&
//计算出新的字节数组的长度
int length = Marshal.SizeOf(size) + Marshal.SizeOf(obj);&
//向服务端异步发送这个字节数组
IAsyncResult asyncSend = clientSocket.BeginSend (newByte,0,length,SocketFlags.None,new AsyncCallback (sendCallback),clientSocket);
&&&&//监测超时
bool success = asyncSend.AsyncWaitHandle.WaitOne( 5000, true );
if ( !success ) &&
clientSocket.Close();
Debug.Log("Time Out !");
}&&&& catch (Exception e)&&&& {&&&&&&&&
Debug.Log("send message error: " + e );&&&& } }& //结构体转字节数组 public byte[] StructToBytes(object structObj)&&&&{&
int size = Marshal.SizeOf(structObj);
IntPtr buffer =&&Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(structObj,buffer,false);
byte[]&&bytes&&=&& new byte[size];
Marshal.Copy(buffer, bytes,0,size);
return&& bytes;&&
Marshal.FreeHGlobal(buffer);&&
}&&&&} //字节数组转结构体 public object BytesToStruct(byte[] bytes,&& Type&& strcutType)&&&&{
int size = Marshal.SizeOf(strcutType);
IntPtr buffer = Marshal.AllocHGlobal(size);&&
&& Marshal.Copy(bytes,0,buffer,size);&&
return&&Marshal.PtrToStructure(buffer,&& strcutType);&&
}&&&&&&&&finally&&
Marshal.FreeHGlobal(buffer);&&
}&& &&&&&}& private void sendCallback (IAsyncResult asyncSend) {& }& //关闭Socket public void Closed() {&
if(clientSocket != null && clientSocket.Connected)
clientSocket.Shutdown(SocketShutdown.Both);
clientSocket.Close();
clientSocket = null; }&}
为了与服务端达成默契,判断数据包是否完成。我们需要在数据包中定义包头 ,包头一般是这个数据包的长度,也就是结构体对象的长度。正如代码中我们把两个数据类型 short 和 object 合并成一个新的字节数组。
然后是数据包结构体的定义,需要注意如果你在做IOS和Android的话数据包中不要包含数组,不然在结构体转换byte数组的时候会出错。
Marshal.StructureToPtr () error : Attempting to JIT compile method
JFPackage.cs
1234567891011121314151617181920212223242526272829303132333435363738
using UnityEngine;using System.Collections;using System.Runtime.InteropServices;&public class JFPackage{ //结构体序列化 [System.Serializable] //4字节对齐 iphone 和 android上可以1字节对齐 [StructLayout(LayoutKind.Sequential, Pack = 4)] public struct WorldPackage {
public byte mEquipID;
public byte mAnimationID;
public byte mHP;
public short mPosx;
public short mPosy;
public short mPosz;
public short mRosx;
public short mRosy;
public short mRosz;&
public WorldPackage(short posx,short posy,short posz, short rosx, short rosy, short rosz,byte equipID,byte animationID,byte hp)
mPosx = posx;
mPosy = posy;
mPosz = posz;
mRosx = rosx;
mRosy = rosy;
mRosz = rosz;
mEquipID = equipID;
mAnimationID = animationID;
在脚本中执行发送数据包的动作,在Start方法中得到Socket对象。
public JFSocket mJFsorket;& void Start () {
mJFsorket = JFSocket.GetInstance(); }
让角色发生移动的时候,调用该方法向服务端发送数据。
12345678910111213141516171819
void SendPlayerWorldMessage() {&&&&&&&&&&&&&&&& //组成新的结构体对象,包括主角坐标旋转等。
Vector3 PlayerTransform = transform.localPosition;
Vector3 PlayerRotation = transform.localRotation.eulerAngles;&&&&&&&&&&&&&&&& //用short的话是2字节,为了节省包的长度。这里乘以100 避免使用float 4字节。当服务器接受到的时候小数点向前移动两位就是真实的float数据
short px =&&(short)(PlayerTransform.x*100);
short py =&&(short)(PlayerTransform.y*100);
short pz =&&(short)(PlayerTransform.z*100);
short rx =&&(short)(PlayerRotation.x*100);
short ry =&&(short)(PlayerRotation.y*100);
short rz =&&(short)(PlayerRotation.z*100);
byte equipID = 1;
byte animationID =9;
byte hp = 2;
JFPackage.WorldPackage wordPackage = new JFPackage.WorldPackage(px,py,pz,rx,ry,rz,equipID,animationID,hp);&&&&&&&&&&&&&&&& //通过Socket发送结构体对象
mJFsorket.SendMessage(wordPackage); }
接着就是客户端同步服务器的数据,目前是测试阶段所以写的比较简陋,不过原理都是一样的。哇咔咔!!
12345678910111213141516171819202122232425262728293031
//上次同步时间&&&&&& private float mSynchronous;& void Update () {&
mSynchronous +=Time.deltaTime;
//在Update中每0.5s的时候同步一次
if(mSynchronous & 0.5f)
int count = mJFsorket.worldpackage.Count;
//当接受到的数据包长度大于0 开始同步
if(count & 0)
{&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&//遍历数据包中 每个点的坐标
foreach(JFPackage.WorldPackage wp in mJFsorket.worldpackage)
float x = (float)(wp.mPosx / 100.0f);
float y = (float)(wp.mPosy /100.0f);
float z = (float)(wp.mPosz /100.0f);&
Debug.Log("x = " + x + " y = " + y+" z = " + z);
&&&&&&//同步主角的新坐标
mPlayer.transform.position = new Vector3 (x,y,z);
}&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&//清空数据包链表
mJFsorket.worldpackage.Clear();
mSynchronous = 0;
主角移动的同时,通过Socket时时同步坐标喔。。有没有感觉这个牛头人非常帅气 哈哈哈。
对于Socket的使用,我相信没有比MSDN更加详细的了。 有关Socket 同步请求异步请求的地方可以参照MSDN
链接地址给出来了,好好学习吧,嘿嘿。
上述代码中我使用的是Thread() 没有使用协同任务StartCoroutine() ,原因是协同任务必需要继承MonoBehaviour,并且该脚本要绑定在游戏对象身上。问题绑定在游戏对象身上切换场景的时候这个脚本必然会释放,那么Socket肯定会断开连接,所以我需要用Thread,并且协同任务它并不是严格意义上的多线程。
HTTP请求在Unity我相信用的会更少一些,因为HTTP会比SOCKET慢很多,因为它每次请求完都会断开。废话不说了, 我用HTTP请求制作用户的登录。用HTTP请求直接使用Unity自带的www类就可以,因为HTTP请求只有登录才会有, 所以我就在脚本中来完成, 使用 www 类 和 协同任务StartCoroutine()。
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
using UnityEngine;using System.Collections;using System.Collections.Generic;public class LoginGlobe : MonoBehaviour {& void Start () {
StartCoroutine(GET("/"));& }& void Update () {& }& void OnGUI() {& }& //登录 public void LoginPressed() {
//登录请求 POST 把参数写在字典用 通过www类来请求
Dictionary&string,string& dic = new Dictionary&string, string& ();
dic.Add("action","0");
dic.Add("usrname","xys");
dic.Add("psw","123456");&
StartCoroutine(POST("http://192.168.1.12/login.php",dic));& } //注册 public void SingInPressed() {
//注册请求 POST
Dictionary&string,string& dic = new Dictionary&string, string& ();
dic.Add("action","1");
dic.Add("usrname","xys");
dic.Add("psw","123456");&
StartCoroutine(POST("http://192.168.1.12/login.php",dic)); }&&&&&//POST请求
IEnumerator POST(string url, Dictionary&string,string& post)&&&&{&&&& WWWForm form = new WWWForm();&&&& foreach(KeyValuePair&string,string& post_arg in post)&&&& {&&&&&&
form.AddField(post_arg.Key, post_arg.Value);&&&& }&
WWW www = new WWW(url, form);
yield return www;&
if (www.error != null)&&&&&&&&{&&&&&&&& //POST请求失败
Debug.Log("error is :"+ www.error);&&&&&&&&&} else
//POST请求成功&&&&&&&&&&&& Debug.Log("request ok : " + www.text);&&&&&&&&}&&&&}& //GET请求 IEnumerator GET(string url)&&&&{&&&&& WWW www = new WWW (url);&&&& yield return www;&
if (www.error != null)&&&&&&&&{&&&&&&&& //GET请求失败
Debug.Log("error is :"+ www.error);&&&&&&&&&} else
//GET请求成功&&&&&&&&&&&& Debug.Log("request ok : " + www.text);&&&&&&&&}&&&&}&}
如果想通过HTTP传递二进制流的话 可以使用 下面的方法。
&& WWWForm wwwForm = new WWWForm();&&&&&&&&&byte[] byteStream = System.Text.Encoding.Default.GetBytes(stream);&&&&&&&&&wwwForm.AddBinaryData("post", byteStream);&&&&&&&&&www = new WWW(Address, wwwForm);
目前Socket数据包还是没有进行加密算法,后期我会补上。欢迎讨论,互相学习互相进度 加油,蛤蛤。
下载地址我不贴了,因为没有服务端的东西 运行也看不到效果。 希望大家学习愉快, 我们下次再见!
本文固定链接:
转载请注明:
MOMO与MO嫂提醒您:亲,如果您觉得本文不错,快快将这篇文章分享出去吧 。另外请点击网站顶部彩色广告或者捐赠支持本站发展,谢谢!
作者:雨松MOMO
专注移动互联网,Unity3D游戏开发
如果您愿意花10块钱请我喝一杯咖啡的话,请用手机扫描二维码即可通过支付宝直接向我捐款哦。
,,,,,,
您可能还会对这些文章感兴趣!memcached_百度百科
关闭特色百科用户权威合作手机百科
收藏 查看&memcached
Memcached 是一个高性能的分布式对象缓存系统,用于动态Web应用以减轻负载。它通过在中缓存数据和对象来减少读取的次数,从而提高动态、数据库驱动网站的速度。Memcached基于一个存储键/值对的。其(daemon )是用写的,但是可以用任何语言来编写,并通过memcached协议与守护进程通信。所&&&&属缓存系统通信手段memcached协议
memcached是一套分布式的快取系统,当初是Danga Interactive为了LiveJournal所发展的,但被许多软件(如MediaWiki)所使用。这是一套开放源代码软件,以BSD license授权协议发布。[1]
memcached缺乏认证以及安全管制,这代表应该将memcached服务器放置在防火墙后。[1]
memcached的API使用32位元的(CRC-32)计算键值后,将资料分散在不同的机器上。当表格满了以后,接下来新增的资料会以LRU机制替换掉。由于memcached通常只是当作快取系统使用,所以使用memcached的应用程式在写回较慢的系统时(像是后端的数据库)需要额外的程式码更新memcached内的资料[1]
memcached 是以LiveJournal 旗下Danga Interactive 公司的Brad Fitzpatric 为首开发的一款软件。已成为mixi、hatena、Facebook、Vox、LiveJournal等众多服务中提高Web应用扩展性的重要因素。许多Web应用都将数据保存到中,应用服务器从中读取数据并在中显示。但随着数据量的增大、访问的集中,就会出现RDBMS的负担加重、响应恶化、网站显示延迟等重大影响。
这时就该memcached大显身手了。memcached是高性能的分布式。一般的使用目的是,通过查询结果,减少数据库访问次数,以提高动态Web应用的速度、提高可扩展性。
Memcached 的守护进程(daemon )是用C写的,但是可以用任何语言来编写,并通过memcached协议与守护进程通信。但是它并不提供(例如,复制其hashmap条目);当某个服务器S停止运行或崩溃了,所有存放在S上的键/值对都将丢失。
Memcached由Danga Interactive开发,其最新版本发布于2010年,作者为Anatoly Vorobey和Brad Fitzpatrick。用于提升LiveJournal . com访问速度的。LJ每秒访问量几千次,用户700万。Memcached将负载大幅度降低,更好的分配资源,更快速访问。memcached作为高速运行的分布式,具有以下的特点。
· 协议简单
· 基于的事件处理
· 内置存储方式
· memcached不互相通信的分布式memcached的服务器通信并不使用复杂的XML等格式,而使用简单的基于行的协议。
因此,通过telnet也能在memcached上保存数据、取得数据。下面是例子。
$ telnet localhost 11211
Trying 127.0.0.1
Connected to localhost.localdomain (127.0.0.1).
Escape character is '^]'.
set foo 0 0 3 (保存命令)
bar (数据)
STORED (结果)
get foo (取得命令)
VALUE foo 0 3 (数据)
bar (数据)libevent是个,它将Linux的epoll、BSD类操作系统的kqueue等事件处理功能封装成统一的接口。即使对服务器的连接数增加,也能发挥O(1)的性能。memcached使用这个libevent库,因此能在Linux、BSD、Solaris等操作系统上发挥其高性能。关于事件处理这里就不再详细介绍,可以参考Dan Kegel的The C10K Problem。为了提高性能,memcached中保存的数据都存储在memcached内置的中。由于数据仅存在于中,因此重启memcached、重启操作系统会导致全部数据消失。另外,内容容量达到指定值之后,就基于LRU(Least Recently Used)算法自动删除不使用的。memcached本身是为缓存而设计的服务器,因此并没有过多考虑数据的永久性问题。memcached尽管是“分布式”,但服务器端并没有分布式功能。各个memcached不会互相通信以共享信息。那么,怎样进行分布式呢?这完全取决于的实现。本文也将介绍memcached的分布式。一个用PHP编写的可视化的MemCached管理系统
MemAdmin是一款可视化的Memcached管理与监控工具,使用PHP开发,体积小,操作简单。
主要功能:
服务器参数监控:STATS、SETTINGS、ITEMS、SLABS、SIZES实时刷新
服务器性能监控:GET、DELETE、INCR、DECR、CAS等常用操作命中率实时监控
支持数据遍历,方便对内容进行监视
支持条件查询,筛选出满足条件的KEY或VALUE
、等序列化字符反序列显示
兼容memcache协议的其他服务,如Tokyo Tyrant (遍历功能除外)
支持服务器,多切换方便简洁许多语言都实现了连接memcached的,其中以Perl、PHP为主。仅仅memcached网站上列出
的语言就有
如何使用memcached-Server端
# ./memcached -d -m 2048 -l 10.0.0.40 -p 11211
这将会启动一个占用2G的进程,并打开11211端口用于接收请求。由于32位系统只能处理4G的寻址,所以在大于4G内存使用PAE的32位服务器上可以运行2-3个进程,并在不同端口进行监听。
如何使用memcached-Client端
在应用端包含一个用于描述Client的Class后,就可以直接使用,非常简单。
PHP Example:
$options[&servers&] = array(&192.168.1.41:11211&, &192.168.1.42:11212&);
$options[&debug&] =
$memc = new MemCachedClient($options);
$myarr = array(&one&,&two&, 3);
$memc-&set(&key_one&, $myarr);
$val = $memc-&get(&key_one&);
print $val[0].&\n&; // prints 'one‘
print $val[1].&\n&; // prints 'two‘
print $val[2].&\n&; // prints 3
CentOS 下的安装
安装  yum -y install memcached
设置为开机启动  chkconfig --level 2345 memcached on
启动和停止  /etc/init.d/memcached start|stop
补充:如果安装缺少其他支持,可以:
yum groupinstall &Development Tools&日,Memcached 1.4.13 发布。[2]
日,Memcached 1.4.14 发布。[3]
日,Memcached 1.4.15 发布。[4]
新手上路我有疑问投诉建议参考资料 查看您所在的位置: &
JSONP跨域原理和jQuery.getJSON用法
JSONP跨域原理和jQuery.getJSON用法
JSONP是一个非官方的协议,它允许在服务器端集成Script tags返回至客户端,通过javascript callback的形式实现跨域访问(这仅仅是JSONP简单的实现形式)。本文主要介绍JSONP跨域原理,一起来看。
JSONP是一个非官方的协议,它允许在服务器端集成Script tags返回至客户端,通过javascript callback的形式实现跨域访问(这仅仅是JSONP简单的实现形式)。JSON系统开发方法是一种典型的面向数据结构的分析和设计方法,以活动为中心,一连串的活动的顺序组合成一个完整的工作进程。
之所以会有跨域这个问题的产生根本原因是浏览器的同源策略限制,理解同源策略的限制同源策略是指阻止代码获得或者更改从另一个域名下获得的文件或者信息。也就是说我们的请求地址必须和当前网站的地指相同。同源策略通过隔离来实现对资源的保护。这个策略的历史非常悠久从Netscape Navigator 2.0时代就开始了。
解决这个限制的一个相对简单的办法就是在服务器端发送请求,服务器充当一个到达第三方资源的代理中继。虽然是用广泛但是这个方法却不够灵活。
另一个办法就是使用框架(frames),将第三方站点的资源包含进来,但是包含进来的资源同样要受到同源策略的限制。
有一个很巧妙的办法就是在页面中使用动态代码元素,代码的源指向服务地址并在自己的代码中加载数据。当这些代码加载执行的时候,同源策略就不会起到限制。但是如果代码试图下载文件的时候执行还是会失败,幸运的是,我们可以使用JSON(JavaScript Object Notation)来改进这个应用。
JSON和JSONP
与XML相比,JSON是一个轻量级的数据交换格式。JSON对于JavaScript开发人员充满魅力的原因在于JSON本身就是Javascript中的对象。
例如一个ticker对象
var ticker = {symbol:'IBM',price:100}
而JSON串就是 {symbol:'IBM',price:100}
这样我们就可以在函数的参数中传递JSON数据。我们很容易掌握在函数中使用动态的JSON参数数据,但是我们的目的并不是这个。
通过使我们的函数能够加载动态的JSON数据,我们就能够处理动态的数据,这项技术叫做 Dynamic Javascript Insertion。
我们看下面的例子:
index.html中
&script&type=&text/javascript&& &function&showPrice(data){ &alert(&Symbol:&&+&data.symbol&+&&,&Price:&&+&data.price); &} &var&url&=&&ticker.js&;&&var&script&=&document.createElement('script'); &script.setAttribute('src',&url); &&document.getElementsByTagName('head')[0].appendChild(script); &&/script& &ticker.js中 &var&data&=&{symbol:'IBM',&price:100}; &showPrice(data);&
上面的代码通过动态加入Javascript代码,来执行函数加载数据。
正如之前提到过的,同源策略对于动态插入的代码不适用。也就是你可以从不同的域中加载代码,来执行在他们代码中的JSON数据。
这就是JSONP(JSON with Padding)。注意,使用这种方法时,你必须在页面中定义回调函数,就像上例中的showPrice一样。
我们通常所说的JSONP服务(远程JSON服务),实际上就是一种扩展的支持在用户定义函数中包含返回数据的能力。这种方法依赖于必须接受一个回调函数的名字作为参数。
然后执行这个函数,处理JSON数据,并显示在客户页面上。
JQuery的JSONP支持
从JQery 1.2以后,就开始支持JSONP的调用。在另外的一个域名中指定好回调函数名称,你就可以用下面的形式来就加载JSON数据。
url?callback=?
jQuery.getJSON(url&+&&&callbak=?&,&function(data) &{ &alert(&Symbol:&&+&data.symbol&+&&,&Price:&&+&data.price); &});&
jquery会在window对象中加载一个全局的函数,当代码插入时函数执行,执行完毕后就会被移除。同时jquery还对非跨域的请求进行了优化,如果这个请求是在同一个域名下那么他就会像正常的Ajax请求一样工作。
上例中我们在动态插入到页面的代码中使用了静态的json数据,虽然完成了依次JSONP返回,但仍不是JSONP服务,因为不支持在URL中定义回调函数名称。下面是一个将其变成JSONP服务的一个方法
服务器端使用PHP。
首先我们来定义接口的规范,就像这样:/jsonp/ticker?symbol=IBM&callback=showPrice
symbol是请求条件,callback是回调函数名称。
在页面文件中,我们使用JQuery的支持:
&var&url&=&&/api/suggest.php?symbol=IBM&callback=?&; &jQuery.getJSON(url,&function(data){ &alert(&Symbol:&&+&data.symbol&+&&,&Price:&&+&data.price); &});&
在suggest.php中
$jsondata&=&&{symbol:'IBM',&price:120}&; &echo&$_GET['callback'].'('.$jsondata.')';&
再举个.NET webservice 的例子
$.getJSON( &&http://192.168.0.66/services/WebService1.asmx/ws?callback=?&, &{&name:&&ff&,&time:&&2pm&&}, &function(data)&{&alert(decodeURI(data.msg))&} &);&
[WebMethod] &public&void&ws(string&name,string&time)&{ &HttpRequest&Request&=&HttpContext.Current.R &string&callback&=&Request[&callback&]; &HttpResponse&Response&=&HttpContext.Current.R &Response.Write(callback&+&&({msg:'this&is&+name+&jsonp'})&); &Response.End(); &}&
现在,如果我们想制作一些mashup,或者将第三方的资源整合到一个页面中,我们就很容易想到JSONP的解决方法了。
JSONP是一个非常强大的构建mashp的方法,可是不是一个解决跨域访问问题的万能药。它也有一些缺点:
第一也是最重要的:JSONP不提供错误处理。如果动态插入的代码正常运行,你可以得到返回,但是如果失败了,那么什么都不会发生。你无法获得一个404的错误,也不能取消这个请求。
另外一个重要的缺点是如果使用了不信任的服务会造成很大的安全隐患。
【编辑推荐】
【责任编辑: TEL:(010)】
关于&&的更多文章
IE浏览器不支持很多CSS属性是出了名的,即便在支持的部分中,也
本次的专刊为大家提供了Oracle最新推出的Java SE 8详细的开发教程,从解读到探究Java 8最新
8月第二周,开发者们每月必看的编程语言排行榜如期而
7月的名字叫“流火”!本周出差工作的各位辛苦了,因
程序员的30岁,是个伤不起的现象。你不可能敲一辈子的
本书全面深入地介绍网络安全的配置与实现技术,包括系统管理、用户账户、病毒防御、灾难恢复、文件备份、安全策略、注册表等服务
51CTO旗下网站}

我要回帖

更多关于 json2.js用法 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信