因为对CORBA分析的需要,这里写一个简单的CORBA例子。从JDK1.2开始,JDK中集成了ORB的实现,本例子使用了JDK1.7,对于JDK1.2+应该都没有问题。这个例子实现一个简单的加减乘除的功能的计算器,客户端将参数和请求的方法名传送到服务端,服务端处理这个请求并将结果返回给客户端。

我们知道不同编程语言中的类型的表达,内存模型是不一样的,为此CORBA发明了一套中间描述语言IDL,不同语言平台的ORB实现负责将IDL中的类型映射到本地类型中。因此IDL是我们编写CORBA程序的出发点,首先,我们用IDL来描述我们的接口/对象:

 module com{
module bes{
module corba{
module test{
interface Calc{
void add(in long a,in long b,out long c);
void sub(in long a,in long b,out long c);
void multi(in long a,in long b,out long c);
void div(in long a,in long b,out long c);
};
};
};
};
};

当然接口Calc中的方法的返回值不一定为void,这里将返回值放到了out类型的参数c中,方法可以带有多个out类型的参数。然后我们用idlj工具(jdk自带)将Calc.idl转换为对应java的描述,并生成Stub和POA等类:

idlj给我们生成很多文件,首先我们来看一下UML图:

上面的图不涉及工具类CalcHelper和CalcHolder,这两个类的作用在后而阐述。

package com.bes.corba.test;

/**
* com/bes/corba/test/_CalcStub.java .
* 由IDL-to-Java 编译器 (可移植), 版本 "3.2"生成
* 从Hello.idl
* 2016年2月15日 星期一 下午09时08分34秒 CST
*/ public class _CalcStub extends org.omg.CORBA.portable.ObjectImpl implements com.bes.corba.test.Calc
{ public void add (int a, int b, org.omg.CORBA.IntHolder c)
{
org.omg.CORBA.portable.InputStream $in = null;
try {
org.omg.CORBA.portable.OutputStream $out = _request ("add", true);
$out.write_long (a);
$out.write_long (b);
$in = _invoke ($out);
c.value = $in.read_long ();
return;
} catch (org.omg.CORBA.portable.ApplicationException $ex) {
$in = $ex.getInputStream ();
String _id = $ex.getId ();
throw new org.omg.CORBA.MARSHAL (_id);
} catch (org.omg.CORBA.portable.RemarshalException $rm) {
add (a, b, c );
} finally {
_releaseReply ($in);
}
} // add public void sub (int a, int b, org.omg.CORBA.IntHolder c)
{
org.omg.CORBA.portable.InputStream $in = null;
try {
org.omg.CORBA.portable.OutputStream $out = _request ("sub", true);
$out.write_long (a);
$out.write_long (b);
$in = _invoke ($out);
c.value = $in.read_long ();
return;
} catch (org.omg.CORBA.portable.ApplicationException $ex) {
$in = $ex.getInputStream ();
String _id = $ex.getId ();
throw new org.omg.CORBA.MARSHAL (_id);
} catch (org.omg.CORBA.portable.RemarshalException $rm) {
sub (a, b, c );
} finally {
_releaseReply ($in);
}
} // sub public void multi (int a, int b, org.omg.CORBA.IntHolder c)
{
org.omg.CORBA.portable.InputStream $in = null;
try {
org.omg.CORBA.portable.OutputStream $out = _request ("multi", true);
$out.write_long (a);
$out.write_long (b);
$in = _invoke ($out);
c.value = $in.read_long ();
return;
} catch (org.omg.CORBA.portable.ApplicationException $ex) {
$in = $ex.getInputStream ();
String _id = $ex.getId ();
throw new org.omg.CORBA.MARSHAL (_id);
} catch (org.omg.CORBA.portable.RemarshalException $rm) {
multi (a, b, c );
} finally {
_releaseReply ($in);
}
} // multi public void div (int a, int b, org.omg.CORBA.IntHolder c)
{
org.omg.CORBA.portable.InputStream $in = null;
try {
org.omg.CORBA.portable.OutputStream $out = _request ("div", true);
$out.write_long (a);
$out.write_long (b);
$in = _invoke ($out);
c.value = $in.read_long ();
return;
} catch (org.omg.CORBA.portable.ApplicationException $ex) {
$in = $ex.getInputStream ();
String _id = $ex.getId ();
throw new org.omg.CORBA.MARSHAL (_id);
} catch (org.omg.CORBA.portable.RemarshalException $rm) {
div (a, b, c );
} finally {
_releaseReply ($in);
}
} // div // Type-specific CORBA::Object operations
private static String[] __ids = {
"IDL:com/bes/corba/test/Calc:1.0"}; public String[] _ids ()
{
return (String[])__ids.clone ();
} private void readObject (java.io.ObjectInputStream s) throws java.io.IOException
{
String str = s.readUTF ();
String[] args = null;
java.util.Properties props = null;
org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init (args, props);
try {
org.omg.CORBA.Object obj = orb.string_to_object (str);
org.omg.CORBA.portable.Delegate delegate = ((org.omg.CORBA.portable.ObjectImpl) obj)._get_delegate ();
_set_delegate (delegate);
} finally {
orb.destroy() ;
}
} private void writeObject (java.io.ObjectOutputStream s) throws java.io.IOException
{
String[] args = null;
java.util.Properties props = null;
org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init (args, props);
try {
String str = orb.object_to_string (this);
s.writeUTF (str);
} finally {
orb.destroy() ;
}
}
} // class _CalcStub

_CalcStub是存根类,有过远程调用编程经验的读者应该对这个词比较熟悉,它是远程对象在本地的一个代理(Proxy)。从业务划分角度来说,CORBA这些底层的东西不应该太多地污染到我们的应用,比如这个_CalcStub是我们不希望在业务代码中出现的,我们只需要看到我们需要的Calc。客户端从ORB中拿到的Calc接口实现其实是一个_CalcStub,客户程序对Calc接口中的方法进行调用时,_CalcStub将相应方法的调用转发到服务端,然后将服务器的响应返回给客户端,从而成功从欺骗客户端程序。

_CalcStub继承了ObjectImpl类,这使得_CalcStub能够关联到ORB环境中,从而完成远程调用。

idlj工具并没有直接在Calc.java中定义idlj方法的java语言描述,而是在CalcOperation.java中。Calc接口继承了IDLEntity,org.omg.CORBA.Object和CalcOperation三个接口:

IDLEntity是一个标记接口,表明Calc接口是一种IDL接口,这个与org.omg.CORBA.Object有点相类似,但实现了org.omg.CORBA.Object接口的对象不一定是IDL描述的,因此这里单独把IDLEntity拎出来。

org.omg.CORBA.Object接口定义了一些CORBA相关的方法,因为客户端所使用的是Calc,参数传递到ORB中语义上也是 Calc类型,当然我们不能将非IDL的对象传递到ORB中,ORB无法完成那样子的操作;Calc接口继承 org.omg.CORBA.Object(ORB层面使用的是org.omg.CORBA.Object),那就意味了通过编译器来保证类型安全(避免 强制转换,ClassCastException之类的异常)。

package com.bes.corba.test;

/**
* com/bes/corba/test/CalcOperations.java .
* 由IDL-to-Java 编译器 (可移植), 版本 "3.2"生成
* 从Hello.idl
* 2016年2月15日 星期一 下午09时08分34秒 CST
*/ public interface CalcOperations
{
void add (int a, int b, org.omg.CORBA.IntHolder c);
void sub (int a, int b, org.omg.CORBA.IntHolder c);
void multi (int a, int b, org.omg.CORBA.IntHolder c);
void div (int a, int b, org.omg.CORBA.IntHolder c);
} // interface CalcOperations

CalcOperation是相对应的IDL映射。

package com.bes.corba.test;

/**
* com/bes/corba/test/CalcPOA.java .
* 由IDL-to-Java 编译器 (可移植), 版本 "3.2"生成
* 从Hello.idl
* 2016年2月15日 星期一 下午09时08分34秒 CST
*/ public abstract class CalcPOA extends org.omg.PortableServer.Servant
implements com.bes.corba.test.CalcOperations, org.omg.CORBA.portable.InvokeHandler
{ // Constructors private static java.util.Hashtable _methods = new java.util.Hashtable ();
static
{
_methods.put ("add", new java.lang.Integer (0));
_methods.put ("sub", new java.lang.Integer (1));
_methods.put ("multi", new java.lang.Integer (2));
_methods.put ("div", new java.lang.Integer (3));
} public org.omg.CORBA.portable.OutputStream _invoke (String $method,
org.omg.CORBA.portable.InputStream in,
org.omg.CORBA.portable.ResponseHandler $rh)
{
org.omg.CORBA.portable.OutputStream out = null;
java.lang.Integer __method = (java.lang.Integer)_methods.get ($method);
if (__method == null)
throw new org.omg.CORBA.BAD_OPERATION (0, org.omg.CORBA.CompletionStatus.COMPLETED_MAYBE); switch (__method.intValue ())
{
case 0: // com/bes/corba/test/Calc/add
{
int a = in.read_long ();
int b = in.read_long ();
org.omg.CORBA.IntHolder c = new org.omg.CORBA.IntHolder ();
this.add (a, b, c);
out = $rh.createReply();
out.write_long (c.value);
break;
} case 1: // com/bes/corba/test/Calc/sub
{
int a = in.read_long ();
int b = in.read_long ();
org.omg.CORBA.IntHolder c = new org.omg.CORBA.IntHolder ();
this.sub (a, b, c);
out = $rh.createReply();
out.write_long (c.value);
break;
} case 2: // com/bes/corba/test/Calc/multi
{
int a = in.read_long ();
int b = in.read_long ();
org.omg.CORBA.IntHolder c = new org.omg.CORBA.IntHolder ();
this.multi (a, b, c);
out = $rh.createReply();
out.write_long (c.value);
break;
} case 3: // com/bes/corba/test/Calc/div
{
int a = in.read_long ();
int b = in.read_long ();
org.omg.CORBA.IntHolder c = new org.omg.CORBA.IntHolder ();
this.div (a, b, c);
out = $rh.createReply();
out.write_long (c.value);
break;
} default:
throw new org.omg.CORBA.BAD_OPERATION (0, org.omg.CORBA.CompletionStatus.COMPLETED_MAYBE);
} return out;
} // _invoke // Type-specific CORBA::Object operations
private static String[] __ids = {
"IDL:com/bes/corba/test/Calc:1.0"}; public String[] _all_interfaces (org.omg.PortableServer.POA poa, byte[] objectId)
{
return (String[])__ids.clone ();
} public Calc _this()
{
return CalcHelper.narrow(
super._this_object());
} public Calc _this(org.omg.CORBA.ORB orb)
{
return CalcHelper.narrow(
super._this_object(orb));
} } // class CalcPOA

CalcPOA:它工作在服务端,POA是Portable Object Adapter的缩写,这里Adapter(适配器)的语义是指适配相应的编程语言中的对象(比如在java中那意思就是指 Java对象的适配器),适配器的作用有三点:

1:接受客户端发过来的调用请求,反序列化(Unmarshalling)参数,方法名等,然后将请求分发给对应的Servant。POA和Servant之间的关系如下图。

2:将对象引用(Object Reference)和相应的Servant起来(可以看到Servant._object_id方法),比如我们在EJB中的有状态会话Bean。

3:负责Servant的生命周期管理(如创建,钝化,销毁等),这里又让我联想到了EJB的生命周期。到这里这里我们可以清楚Home接口存在的理由。

好吧,我们要实现的例子确实很简单,在这个例子中,读者只需要了解到第一点即可(CORBA水很深,很容易死里面去的)。idlj为我们生成的POA中,集Servant,CalcOperation和InvocationHandler于一身,有越殂代疱的嫌疑,当然这并不影响程序的正常执行,当然如果服务端比较关注2,3两点的话,自己实现POA还是很有必要的,但这已经超出了本文的范围。

同样地ORB的东西不应该玷污到我们服务端的业务逻辑 ,Servant和InvocationHandler将POA关联到ORB中去。注意Servant和InvocationHandler是两接口是分开的,这一点还不是太清楚(也许是为了特性的划分吧)。

package com.bes.corba.test;

/**
* com/bes/corba/test/CalcHelper.java .
* 由IDL-to-Java 编译器 (可移植), 版本 "3.2"生成
* 从Hello.idl
* 2016年2月15日 星期一 下午09时08分34秒 CST
*/ abstract public class CalcHelper
{
private static String _id = "IDL:com/bes/corba/test/Calc:1.0"; public static void insert (org.omg.CORBA.Any a, com.bes.corba.test.Calc that)
{
org.omg.CORBA.portable.OutputStream out = a.create_output_stream ();
a.type (type ());
write (out, that);
a.read_value (out.create_input_stream (), type ());
} public static com.bes.corba.test.Calc extract (org.omg.CORBA.Any a)
{
return read (a.create_input_stream ());
} private static org.omg.CORBA.TypeCode __typeCode = null;
synchronized public static org.omg.CORBA.TypeCode type ()
{
if (__typeCode == null)
{
__typeCode = org.omg.CORBA.ORB.init ().create_interface_tc (com.bes.corba.test.CalcHelper.id (), "Calc");
}
return __typeCode;
} public static String id ()
{
return _id;
} public static com.bes.corba.test.Calc read (org.omg.CORBA.portable.InputStream istream)
{
return narrow (istream.read_Object (_CalcStub.class));
} public static void write (org.omg.CORBA.portable.OutputStream ostream, com.bes.corba.test.Calc value)
{
ostream.write_Object ((org.omg.CORBA.Object) value);
} public static com.bes.corba.test.Calc narrow (org.omg.CORBA.Object obj)
{
if (obj == null)
return null;
else if (obj instanceof com.bes.corba.test.Calc)
return (com.bes.corba.test.Calc)obj;
else if (!obj._is_a (id ()))
throw new org.omg.CORBA.BAD_PARAM ();
else
{
org.omg.CORBA.portable.Delegate delegate = ((org.omg.CORBA.portable.ObjectImpl)obj)._get_delegate ();
com.bes.corba.test._CalcStub stub = new com.bes.corba.test._CalcStub ();
stub._set_delegate(delegate);
return stub;
}
} public static com.bes.corba.test.Calc unchecked_narrow (org.omg.CORBA.Object obj)
{
if (obj == null)
return null;
else if (obj instanceof com.bes.corba.test.Calc)
return (com.bes.corba.test.Calc)obj;
else
{
org.omg.CORBA.portable.Delegate delegate = ((org.omg.CORBA.portable.ObjectImpl)obj)._get_delegate ();
com.bes.corba.test._CalcStub stub = new com.bes.corba.test._CalcStub ();
stub._set_delegate(delegate);
return stub;
}
} }

CalcHelper:从名字上可以看出,它是一个工具类,它的职责在有:

1、通过提供narrow方法和RepositoryId等信息来实现类型安全。

2、与Any类型之间的进行转换。

3、从InputStream中读出Calc(对象引用),将Calc写出到OutputStream中。

我们的例子中将只用到第一点。

package com.bes.corba.test;

/**
* com/bes/corba/test/CalcHolder.java .
* 由IDL-to-Java 编译器 (可移植), 版本 "3.2"生成
* 从Hello.idl
* 2016年2月15日 星期一 下午09时08分34秒 CST
*/ public final class CalcHolder implements org.omg.CORBA.portable.Streamable
{
public com.bes.corba.test.Calc value = null; public CalcHolder ()
{
} public CalcHolder (com.bes.corba.test.Calc initialValue)
{
value = initialValue;
} public void _read (org.omg.CORBA.portable.InputStream i)
{
value = com.bes.corba.test.CalcHelper.read (i);
} public void _write (org.omg.CORBA.portable.OutputStream o)
{
com.bes.corba.test.CalcHelper.write (o, value);
} public org.omg.CORBA.TypeCode _type ()
{
return com.bes.corba.test.CalcHelper.type ();
} }

CalcHolder:这个类在Calc被作为out或者inout类型的参数传递时候被使用,如果我们在另一个IDL方法中使用Calc作为参数,那么生成的代码将会是这样子的:

void test(int a, int b, CalcHolder calc)

CalcHolder负责从InputStream或者OuputStream分别读出和写入Calc,从CalcHolder生成的代码中我们可以看出,CalcHolder的_read和_write方法将相应的操作委托给了CalcHelper。

哆嗦了这么多,是时候拿出我们的客户端和服务端了。

服务端代码:

CalculatorImpl是Servant的实现,它继承了CalcPOA类。

package com.bes.corba.impl;

import org.omg.CORBA.IntHolder;

import com.bes.corba.test.CalcPOA;

public class CalculatorImpl extends CalcPOA{

    @Override
public void add(int a, int b, IntHolder c) {
c.value=a+b;
} @Override
public void sub(int a, int b, IntHolder c) {
c.value=a-b;
} @Override
public void multi(int a, int b, IntHolder c) {
c.value=a*b;
} @Override
public void div(int a, int b, IntHolder c) {
c.value=a/b;
}
}

Server类:

package com.bes.corba.test;

import org.omg.CORBA.ORB;
import org.omg.CosNaming.NameComponent;
import org.omg.CosNaming.NamingContextExt;
import org.omg.CosNaming.NamingContextExtHelper;
import org.omg.PortableServer.POA;
import org.omg.PortableServer.POAHelper; import com.bes.corba.test.Calc;
import com.bes.corba.test.CalcHelper;
import com.bes.corba.impl.CalculatorImpl; public class Server {
public static void main(String[] args) throws Exception{
/*
* ORB 初始化。
*/
ORB orb=ORB.init(args,null); /*
* 获取根POA并初始化。
*/
POA rootPoa=POAHelper.narrow(orb.resolve_initial_references("RootPOA") );
rootPoa.the_POAManager().activate(); /*
* 构建一个CalculatorImpl。
*/
CalculatorImpl calculatorImpl=new CalculatorImpl(); /*
* 将Servant注册到RootPOA中,建立Servant到Object Reference的相互映射,
* 注意这里具体行为跟RootPOA的POA Policy有关。
*/
org.omg.CORBA.Object ref=rootPoa.servant_to_reference(calculatorImpl);
Calc iref=CalcHelper.narrow(ref); /*
* 获取命名服务。
*/
org.omg.CORBA.Object objRef=orb.resolve_initial_references("NameService");
NamingContextExt ncRef= NamingContextExtHelper.narrow(objRef); /*
* 将对象引用以相应的名字发布到命名服务中。
*/
String name="Calc";
NameComponent path[] = ncRef.to_name(name);
ncRef.rebind(path,iref); System.out.println("Calculator server ready..."); /*
* 阻塞直到ORB关闭。
*/
orb.run();
}
}

客户端代码Client

package com.bes.corba.test;

import org.omg.CORBA.IntHolder;
import org.omg.CORBA.ORB;
import org.omg.CosNaming.NamingContextExt;
import org.omg.CosNaming.NamingContextExtHelper; import com.bes.corba.test.Calc;
import com.bes.corba.test.CalcHelper; public class Client {
public static void main(String[] args) throws Exception{
/*
* ORB 初始化。
*/
ORB orb=ORB.init(args,null); /*
* 获取命名服务。
*/
org.omg.CORBA.Object objRef=orb.resolve_initial_references("NameService");
NamingContextExt ncRef=NamingContextExtHelper.narrow(objRef); /*
* 从命名服务中查找相应的对象引用,并进行类型转型。
*/ String name="Calc";
Calc calc=CalcHelper.narrow(ncRef.resolve_str(name)); /*
* 调用对象的方法。
*/
IntHolder result=new IntHolder(); calc.add(1,2,result);
System.out.printf("1+2=%d\n",result.value);
}
}

启动命名服务,命名服务器不一定运行在对象服务器的进程中,尤其是在一个分布式的环境中,命名服务器与对象服务器通常是一对多的关系。

orbd -ORBInitialPort 1050 -ORBInitialHost localhost&

启动服务端:

java HelloServer -ORBInitialPort 1050 -ORBInitialHost localhost

ORBInitialPort和ORBInitialHost参数指定了命名服务器的主机名和端口号。

启动客户端:

java com.bes.corba.test.Client -ORBInitialPort 1050 -ORBInitialHost localhost

一个简单的CORBA例子的更多相关文章

  1. 轻松创建nodejs服务器(1):一个简单nodejs服务器例子

    这篇文章主要介绍了一个简单nodejs服务器例子,本文实现了一个简单的hello world例子,并展示如何运行这个服务器,需要的朋友可以参考下   我们先来实现一个简单的例子,hello world ...

  2. 使用Multiplayer Networking做一个简单的多人游戏例子-3/3(Unity3D开发之二十七)

    使用Multiplayer Networking做一个简单的多人游戏例子-1/3 使用Multiplayer Networking做一个简单的多人游戏例子-2/3 使用Multiplayer Netw ...

  3. 使用Multiplayer Networking做一个简单的多人游戏例子-2/3(Unity3D开发之二十六)

    猴子原创,欢迎转载.转载请注明: 转载自Cocos2Der-CSDN,谢谢! 原文地址: http://blog.csdn.net/cocos2der/article/details/51007512 ...

  4. 使用Multiplayer Networking做一个简单的多人游戏例子-1/3(Unity3D开发之二十五)

    猴子原创,欢迎转载.转载请注明: 转载自Cocos2Der-CSDN,谢谢! 原文地址: http://blog.csdn.net/cocos2der/article/details/51006463 ...

  5. 一个简单的cmake例子

    一个简单的cmake例子CMakeLists.txt,生成动态库文件,可以指定发布目录. 尚不支持: 1.交叉编译环境配置 2.添加依赖库   #在当前目录新建一个build目录,然后cd build ...

  6. 一个简单的springmvc例子 入门(1)

    一直是从事棋牌游戏,平常用的东西 大多数只是使用一些javase的一些 api对spring 这方面 用到的比较少,每次学了都忘,始终记不住.为了 更轻松学习springboot,从新学习了sprin ...

  7. 利用VisualStudio单元测试框架举一个简单的单元测试例子

    本随笔很简单,不涉及mock和stub对象,而是只给出一个简单的利用Visual Studio单元测试框架的最简单例子.如果需要深入理解Unit Test的原理与艺术,请参考<The art o ...

  8. 撸一个简单的MVVM例子

    我个人以为mvvm框架里面最重要的一点就是VM这部分,它要与Model层建立联系,将Model层转换成可以被View层识别的数据结构:其次也要同View建立联系,将数据及时更新到View层上,并且响应 ...

  9. 企业级任务调度框架Quartz(3) 一个简单的Quartz 例子

    1. 一个简单的Quartz 工程     本示例应用比起众所周知的 System.out.println("Hello world from Quartz") 来还是要有趣些.当 ...

随机推荐

  1. zookeeper学习系列:三、利用zookeeper做选举和锁

    之前只理解zk可以做命名,配置服务,现在学习下他怎么用作选举和锁,进一步还可构建master-slave模式的分布式系统. 为什么叫Zoo?“因为要协调的分布式系统是一个动物园”. ZooKeeper ...

  2. 关于MediaPlayer的详细介绍

    1)如何获得MediaPlayer实例:可以使用直接new的方式:MediaPlayer mp = new MediaPlayer();也可以使用create的方式,如:MediaPlayer mp ...

  3. Java正则匹配数字

    包括5种形式,如测试结果 import java.util.Scanner; import java.util.regex.*; public class Com { public static vo ...

  4. RichTextBox文字处理控件属性介绍

    RichTextBox控件是一种既能够输入文本. 又能够修改文本的文字处理控件, 与TextBox控件比较, RichTextBox控件的文字处理功用更加丰厚, 不只能够设定文字的色彩. 字体, 还具 ...

  5. android贪吃蛇(超级简陋版)

    public class body { public int ax;//代表X周变量 public int ay;//代表Y轴变量 public int getAx() { return ax; } ...

  6. android studio 开启genymotion 出现"failed to create framebuffer image"

    出现错误 Unable to start the virtul device To start virtual devices, make sure that your video card supp ...

  7. SPFA算法学习笔记

    一.理论准备 为了学习网络流,先水一道spfa. SPFA算法是1994年西南交通大学段凡丁提出,只要最短路径存在,SPFA算法必定能求出最小值,SPFA对Bellman-Ford算法优化的关键之处在 ...

  8. Notification NotificationManager RemoteViews PendingIntent

    Notification和NotificationManager :可以用来实现可视化的消息通知等,比如:下载进度显示,消息显示,广播的内容等 RemoteViews :用于创建自定义的Notific ...

  9. (转)PhoneGap工作原理及需改进的地方

    原文:http://mobile.51cto.com/web-330900.htm PhoneGap工作原理及需改进的地方 2012-04-18 16:42 佚名 网络整理 字号:T | T 目前开发 ...

  10. 2016 - 1 - 24 NSURLSession (一)

    一: NSURLSession简介 1.实施步骤 1.1 使用 NSURLSession对象 创建TASK ,然后执行TASK 2.TASK的类型: 二: NSURLSession的简单使用: - ( ...