CyberLink for Java 编程指南

这篇是翻译官网的编程指南,官网地址:http://www.cybergarage.org/pdfdoc/clinkjavaproguide.pdf

目录:

1  简介

2  步骤

3  设备

    3.1  类概述

    3.2  描述

    3.3 初始化

    3.4 通知

    3.5 嵌入的设备

    3.6 服务

    3.7 控制

    3.8 事件

4.  控制点

    4.1 类概述

    4.2 初始化

    4.3 通知

    4.4 搜索

    4.5 根设备

    4.6 控制

    4.7 事件

__________________________________________________

1. 简介

UPnP™*1 技术基于开放的网络,能够发现并控制网络上的设备和服务,比如在家里使用的多媒体提供者和播放者。(如DMS,DMP)

UPnP™ 技术基于许多标准协议,如:GENA, SSDP, SOAP, HTTPU 和 HTTP。因此为了创建你的基于 UPnP™ 的设备你必须懂得并实现这些协议。

C 语言版的 CyberLink 是一个提供给 UPnP™ 开发者的开发包。CyberLink 自动控制这些协议,以此帮助你快速地开发设备和控制点。

请查阅以下站点和文档去了解关于 UPnP™ 的更多细节。

文档                                                                                           地址

UPnP™ Forum                                                                       http://www.upnp.org/

2. 步骤

要使用这个开发包,需要将开发包复制到你的 JDK 和 JRE 库所在的目录。例如,

cp clink???.jar C:¥jdk1.x¥jre¥lib¥ext¥
        cp clink???.jar C:¥Program Files¥JavaSoft¥JRE¥1.x¥lib¥ext¥               (这里本来就是这样)

当前版本的这个开发包需要以下包用于解析 XML 和 SOAP 请求。CyberLink 会自动加载以下 XML 包。如果你的开发框架没有这些 XML 包,请先安装它们。

包名                                             地址

Apache Xerces                          http://xml.apache.org/xerces2-j/index.html
    kXML2                                         http://www.kxml.org/
    JAXP                                            https://jaxp.dev.java.net/
    XMLPull                                       http://www.xmlpull.org/

3    设备

3.1 类概述

以下静态结构图列出了 CyberLink 中用于创建 UPnP™ 设备的相关类。一个设备可以含有数量不等的内嵌的设备和服务,服务则包含一些动作和变量。

以上思维结构图为了显示而做了简化。

3.2 描述

首先,在创建 UPnP™ 设备之前,你必须完成一些对应你的设备和服务的描述文件。

根设备的描述文件不要有 URLBase 元素,因为在使用描述文件创建设备的时候该元素会被自动添加。

服务描述文件需要创建一个设备,但是 presentationURL 和 iconList 元素只是推荐选项。请阅读 UPnP™ 说明书中关于描述文件格式的章节获得更多的细节。

3.3. 初始化

为了创建 UPnP™ 设备,需要根据根描述文件创建一个设备类的实例。这个创建出的设备是一个根设备,只有根设备可以使用 Device::start()。设备创建完成后会向

UPnP™ 网络发送通知。以下内容展示初始化一个设备。

[java]  view plain copy
  1. import org.cybergarage.upnp.*;
  2. import org.cybergarage.upnp.device.*;
  3. ……
  4. String descriptionFileName = "description/description.xml";
  5. Try {
  6. Device upnpDev = new Device(descriptionFileName);
  7. ……
  8. upnpDev.start();
  9. } catch (InvalidDescriptionException e){
  10. String errMsg = e.getMessage();
  11. System.out.println(“InvalidDescriptionException = ” + errMsg);
  12. }

当描述文件无效时会产生 InvalidDescriptionException 异常。可以使用 getMessage() 获得异常原因的更多细节。

有两种方案可供选择,你可以向下面这样使用 Device::loadDescription() 和 Service::loadSCPD() 加载描述代替代替使用描述文件。加载出错会使加载方法抛出异常。

[java]  view plain copy
  1. String DEVICE_DESCRIPTION =
  2. "<?xml version=¥"1.0¥" ?>¥n" +
  3. "<root xmlns=¥"urn:schemas-upnp-org:device-1-0¥">¥n" +
  4. . . . .
  5. "</root>";
  6. String SERVICE_DESCRIPTION[] =
  7. "<?xml version=¥"1.0¥"?>¥n" +
  8. "<scpd xmlns=¥"urn:schemas-upnp-org:service-1-0¥" >¥n" +
  9. . . . .
  10. “</scpd>”;
  11. try {
  12. Device upnpDev = new Device();
  13. boolean descSuccess = upnpDev.loadDescription(DEVICE_DESCRIPTION);
  14. Service upnpService = getService("urn:schemas-upnp-org:service:****:1");
  15. boolean scpdSuccess = upnpService.loadSCPD(SERVICE_DESCRIPTION[);
  16. }
  17. catch (InvalidDescriptionException e){
  18. String errMsg = e.getMessage();
  19. System.out.println(“InvalidDescriptionException = ” + errMsg);
  20. }

活动的根设备拥有一些服务线程(这里写 processes),在控制点发向设备发送请求的时候会自动回应。例如,根设备有一个 HTTP 服务,当控制点使用 GET 请求希望获得描述文件时该服务会回应一个描述文件。在设备启动时它支自动为 HTTP 服务寻找一个有效的端口。

根设备使用以下默认的参数创建,你可以在根设备创建之前使用以下方法改变这些参数。

Parameter                   Default                     Function
1 HTTP port                    4004                         setHTTPPort()
2 Description URI         /description.xml      setDescriptionURI()
3 Lease time                 1800                          setLeaseTime()

3.4 通知

当设备通过 Device::start() 启动并加入 UPnP™ 网络时会自动发出一个带有 ssdp::alive 标头的通知消息。当设备通过 Device::stop() 停止运行,

一个带有ssdp::byebye 标头的通知自动发送。你也可以使用方法 Device::announce() 和 Device::byebye() 发送这些通知。

当一个控制点向 UPnP™ 网络发送 M-SEARCH 请求时,活动的设备会在规定时间内自动回应。

3.5 内嵌设备

一个设备可能拥有一些内嵌设备。使用 Device::getDeviceList() 可以得到内嵌设备的列表。下面的例子输出所有内嵌设备的友好名称。

[java]  view plain copy
  1. public void printDevice(Device dev)
  2. {
  3. String devName = dev.getFriendlyName();
  4. System.out.println(devName);
  5. DeviceList childDevList = dev.getDeviceList();
  6. int nChildDevs = childDevList.size();
  7. for (int n =0; n <nChildDevs; n ++) {
  8. Device childDev = childDevList.getDevice(n);
  9. printDevice(childDev);
  10. }
  11. }
  12. ......
  13. Dev rootDev = ....;
  14. ……
  15. DeviceList childDevList = rootDev.getDeviceList();
  16. int childDevs = childDevList.size();
  17. for (int n=0; n< childDevs; n++) {
  18. Device childDev = rootDevList.getDevice(n);
  19. printDevice(childDev);
  20. }

你可以通过使用 Device::getDevice() 并传入一个 friendly name 或者 UDN 找到内嵌设备。下面以例子通过 friendly name 找到一个内嵌设备。

[java]  view plain copy
  1. Device homeServerDev ....
  2. Device musicDev = homeServerDev.getDevice(“music”);

3.6 服务

使用 Device::getServiceList() 可以访问设备的所有内嵌服务。服务可以有许多动作和变量。使用 Service::getActionList() 得到这些动作,使用

Service::getServiceStateTable() 得到这些变量。下面的例子输出一个设备的所有动作和变量。

[java]  view plain copy
  1. Device dev ....
  2. ServiceList serviceList = dev.getServiceList();
  3. int serviceCnt = serviceList.size();
  4. for (int n=0; n<serviceCnt; n++) {
  5. Service service = serviceList.getService(n);
  6. ActionList actionList = service.getActionList();
  7. int actionCnt = actionList.size();
  8. for (int i=0; i<actionCnt; i++) {
  9. Action action = actionList.getAction(i);
  10. System.out.println(“action [“ + i + “] = “ + action.getName());
  11. }
  12. ServiceStateTable stateTable = service. getServiceStateTable ();
  13. int varCnt = stateTable.size();
  14. for (int i=0; i<actionCnt; i++) {
  15. StateVariable stateVar = stateTable.getServiceStateVariable(i);
  16. System.out.println(“stateVar [“ + i + “] = “ + stateVar.getName());
  17. }
  18. }

你可以使用 Device::getService() 并以 service ID 为参数找到服务,同样你可以通过名字得到服务中的动作和状态变量。使用 Device::getAction() 或 Service::getAction() 找到动作,Device::getStateriable() 或 Service::getStateVariable() 去找到状态变量。下面的例子通过名字找到一个服务,一个动作和一个状态变量。

[java]  view plain copy
  1. Device clockDev ....
  2. Service timerSev = clockDev.getService(“timer”);
  3. Action getTimeAct = clockDev.getAction(“GetTime”);
  4. StateVariable timeStat = clockDev.getStateVariable(“time”);

3.7 控制

想要接受来自控制点的动作控制,设备需要实现 ActionListener 接口。这个监听接口必须实现拥有动作及参数列表为参数的 acctionControlReceived() 方法。输入参数有从控制点传来的值,如果有请求有效就设置回应值作为输出参数并返回 true。如果请求无效则返回 false。当返回值是 false 或设备没有实现该接口时会向控制点返回 UPnPError 作为结果。UPnPError 的默认值是 INVALID_ACTION,但是可以使用 Action::setSetStatus() 来返回其它 UPnP 错误。

想要接受来自控制点的查询控制,设备需要实现 QueryListener 接口。这个监听接口必须实现含有服务变量为参数的 queryControlReceived() 方法。请求有效时返回 true,其它情况返回 false。当返回值为 false 或设备没有实现这个接口时 UPnPError 反馈会自动被返回给控制点。

UPnPError 的默认值是 INVALID_ACTION,可以使用 ServiceVariable::setSetStatus() 返回其它 UPnP 错误

下面的例子是个时钟设备。这个设备处理两个动作请求和一个查询请求。

[java]  view plain copy
  1. public class ClockDevice extends Device implements ActionListener, QueryListener {
  2. public ClockDevice() {
  3. super (“/clock/www/description.xml”);
  4. Action setTimeAction = getAction(“SetTime”);
  5. setTimeAction.setActionListener(this);
  6. Action getTimeAction = getAction(“GetTime”);
  7. getTimeAction.setActionListener(this);
  8. StateVariable stateVar = getStateVariable(“Timer”);
  9. stateVar.setQueryListener(this);
  10. }
  11. public boolean actionControlRecieved(Action action) {
  12. ArgumentList argList = action.getArgumentList();
  13. String actionName = action.getName();
  14. if (actionName.equals("SetTime") == true) {
  15. Argument inTime = argList.getArgument(“time”);
  16. String timeValue = inTime.getValue();
  17. If (timeValue == null || timeValue.length() <= 0)
  18. return false;
  19. ……..
  20. Argument outResult = argList.getArgument(“result”);
  21. arg.setValue(“TRUE”);
  22. return true;
  23. }
  24. else if (actionName.equals(“GetTime”) == true) {
  25. String currTimeStr = ….. Argument currTimeArg = argList.getArgument(“currTime”);
  26. currTimeArg.setValue(currTimeStrs);
  27. return true;
  28. }
  29. action.setStatus(UPnP::INVALID_ACTION, “…..”);
  30. return false;
  31. }
  32. public bool queryControlReceived(StateVariable stateVar) {
  33. if (varName.equals(“Time”) == true) {
  34. String currTimeStr = ….;
  35. stateVar.setValue(currTimeStr);
  36. return true;
  37. }
  38. stateVar.setStatus(UPnP::INVALID_VAR, “…..”);
  39. return false;
  40. }
  41. }

使用 Device::setActionListener() 或 Service::setActionListener() 给设备或服务添加针对控制点动作控制的监听。使用 Device::setQueryListener() 或 Service::setQueryListener() 给设备或服务添加针对控制点查询控制的监听。下面的例子设置了一个监听用于设备的所有控制。

[java]  view plain copy
  1. class ClockDevice : public Device, public ActionListener, public QueryListener
  2. {
  3. public:
  4. ClockDevice() : Device(“/clock/www/description.xml”) {
  5. setActionListner(this);
  6. setQueryListener (this);
  7. }
  8. bool actionControlRecieved(Action *action) { ……. }
  9. bool queryControlReceived(StateVariable *stateVar) { ……. }
  10. }

3.8 事件

控制点也许会订阅设备的一些事件。你不需要管理控制点的订阅,因为设备会自动管理订阅事件。例如,当一个控制点发送订阅消息给设备时时务会自动添加该控制点进入订阅者列表,当控制点发送一个取消订阅的消息时设备会自动将其从订阅者列表中删除。

当你希望向订阅者发送状态时可以使用 ServiceStateVariable::setValue() 方法,这样当状态变量更新的时候设备会自动向订阅者发送事件通知。下面的例子更新了一个状态变量,然后状态变量的改变自动发布到订阅者中。

[java]  view plain copy
  1. Device clockDevice = ....
  2. StateVariable timeVar = clockDevice.getStateVariable("Time");
  3. String timeStr = .....
  4. timeVar.setValue(timeStr);

4  控制点

4.1 类概述

以下静态结构图显示了创建一个 UPnP™ 控制点相关的类。控制点在 UPnP™ 中拥有一些根设备。

4.2 初始化

为了创建一个 UPnP™ 控制点需要创建一个 ControlPoint 类的实例。使用 ControlPoint::start() 去启动控制点。当控制点启动后它会向 UPnP™ 网络多播一个发现消息。

[java]  view plain copy
  1. import org.cybergarage.upnp.*;
  2. import org.cybergarage.upnp.device.*;
  3. ……
  4. ControlPoint ctrlPoint = new ControlPoint();
  5. ……
  6. ctrlPoint.start();

活动的控制点拥有一些服务线程(server processes),会自动回复一些 UPnP™ 发送过来的消息。例如,控制点有一个 SSDP 服务用来处理 SEARCH 回应,当控制点启动时会自动找一个合适的端口用于 SSDP 服务。控制点以下面的默认参数启动。

Parameter                   Default                    Methods
1 HTTP port                   39500                      setHTTPPort()
2 SSDP port                  39400                      setSSDPPort()
3 Subscription URI      /eventSub                setEventSubURI()
4 Search Response    3                               setSerchMx()

4.3 通知

控制点接收来自 UPnP™ 网络中的设备的通知事件,并据些自动增加或删除设备。同样过期的设备也自动同设备列表中移除。你不需要管理通知事件,但是你可以实现 NotifyListener 接口来接收事件。下面的例子展示了接收通知消息。

[java]  view plain copy
  1. public class MyCtrlPoint extends ControlPoint implements NotifyListener {
  2. public MyCtrlPoint() {
  3. ........
  4. addNotifyListener(this);
  5. start();
  6. }
  7. public void deviceNotifyReceived(SSDPPacket ssdpPacket) {
  8. String uuid = ssdpPacket.getUSN();
  9. String target = ssdpPacket.getNT();
  10. String subType = ssdpPacket.getNTS();
  11. String location = ssdpPacket.getLocation();
  12. .......
  13. }
  14. }

只有在增加或删除设备的时候才会调用下面的接口, DeviceChangeListener。

[java]  view plain copy
  1. public class MyCtrlPoint extends ControlPoint implements DeviceChangeListener {
  2. public MyCtrlPoint() {
  3. ........
  4. addDeviceChangeListener (this);
  5. start();
  6. }
  7. public void deviceAdded (Device dev) {
  8. ……..
  9. }
  10. public void deviceRemoved(Device dev) {
  11. ……..
  12. }
  13. }

4.4 搜索

你可以调用 ControlPoint::search() 方法来更新设备。被发现的设备会自动添加到设备列表中,同时你可以实现 SearchResponseListener 接口来接收反馈。下面的例子展示了接收通知消息。

[java]  view plain copy
  1. public class MyCtrlPoint extends ControlPoint implements SearchResponseListener {
  2. public MyCtrlPoint() {
  3. ........
  4. addSearchResponseListener(this);
  5. start();
  6. ........
  7. search(“upnp:rootdevice”);
  8. }
  9. public void deviceSearchResponseReceived(SSDPPacket ssdpPacket) {
  10. String uuid = ssdpPacket.getUSN();
  11. String target = ssdpPacket.getST();
  12. String location = ssdpPacket.getLocation();
  13. ........
  14. }
  15. }

4.5 根设备

使用 ControlPoint::getDeviceList() 仅返回设备列表中的根设备。下面的例子展示了输出所有根设备的友好名字。

[java]  view plain copy
  1. ControlPoint ctrlPoint = new ControlPoint();
  2. ……
  3. ctrlPoint.start();
  4. ……
  5. DeviceList rootDevList = ctrlPoint.getDeviceList();
  6. int nRootDevs = rootDevList.size();
  7. for (int n=0; n<nRootDevs; n++) {
  8. Device dev = rootDevList.getDevice(n);
  9. String devName = dev.getFriendlyName();
  10. System.out.println(“[“ + n + “] = ” + devName);
  11. }

你可以通过根设备的友好名字,设备类型,或 UDN 结合 ControlPoint::getDevice() 找到根设备。下面的例子展示了通过友好名字找到根设备。

[java]  view plain copy
  1. ControlPoint ctrlPoint = new ControlPoint();
  2. ……
  3. ctrlPoint.start();
  4. ……
  5. Device homeServerDev = ctrlPoint.getDevice(“xxxx-home-server”);

4.6 控制

控制点可以发送动作或查询控制消息给被发现的设备。如果要发送动作控制消息,使用 Action::setArgumentValue() 和 Action::postControlAction()。你可以设置动作的值给所有输入参数,如果你设置了输出参数它会被自动忽略。下面的例子发送一个动作控制请求设置一个新的时候,然后显示返回的值。

[java]  view plain copy
  1. Device clockDev = ....
  2. Action setTimeAct = clockDev.getAction(“SetTime”);
  3. String newTime = ....
  4. setTimeAct.setArgumentValue(“time”, newTime); // setTimeAct.getArgument(“time”).setValue(newTime);
  5. if (setTimeAct.postControlAction() == true) {
  6. ArgumentList outArgList = setTimeAct.getOutputArgumentList();
  7. int nOutArgs = outArgList.size();
  8. for (int n=0; n<nOutArgs; n++) {
  9. Argument outArg = outArgList.getArgument(n);
  10. String name = outArg.getName();
  11. String value = outArg.getValue();
  12. ......
  13. }
  14. }
  15. else {
  16. UPnPStatus err = setTimeAct.getUPnPStatus();
  17. System.out.println("Error Code = " + err.getCode());
  18. System.out.println("Error Desc = " + err.getDescription());
  19. }

可以使用 StateVariable::postQueryControl() 发送一个查询控制消息。下面的例子发送一个查询控制消息,然后输出返回值。

[java]  view plain copy
  1. Device clockDev = ....
  2. StateVariable timeStateVar = clockDev.getStateVariable(“time”);
  3. if (timeStateVar.postQueryControl() == true) {
  4. String value = timeStateVar.getValue();
  5. ......
  6. }
  7. else {
  8. UPnPStatus err = timeStateVar.getUPnPStatus();
  9. System.out.println("Error Code = " + err.getCode());
  10. System.out.println("Error Desc = " + err.getDescription());
  11. }

使用 Argument::getRelatedStateVariable() 去获得参数相关联的 StatiVariable,使用 StateVariable::getAllowedValueRange() 或 getAllowedValueList() 去获得值允许的范围或列表。

[java]  view plain copy
  1. Device clockDev = ....
  2. Action timeAct = clockDev.getAction(“SetTime”);
  3. Argument timeArg = timeAct.getArgument(“time”);
  4. StataVariable stateVar = timeArg.getRelatedStateVariable();
  5. if (stateVar != null) {
  6. if (stateVar.hasAllowedValueRange() == true) {
  7. AllowedValueRange valRange = stateVar.getAllowedValueRange();
  8. ......
  9. }
  10. if (stateVar.hasAllowedValueList() == true) {
  11. AllowedValueList valList = stateVar.getAllowedValueList ();
  12. ......
  13. }
  14. }

4.7 事件

控制点可以订阅被发现的设备的事件,使用 ControlPoint::subscribe() 并实现 EventListener 接口来获得服务状态的变化。这个监听必段实现 eventNotifyReceived()。

[java]  view plain copy
  1. public MyControlPoint extends ControlPoint implements EventListener {
  2. public MyControlPoint() {
  3. .....
  4. addEventListener(this);
  5. }
  6. .....
  7. public void eventNotifyReceived(String uuid, long seq, String name, String value) {
  8. ....
  9. }
  10. }

如果服务接受了订阅, ControlPoint::subscribe() 将返回 true,这时你可以获得订阅 id 和 过期时间。

[java]  view plain copy
  1. ControlPoint ctrlPoint = .....
  2. Device clockDev = ctrlPoint.getDevice(“xxxx-clock”);
  3. Service timeService = clockDev.getService(“time:1”);
  4. boolean subRet = ctrlPoint.subscribe(timeService);
  5. if (subRet == true) {
  6. String sid = timeService.getSID();
  7. long timeout = timeService.getTimeout();
  8. }

CyberLink for Java 编程指南相关推荐

  1. 推荐一本书《网络机器人java编程指南》

    如果对搜索引擎感兴趣,推荐给大家一本书,Jeff Heaton的<网络机器人java编程指南>.中文版,E文不好的这下不用头痛了.有需要的,可以留言给我. Jeff Heaton is a ...

  2. 【Java编程指南】综合案例

    目录 一.前言 二.编程训练 三.减肥计划 1.问题分析 2.if编码实现 3.switch编码实现 4.输出结果 四.逢七过 1.问题分析 2.编码实现 3.输出结果 五.百鸡百钱 1.问题分析 2 ...

  3. 【Java编程指南】语法基础

    目录 一.前言 二.关键字 三.数据类型 1.存储单元 2.存储范围 3.类型转换 四.常量 五.变量 六.标识符 七.注释 一.前言 学习目标 1:熟悉Java的关键字.数据类型(包括范围).常量与 ...

  4. 谷歌 Java 编程风格指南

    点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 来源 | http://hawstein.com/201 ...

  5. Java 7 并发编程指南

    原文是发表在并发编程网上翻译后的 <Java 7 并发编程指南>,这里对其中的目录做个更加详细的描述,并且写出了重点说明,方便日后快速查阅.建议仔细查看每节的代码实现,非常具有参考价值.可 ...

  6. 深度解析Google Java 编程风格指南

    这份文档是Google Java编程风格规范的完整定义.当且仅当一个Java源文件符合此文档中的规则, 我们才认为它符合Google的Java编程风格. 与其它的编程风格指南一样,这里所讨论的不仅仅是 ...

  7. Google Java编程风格指南中文版

    作者:Hawstein 出处:http://hawstein.com/posts/google-java-style.html 声明:本文采用以下协议进行授权: 自由转载-非商用-非衍生-保持署名|C ...

  8. Google 出品的 Java 编码规范和编程指南!

    这份文档是Google Java编程风格规范的完整定义.当且仅当一个Java源文件符合此文档中的规则, 我们才认为它符合Google的Java编程风格. 与其它的编程风格指南一样,这里所讨论的不仅仅是 ...

  9. Google Java编程风格指南中文版(转)

    作者:Hawstein 出处:http://hawstein.com/posts/google-java-style.html 声明:本文采用以下协议进行授权: 自由转载-非商用-非衍生-保持署名|C ...

最新文章

  1. 视频目标跟踪算法综述
  2. linux环境下最简单的C语言例子
  3. 论文浅尝 | 一种可解释的语义匹配复值网络
  4. 32 岁大佬阿里二面,他答 JVM 题的姿势,令面试官很想不通。。。
  5. 【MIT领导力课程笔记】前Nokia 总裁Ollila——打造移动未来
  6. 2020年12月国产数据库排行:榜首TiDB 2.7亿融资再破纪录;openGauss晋级十强!
  7. lsof 列出谁在使用某个端口
  8. DFS+剪枝:N个蛋放入M个篮子并可以任意取
  9. CF1139D Steps to One
  10. Linux下安装配置JDK6
  11. 用Bluemix虚拟机搭建自己的博客系统
  12. 单片机花灯开关控制器C语言代码,以51单片机为核心的LED彩灯控制器设计
  13. 哥德巴赫猜想 php,C++_c++验证哥德巴赫猜想,哥德巴赫猜想是世界近代三大 - phpStudy...
  14. element-ui 时间日期选择器格式调整(yyyy-mm-dd)
  15. Rviz 实现 pannel 插件
  16. AI-人工智能 easyAi开发技术
  17. python 爱心文字墙_博客园墙裂推荐!从未见过如些清新脱俗的完整Python+requests接口自动化测试框架搭建文章!...
  18. 博导谈寒门子弟上大学:要相信双一流大学没有“废物”
  19. bp神经网络解决什么问题,bp神经网络的改进方法
  20. 使用VC++6.0时出现error spawning cl.exe解决方法

热门文章

  1. python n个list如何组成矩阵_硬核科普系列:用python帮你建立自己的投资组合
  2. 211北京林业大学计算机本科生,平均月薪10733元!
  3. 流量调节阀的特性的影响因素,你知道多少?
  4. linux namespace简单操作
  5. Apollo Planning(二)
  6. 题目 1012: [编程入门]字符串分类统计
  7. IT绿色节能大势所趋 观念行动很重要
  8. dayz哪些服务器物品是互通,DayZ服务器常用的一些武器食物吃喝子弹弹夹配件背包代码...
  9. 重庆北大青鸟解放碑校区J12班 小米步枪队【在线预约挂号系统】
  10. 最近心情真的是烦到爆