Cling核心手册

2019-07-13 05:32发布

Cling


Table Of Contents:
目录
1.Getting Started(开始)
2.A first UPnP service and control point(第一个UPnP服务和控制点)
2.1.The SwitchPower service implementation(开关服务的实施)
2.2.Binding a UPnP device(绑定到一个UPnP设备)
2.3.Running the server(运行服务)
2.4.Creating a control point(生成控制点)
2.5.Executing an action(执行动作)
2.6.Starting the application(开始一个应用程序)
2.7.Debugging and logging(调试和日志信息)
3.The Cling Core API(Cling核心的API)
3.1.Working with a UpnpService(开始一个UpnpService)
3.1.1.Customizing configuration settings(定制配置设置)
3.1.2.The protocol factory(协议工厂)
3.1.3.Accessing low-level network services(访问低级网络服务)
3.2.Client operations with ControlPoint(使用控制点完成客户操作)
3.2.1.Searching the network(查找网络)
3.2.2.Invoking an action(调用一个动作)
3.2.3.Receiving events from services(从服务收到事件)
3.3.The Registry(注册机)
3.3.1.Browsing the Registry(浏览注册机)
3.3.2.Listening to registry changes(侦听注册机改变)
4.Creating and binding services(生成和绑定服务)
4.1.Annotating a service implementation(注意一个服务实施)
4.1.1.Mapping state variables(映射状态参数)
4.1.2.Explicitly naming related state variables(清除命名相关状态参数)
4.1.3.Getting an output value from another method(从一个方法得到输出)
4.1.4.Getting output values from a JavaBean(从JavaBean得到输出值)
4.2.Providing events on service state changes(提供服务状态改变的事件)
4.3.Converting string action argument values(转变动作字符串参数)
4.3.1.String value converters(字符串转换)
4.3.2.Working with enums(使用枚举变量)
4.4.Restricting allowed state variable values(限制允许的状态参数值)
4.4.1.Exclusive list of string values(字符串值的排他列表)
4.4.2.Restricting numeric value ranges(限制数值范围)
5.Cling on Android(android上的Cling)
5.1.Configuring the application service(配置应用程序的服务)
5.2.Accessing the service from an activity(从一个activity中访问服务)
5.3.Creating a UPnP device(生成一个UPnP设备)
5.4.Optimizing service behavior(优化服务行为)
5.4.1.Tuning registry maintenance(调整注册维护线程)
5.4.2.Pausing and resuming registry maintenance(暂停和恢复寄存器维护线程)
5.4.3.Configuring discovery(配置发现)
6.Advanced options(高级选项)
6.1.Custom client/server information(定制客户/服务信息)
6.1.1.Adding extra request headers(添加外部请求头部信息)
6.1.2.Accessing remote client information(访问远程客户信息)
6.2.Long-running actions(长时运行的行为)
6.2.1.Cancelling an action invocation(暂停行为请求)
6.2.2.Reacting to cancellation on the server(服务取消的反动作)
6.3.Switching XML descriptor binders(切换XMP描述符绑定者)
6.4.Switching XML processors(切换XML处理器)
6.5.Solving discovery problems(解决发现问题)
6.5.1.Maximum age of remote devices(远程)
6.5.2.Alive messages at regular intervals(周期性在线信息)
6.5.3.Using discovery options for local devices(使用本地设备发现选项)
6.5.4.Manual advertisement of local devices(手动广播本地设备)
6.6.Configuring network transports(配置网络传输。)
1. Getting Started
开始
This is how you use Cling:
下面是你怎么使用Cling
#-----------------------------------code start here--------------------------------------------------------------------------------
package ...;


import org.fourthline.cling.model.message.header.STAllHeader;
import org.fourthline.cling.model.meta.LocalDevice;
import org.fourthline.cling.model.meta.RemoteDevice;
import org.fourthline.cling.registry.Registry;
import org.fourthline.cling.registry.RegistryListener;


/**
 * Runs a simple UPnP discovery procedure.
 */
public class Main {


    public static void main(String[] args) throws Exception {


        // UPnP discovery is asynchronous, we need a callback
        RegistryListener listener = new RegistryListener() {


            public void remoteDeviceDiscoveryStarted(Registry registry,
                                                     RemoteDevice device) {
                System.out.println(
                        "Discovery started: " + device.getDisplayString()
                );
            }


            public void remoteDeviceDiscoveryFailed(Registry registry,
                                                    RemoteDevice device,
                                                    Exception ex) {
                System.out.println(
                        "Discovery failed: " + device.getDisplayString() + " => " + ex
                );
            }


            public void remoteDeviceAdded(Registry registry, RemoteDevice device) {
                System.out.println(
                        "Remote device available: " + device.getDisplayString()
                );
            }


            public void remoteDeviceUpdated(Registry registry, RemoteDevice device) {
                System.out.println(
                        "Remote device updated: " + device.getDisplayString()
                );
            }


            public void remoteDeviceRemoved(Registry registry, RemoteDevice device) {
                System.out.println(
                        "Remote device removed: " + device.getDisplayString()
                );
            }


            public void localDeviceAdded(Registry registry, LocalDevice device) {
                System.out.println(
                        "Local device added: " + device.getDisplayString()
                );
            }


            public void localDeviceRemoved(Registry registry, LocalDevice device) {
                System.out.println(
                        "Local device removed: " + device.getDisplayString()
                );
            }


            public void beforeShutdown(Registry registry) {
                System.out.println(
                        "Before shutdown, the registry has devices: "
                        + registry.getDevices().size()
                );
            }


            public void afterShutdown() {
                System.out.println("Shutdown of registry complete!");


            }
        };


        // This will create necessary network resources for UPnP right away
        System.out.println("Starting Cling...");
        UpnpService upnpService = new UpnpServiceImpl(listener);


        // Send a search message to all devices and services, they should respond soon
        upnpService.getControlPoint().search(new STAllHeader());


        // Let's wait 10 seconds for them to respond
        System.out.println("Waiting 10 seconds before shutting down...");
        Thread.sleep(10000);


        // Release all resources and advertise BYEBYE to other UPnP devices
        System.out.println("Stopping Cling...");
        upnpService.shutdown();
    }
}
#-----------------------------------code end here--------------------------------------------------------------------------------


You need cling-core.jar and its dependencies (seamless-*.jar files) on your classpath to build and run this code.
为了运行上面代码,你需要在你的classpath中添加 cling-core.jar and its dependencies (seamless-*.jar files)。
2. A first UPnP service and control point
//第一个UPnP服务和控制点


The most basic UPnP service imaginable is the binary light. This device has one service, the power switch, turning the light on and off. In fact, the SwitchPower:1 service and the BinaryLight:1 device are standardized templates you can download here.
//最基本可能的UPnP服务是一个二元灯。这个设备具有一个服务,一个电源开关来开关灯。实际上,这个二元灯服务和二元光设备有标准化的模板可以下载的。
In the following sections we'll implement this UPnP service and device with the Cling Core library as a simple Java console application.
//在下面的章节,我们将使用Cling核心库来实现一个UPnP服务和设备的简单Java控制台程序。
2.1. The SwitchPower service implementation
//2.1. 电源开关服务实施
This is the source of the SwitchPower:1 service - note that although there are many annotations in the source, no runtime dependency on Cling exists:
//这是一个电源开关服务,请注意,虽然源代码具有很多的的注释类,实际上没有对Cling的运行时依赖关系的。(说明注释类是给编译器使用的,哈哈)
#-----------------------------------code start here--------------------------------------------------------------------------------
package example.binarylight;


import org.fourthline.cling.binding.annotations.*;


@UpnpService(
        serviceId = @UpnpServiceId("SwitchPower"),
        serviceType = @UpnpServiceType(value = "SwitchPower", version = 1)
)
public class SwitchPower {


    @UpnpStateVariable(defaultValue = "0", sendEvents = false)
    private boolean target = false;


    @UpnpStateVariable(defaultValue = "0")
    private boolean status = false;


    @UpnpAction
    public void setTarget(@UpnpInputArgument(name = "NewTargetValue")
                          boolean newTargetValue) {
        target = newTargetValue;
        status = newTargetValue;
        System.out.println("Switch is: " + status);
    }


    @UpnpAction(out = @UpnpOutputArgument(name = "RetTargetValue"))
    public boolean getTarget() {
        return target;
    }


    @UpnpAction(out = @UpnpOutputArgument(name = "ResultStatus"))
    public boolean getStatus() {
        // If you want to pass extra UPnP information on error:
        // throw new ActionException(ErrorCode.ACTION_NOT_AUTHORIZED);
        return status;
    }
}
#-----------------------------------code end here--------------------------------------------------------------------------------


To compile this class the Cling Core library has to be available on your classpath. However, once compiled this class can be instantiated and executed in any environment, there are no dependencies on any framework or library code.
//为了编译这个类,你必须提供你的classpath。然后,一旦这个类被实例化之后被任何环境中执行,这个程序就不对任何框架和库代码的依赖关系了。


The annotations are used by Cling to read the metadata that describes your service, what UPnP state variables it has, how they are accessed, and what methods should be exposed as UPnP actions. You can also provide Cling metadata in an XML file or programmatically through Java code - both options are discussed later in this manual. Source code annotations are usually the best choice.
//注解类(annotations)被Cling用来度描述你的服务的元数据metadata,这些元数据包括UPnP所具有状态参数,他们怎么被访问,这个UPnP Actions应该暴露的方法。你可以通过xml文件提供这些Cling元数据,也可以通过Java代码中实现,这两个选项我们将在后面讨论。源码注视通常是最好的选择。


You might have expected something even simpler: After all, a binary light only needs a single boolean state, it is either on or off. The designers of this service also considered that there might be a difference between switching the light on, and actually seeing the result of that action. Imagine what happens if the light bulb is broken: The target state of the light is set to true but the status is still false, because the SetTarget action could not make the switch. Obviously this won't be a problem with this simple demonstration because it only prints the status to standard console output.
//你可能期望一些事情更简单:毕竟,一个二元光的例子仅仅就是一个单一的布尔状态,它是开或者关。显然,服务的设计者考虑到动作实施的结果可能和二元光有一些的不同。


2.2. Binding a UPnP device
//绑定UPnP设备
Devices (and embedded devices) are created programmatically in Cling, with plain Java code that instantiates an immutable graph of objects. The following method creates such a device graph and binds the service from the previous section to the root device:
//设备(和嵌入式设备)是在Cling代码里面生成,使用java代码来实例化一个immutable graph of objects。下面的方法用来生成一个这样的设备图,且绑定到前面章节提供的根设备的服务。


#-----------------------------------code start here--------------------------------------------------------------------------------
LocalDevice createDevice()
        throws ValidationException, LocalServiceBindingException, IOException {


    DeviceIdentity identity =
            new DeviceIdentity(
                    UDN.uniqueSystemIdentifier("Demo Binary Light")
            );


    DeviceType type =
            new UDADeviceType("BinaryLight", 1);


    DeviceDetails details =
            new DeviceDetails(
                    "Friendly Binary Light",
                    new ManufacturerDetails("ACME"),
                    new ModelDetails(
                            "BinLight2000",
                            "A demo light with on/off switch.",
                            "v1"
                    )
            );


    Icon icon =
            new Icon(
                    "image/png", 48, 48, 8,
                    getClass().getResource("icon.png")
            );


    LocalService switchPowerService =
            new AnnotationLocalServiceBinder().read(SwitchPower.class);


    switchPowerService.setManager(
            new DefaultServiceManager(switchPowerService, SwitchPower.class)
    );


    return new LocalDevice(identity, type, details, icon, switchPowerService);


    /* Several services can be bound to the same device:
    return new LocalDevice(
            identity, type, details, icon,
            new LocalService[] {switchPowerService, myOtherService}
    );
    */
    
}
#-----------------------------------code end here--------------------------------------------------------------------------------


Let's step through this code. As you can see, all arguments that make up the device's metadata have to be provided through constructors, because the metadata classes are immutable and hence thread-safe.
//我们来分析一下这个代码,你可以看到,构成设备的元数据参数都是通过对象的构造器提供,因为这些元数据都是不可改变的,所以是线程安全的。


DeviceIdentity
//设备身份
    Every device, no matter if it is a root device or an embedded device of a root device, requires a unique device name (UDN). This UDN should be stable, that is, it should not change when the device is restarted. When you physically unplug a UPnP appliance from the network (or when you simply turn it off or put it into standby mode), and when you make it available later on, it should expose the same UDN so that clients know they are dealing with the same device. The UDN.uniqueSystemIdentifier() method provides exactly that: A unique identifier that is the same every time this method is called on the same computer system. It hashes the network cards hardware address and a few other elements to guarantee uniqueness and stability.
//每一个设备,不论其是否是根设备,还是在根设备上的一个嵌入式设备,都需要一个第一无二的设备名字:UDN。这个UDN将是稳定的,也就是说当设备重启时,这个值不会被改变。
DeviceType
//设备类型
    The type of a device also includes its version, a plain integer. In this case the BinaryLight:1 is a standardized device template which adheres to the UDA (UPnP Device Architecture) specification.
DeviceDetails
//设备细节
    This detailed information about the device's "friendly name", as well as model and manufacturer information is optional. You should at least provide a friendly name value, this is what UPnP applications will display primarily.
Icon
//图标
    Every device can have a bunch of icons associated with it which similar to the friendly name are shown to users when appropriate. You do not have to provide any icons if you don't want to, use a constructor of LocalDevice without an icon parameter.
Service
//服务
    Finally, the most important part of the device are its services. Each Service instance encapsulates the metadata for a particular service, what actions and state variables it has, and how it can be invoked. Here we use the Cling annotation binder to instantiate a Service, reading the annotation metadata of the SwitchPower class.
//最后,设备最重要的部件就是他的服务了。每一个服务实例封装了特殊服务的的元数据,那些他具有那些动作和状态参数,他如何被调用。这里,我们使用Cling的注释Binder来实例化一个服务,读取开关电源类的注释元数据。
Because a Service instance is only metadata that describes the service, you have to set a ServiceManager to do some actual work. This is the link between the metadata and your implementation of a service, where the rubber meets the road. The DefaultServiceManager will instantiate the given SwitchPower class when an action which operates on the service has to be executed (this happens lazily, as late as possible). The manager will hold on to the instance and always re-use it as long as the service is registered with the UPnP stack. In other words, the service manager is the factory that instantiates your actual implementation of a UPnP service.
//以为服务的实例化仅仅是提供了一些描述服务的元数据,所以,你必须设置一个服务管理类来做一些实际工作。这是元数据和服务实施的链接,就像橡胶和马路的关系。当一个动作(action)操作这个服务必须被执行的时候,缺省服务管理类(DefaultServiceManager)将初始化一个电源开关类,这个行为发生有些懒,当万不得已的时候才干。随着服务被注册到UPnP堆栈,管理器保持实例化而且经常被重用。


Also note that LocalDevice is the interface that represents a UPnP device which is "local" to the running UPnP stack on the host. Any device that has been discovered through the network will be a RemoteDevice with RemoteService's, you typically do not instantiate these directly.
//你需要注意,本地设备是一个接口,这个接口代表本地的UPnP设备,在本地(Host)上运行的UPnP堆栈。任何网络发现的设备是远程设备,其服务也是远程服务,这个远程服务你不需要直接实例化它的。
A ValidationException will be thrown when the device graph you instantiated was invaild, you can call getErrors() on the exception to find out which property value of which class failed an integrity rule. The local service annotation binder will provide a LocalServiceBindingException if something is wrong with your annotation metadata on your service implementation class. An IOException can only by thrown by this particular Icon constructor, when it reads the resource file.
//如果你的实例化无效,那么一个有效性异常将被抛出。你可以调用用getErrors()来查找你的类那个属性值破坏了完整性规则。如果在你的服务实施类中你的注释元数据出现错误,局部服务的binder将提供LocalServiceBindingException。同样,如果你的图片资源提供出错,那么一个IOException将被这个特别的图标构造器抛出。


2.3. Running the server
//运行服务器
The Cling Core main API entry point is a thread-safe and typically single shared instance of UpnpService:
Cling核心的主要API入口点都是线程安全的,所以UpnpService可以被分享。


#-----------------------------------code start here--------------------------------------------------------------------------------
package example.binarylight;


import org.fourthline.cling.UpnpService;
import org.fourthline.cling.UpnpServiceImpl;
import org.fourthline.cling.binding.*;
import org.fourthline.cling.binding.annotations.*;
import org.fourthline.cling.model.*;
import org.fourthline.cling.model.meta.*;
import org.fourthline.cling.model.types.*;


import java.io.IOException;


public class BinaryLightServer implements Runnable {


    public static void main(String[] args) throws Exception {
        // Start a user thread that runs the UPnP stack
        Thread serverThread = new Thread(new BinaryLightServer());
        serverThread.setDaemon(false);
        serverThread.start();
    }


    public void run() {
        try {


            final UpnpService upnpService = new UpnpServiceImpl();


            Runtime.getRuntime().addShutdownHook(new Thread() {
                @Override
                public void run() {
                    upnpService.shutdown();
                }
            });


            // Add the bound local device to the registry
            upnpService.getRegistry().addDevice(
                    createDevice()
            );


        } catch (Exception ex) {
            System.err.println("Exception occured: " + ex);
            ex.printStackTrace(System.err);
            System.exit(1);
        }
    }


}
#-----------------------------------code end here--------------------------------------------------------------------------------


(The createDevice() method from the previous section should be added to this class.)
//前面章节createDevice()方法将被添加到这个类。
As soon as the UPnPServiceImpl is created, the stack is up and running. You always have to create a UPnPService instance, no matter if you write a client or a server. The UpnpService maintains a registry of all the discovered remote device on the network, and all the bound local devices. It manages advertisements for discovery and event handling in the background.
//一旦UPnPServiceImpl被生成,这个服务堆栈就就被建立和运行了。不论你是写一个客户还是服务器,你必须实例化一个UPnPService。这个UpnpService保持网络上发现的的设备以及所有本地设备的注册机。这个服务管理设备发现的广播,然后在后台处理。
You should shut down the UPnP service properly when your application quits, so that all other UPnP systems on your network will be notified that bound devices which are local to your application are no longer available. If you do not shut down the UpnpService when your application quits, other UPnP control points on your network might still show devices as available when they are in fact already gone.
//当你的应用程序停止,你应该关掉UPnP服务,这样,你的网络中其他的UPnP系统将被通知,你的应用程序所绑定的本地设备不存在了。如果你的应该程序终止了,然后你却没有关掉Upnp服务,尽管你的设备不存在了,但是你的网络中其他的UPnP控制点却还是看到你存在的。


The createDevice() method from the previous section is called here, as soon as the Registry of the local UPnP service stack is available.
//以太UPnP服务堆栈的注册机可用了,createDevice()方法立即被调用。
You can now compile and start this server, it should print some informational messages to your console and then wait for connections from UPnP control points. Use the Cling Workbench if you want to test your server immediately.
//你可以编译和运行这个服务器,这个程序会给你打印一些消息,提醒等待UPnP控制连的链接。你可以使用Cling Workbench来测试这个服务器。
2.4. Creating a control point


The client application has the same basic scaffolding as the server, it also uses a shared single instance of UpnpService:
//客户程序和服务器程序有同样的结构,它也是使用一个共享的单个UpnpService实例。


#-----------------------------------code start here--------------------------------------------------------------------------------
package example.binarylight;


import org.fourthline.cling.UpnpService;
import org.fourthline.cling.UpnpServiceImpl;
import org.fourthline.cling.controlpoint.*;
import org.fourthline.cling.model.action.*;
import org.fourthline.cling.model.message.*;
import org.fourthline.cling.model.message.header.*;
import org.fourthline.cling.model.meta.*;
import org.fourthline.cling.model.types.*;
import org.fourthline.cling.registry.*;


public class BinaryLightClient implements Runnable {


    public static void main(String[] args) throws Exception {
        // Start a user thread that runs the UPnP stack
        Thread clientThread = new Thread(new BinaryLightClient());
        clientThread.setDaemon(false);
        clientThread.start();


    }


    public void run() {
        try {


            UpnpService upnpService = new UpnpServiceImpl();


            // Add a listener for device registration events
            upnpService.getRegistry().addListener(
                    createRegistryListener(upnpService)
            );


            // Broadcast a search message for all devices
            upnpService.getControlPoint().search(
                    new STAllHeader()
            );


        } catch (Exception ex) {
            System.err.println("Exception occured: " + ex);
            System.exit(1);
        }
    }


}
#-----------------------------------code end here--------------------------------------------------------------------------------


Typically a control point sleeps until a device with a specific type of service becomes available on the network. The RegistryListener is called by Cling when a remote device has been discovered - or when it announced itself automatically. Because you usually do not want to wait for the periodic announcements of devices, a control point can also execute a search for all devices (or devices with certain service types or UDN), which will trigger an immediate discovery announcement from those devices that match the search query.
//通常控制点移植在休眠的状态,直到一个特定类型的服务在网络上出现。当一个远程设备是被发现,RegistryListener将被Cling调用。或者是它(?)自动公告它自己(谁)?因为通常你不需要等待设备公告周期,一个控制点可以执行发现所有设备的过程,或者是某种特定服务,或者特定UDN,这将触发立即发现公告,这个设备匹配这个查询。
You can already see the ControlPoint API here with its search(...) method, this is one of the main interfaces you interact with when writing a UPnP client with Cling.
//你已经看到了一个its search(...)的控制点API方法,这个方法是你和UPnP客户和Cling核心的主要接口。
If you compare this code with the server code from the previous section you can see that we are not shutting down the UpnpService when the application quits. This is not an issue here, because this application does not have any local devices or service event listeners (not the same as registry listeners) bound and registered. Hence, we do not have to announce their departure on application shutdown and can keep the code simple for the sake of the example.
//如果你和上面的服务器代码比较,你会发现,这个控制点程序停止了,UpnpService并没有被关闭。这不是问题,因为你的应用程序没有任何本地设备,也没有绑定本地服务器事件侦听器,也没有注册。因此当程序关闭,你不需要公告你的离开。因为是代码,为了简单的原因,我们就不写这些内容了。
Let's focus on the registry listener implementation and what happens when a UPnP device has been discovered on the network.
//让我们集中精力,研究一下注册机侦听实施以及当一个UPnP设备被发现的时候,将发生什么?
2.5. Executing an action
//执行一个动作
The control point we are creating here is only interested in services that implement SwitchPower. According to its template definition this service has the SwitchPower service identifier, so when a device has been discovered we can check if it offers that service:
//我们建立的控制点仅仅对实施SwitchPower的服务感兴趣。使用模板定义,我们建立的服务有SwitchPower标志,所以,当这个设备被发现,我们可以检查这个设备是否提供那个服务。
#-----------------------------------code start here--------------------------------------------------------------------------------
RegistryListener createRegistryListener(final UpnpService upnpService) {
    return new DefaultRegistryListener() {


        ServiceId serviceId = new UDAServiceId("SwitchPower");


        @Override
        public void remoteDeviceAdded(Registry registry, RemoteDevice device) {


            Service switchPower;
            if ((switchPower = device.findService(serviceId)) != null) {


                System.out.println("Service discovered: " + switchPower);
                executeAction(upnpService, switchPower);


            }


        }


        @Override
        public void remoteDeviceRemoved(Registry registry, RemoteDevice device) {
            Service switchPower;
            if ((switchPower = device.findService(serviceId)) != null) {
                System.out.println("Service disappeared: " + switchPower);
            }
        }


    };
}
#-----------------------------------code end here--------------------------------------------------------------------------------


If a service becomes available we immediately execute an action on that service. When a SwitchPower device disappears from the network a log message is printed. Remember that this is a very trivial control point, it executes a single a fire-and-forget operation when a service becomes available:
//当一个服务有效的时候,我们立即执行那个服务的动作。当这个SwitchPower设备消失的时候,我们仅仅打印一些消息。记住,这是一个非常琐碎的控制点,当一个服务出现时,这个任务是自动导航的。


#-----------------------------------code start here--------------------------------------------------------------------------------
void executeAction(UpnpService upnpService, Service switchPowerService) {


        ActionInvocation setTargetInvocation =
                new SetTargetActionInvocation(switchPowerService);


        // Executes asynchronous in the background
        upnpService.getControlPoint().execute(
                new ActionCallback(setTargetInvocation) {


                    @Override
                    public void success(ActionInvocation invocation) {
                        assert invocation.getOutput().length == 0;
                        System.out.println("Successfully called action!");
                    }


                    @Override
                    public void failure(ActionInvocation invocation,
                                        UpnpResponse operation,
                                        String defaultMsg) {
                        System.err.println(defaultMsg);
                    }
                }
        );


}


class SetTargetActionInvocation extends ActionInvocation {


    SetTargetActionInvocation(Service service) {
        super(service.getAction("SetTarget"));
        try {


            // Throws InvalidValueException if the value is of wrong type
            setInput("NewTargetValue", true);


        } catch (InvalidValueException ex) {
            System.err.println(ex.getMessage());
            System.exit(1);
        }
    }
}
#-----------------------------------code end here--------------------------------------------------------------------------------


The Action (metadata) and the ActionInvocation (actual call data) APIs allow very fine-grained control of how an invocation is prepared, how input values are set, how the action is executed, and how the output and outcome is handled. UPnP is inherently asynchronous, so just like the registry listener, executing an action is exposed to you as a callback-style API.
//动作(元数据)和动作调用(实际呼叫数据)API允许提供了细粒度控制这些行为,调用如何被准备,输入参数如何设置,动作如何执行,以及输出如何被处理。就像注册器侦听器,UPnP是内置异步的,所以执行动作是作为一个回调类型的API呈现给你的。


It is recommended that you encapsulate specific action invocations within a subclass of ActionInvocation, which gives you an opportunity to further abstract the input and output values of an invocation. Note however that an instance of ActionInvocation is not thread-safe and should not be executed in parallel by two threads.
//你需要使用ActionInvocation的子类来封装你的特别行动调用,这给你机会来抽象调用的输入输出值。然后,调用的实例不是线程安全的,不用同时运行两个调用线程。


The ActionCallback has two main methods you have to implement, one is called when the execution was successful, the other when it failed. There are many reasons why an action execution might fail, read the API documentation for all possible combinations or just print the generated user-friendly default error message.
//回调有两个你必须实施的方法,一个就是当调用成功,另一个就是调用失败。很多原因可以导致执行失败。你需要读API文档,来判断失败的原因组合,或者给用户打印出来错误信息。


2.6. Starting the application
//运行这个应用程序。


Compile the binary light demo application:
#----------------------------------command start here---------------------------------------------------------------
$ javac -cp /path/to/seamless-jar-files:/path/to/cling-core.jar
        -d classes/
        src/example/binarylight/BinaryLightServer.java
        src/example/binarylight/BinaryLightClient.java
        src/example/binarylight/SwitchPower.java
#----------------------------------command end here---------------------------------------------------------------


Don't forget to copy your icon.png file into the classes output directory as well, into the right package from which it is loaded as a reasource (the example.binarylight package if you followed the previous sections verbatim).
//不要忘记把icon.png复制到你的类输出目录,拷贝到资源加载的正确包,如果你逐句阅读上面的章节,你会发现这个包就是example.binarylight包
You can start the server or client first, which one doesn't matter as they will discover each other automatically:
先运行服务,还是先运行客户,你随便,因为他们将相互发现彼此。
#----------------------------------command start here---------------------------------------------------------------
$ java -cp /path/to/seamless-jar-files:/path/to/cling-core.jar:classes/
        example.binaryLight.BinaryLightServer


$ java -cp /path/to/seamless-jar-files:/path/to/cling-core.jar:classes/
        example.binaryLight.BinaryLightClient
#----------------------------------command end here---------------------------------------------------------------


You should see discovery and action execution messages on each console. You can stop and restart the applications individually (press CTRL+C on the console).
//你将在各自的终端上看到发现和动作执行的信息,你可以各自重启应用(在控制台上按CTRL+C)


2.7. Debugging and logging


Although the binary light is a very simple example, you might run into problems. Cling Core helps you resolve most problems with extensive logging. Internally, Cling Core uses Java JDK logging, also known as java.util.logging or JUL. There are no wrappers, logging frameworks, logging services, or other dependencies.


By default, the implementation of JUL in the Sun JDK will print only messages with level INFO, WARNING, or SEVERE on System.out, and it will print each message over two lines. This is quite inconvenient and ugly, so your first step is probably to configure one line per message. This requires a custom logging handler.


Next you want to configure logging levels for different logging categories. Cling Core will output some INFO level messages on startup and shutdown, but is otherwise silent during runtime unless a problem occurs - it will then log messages at WARNING or SEVERE level.


For debugging, usually more detailed logging levels for various log categories are required. The logging categories in Cling Core are package names, e.g the root logger is available under the name org.fourthline.cling. The following tables show typically used categories and the recommended level for debugging:
Network/Transport  
org.fourthline.cling.transport.spi.DatagramIO (FINE)
org.fourthline.cling.transport.spi.MulticastReceiver (FINE)
UDP communication
org.fourthline.cling.transport.spi.DatagramProcessor (FINER)
UDP datagram processing and content
org.fourthline.cling.transport.spi.UpnpStream (FINER)
org.fourthline.cling.transport.spi.StreamServer (FINE)
org.fourthline.cling.transport.spi.StreamClient (FINE)
TCP communication
org.fourthline.cling.transport.spi.SOAPActionProcessor (FINER)
SOAP action message processing and content
org.fourthline.cling.transport.spi.GENAEventProcessor (FINER)
GENA event message processing and content
org.fourthline.cling.transport.impl.HttpHeaderConverter (FINER)
HTTP header processing


UPnP Protocol  
org.fourthline.cling.protocol.ProtocolFactory (FINER)
org.fourthline.cling.protocol.async (FINER)
Discovery (Notification & Search)
org.fourthline.cling.protocol.ProtocolFactory (FINER)
org.fourthline.cling.protocol.RetrieveRemoteDescriptors (FINE)
org.fourthline.cling.protocol.sync.ReceivingRetrieval (FINE)
org.fourthline.cling.binding.xml.DeviceDescriptorBinder (FINE)
org.fourthline.cling.binding.xml.ServiceDescriptorBinder (FINE)
Description
org.fourthline.cling.protocol.ProtocolFactory (FINER)
org.fourthline.cling.protocol.sync.ReceivingAction (FINER)
org.fourthline.cling.protocol.sync.SendingAction (FINER)
Control
org.fourthline.cling.model.gena (FINER)
org.fourthline.cling.protocol.ProtocolFactory (FINER)
org.fourthline.cling.protocol.sync.ReceivingEvent (FINER)
org.fourthline.cling.protocol.sync.ReceivingSubscribe (FINER)
org.fourthline.cling.protocol.sync.ReceivingUnsubscribe (FINER)
org.fourthline.cling.protocol.sync.SendingEvent (FINER)
org.fourthline.cling.protocol.sync.SendingSubscribe (FINER)
org.fourthline.cling.protocol.sync.SendingUnsubscribe (FINER)
org.fourthline.cling.protocol.sync.SendingRenewal (FINER)
GENA


Core  
org.fourthline.cling.transport.Router (FINER)
Message Router
org.fourthline.cling.registry.Registry (FINER)
org.fourthline.cling.registry.LocalItems (FINER)
org.fourthline.cling.registry.RemoteItems (FINER)
Registry
org.fourthline.cling.binding.annotations (FINER)
org.fourthline.cling.model.meta.LocalService (FINER)
org.fourthline.cling.model.action (FINER)
org.fourthline.cling.model.state (FINER)
org.fourthline.cling.model.DefaultServiceManager (FINER)
Local service binding & invocation
org.fourthline.cling.controlpoint (FINER)
Control Point interaction


One way to configure JUL is with a properties file. For example, create the following file as mylogging.properties:


# Enables a one-message-per-line handler (shipping in seamless-util.jar)
handlers=org.seamless.util.logging.SystemOutLoggingHandler


# The default (root) log level
.level=INFO


# Extra settings for various categories


org.fourthline.cling.level=INFO


org.fourthline.cling.protocol.level=FINEST


org.fourthline.cling.registry.Registry.level=FINER
org.fourthline.cling.registry.LocalItems.level=FINER
org.fourthline.cling.registry.RemoteItems.level=FINER 


You can now start your application with a system property that names your logging configuration:


$ java -cp /path/to/seamless-jar-files:/path/to/cling-core.jar:classes/
        -Djava.util.logging.config.file=/path/to/mylogging.properties
        example.binaryLight.BinaryLightServer


You should see the desired log messages printed on System.out.




3. The Cling Core API
//Cling核心API
The programming interface of Cling is fundamentally the same for UPnP clients and servers. The single entry point for any program is the UpnpService instance. Through this API you access the local UPnP stack, and either execute operations as a client (control point) or provide services to local or remote clients through the registry.
//


The following diagram shows the most important interfaces of Cling Core:
API Overview


You'll be calling these interfaces to work with UPnP devices and interact with UPnP services. Cling provides a fine-grained meta-model representing these artifacts:
Metamodel Overview
//
In this chapter we'll walk through the API and metamodel in more detail, starting with the UpnpService.
//
3.1. Working with a UpnpService


The UpnpService is an interface:
//UpnpService是一个接口类
public interface UpnpService {


    public UpnpServiceConfiguration getConfiguration();
    public ProtocolFactory getProtocolFactory();
    public Router getRouter();


    public ControlPoint getControlPoint();
    public Registry getRegistry();


    public void shutdown();


}


An instance of UpnpService represents a running UPnP stack, including all network listeners, background maintenance threads, and so on. Cling Core bundles a default implementation which you can simply instantiate as follows:
//UpnpService的实例化代表一个运行的UPnP协议堆栈,包括网络监听,后台维护线程等等。Cling Core绑定一个缺省实施,这个实施可以如下完成:
UpnpService upnpService = new UpnpServiceImpl();


With this implementation, the local UPnP stack is ready immediately, it listens on the network for UPnP messages. You should call the shutdown() method when you no longer need the UPnP stack. The bundled implementation will then cut all connections with remote event listeners and also notify all other UPnP participants on the network that your local services are no longer available. If you do not shutdown your UPnP stack, remote control points might think that your services are still available until your earlier announcements expire.
//有了这个实施,本地的UPnP协议堆栈就立即准备好了,它就开始侦听网络的UPnP消息了。当你不在需要UPnP协议堆栈的时候,你需要调用shutdown信息来关闭。这个批量的实施将切断它和所有的远程侦听器的联系,也通知所有UPnP参与者,你的本地UPnP堆栈已经不再存在了。如果你不关闭你的UPnP协议堆栈,远程控制点还可能认为你的服务仍然有效,知道你的早期声明过期了。


The bundled implementation offers two additional constructors:
//这个批量的实施提供如下两个附加构造器。


UpnpService upnpService =
    new UpnpServiceImpl(RegistryListener... registryListeners);


This constructor accepts your custom RegistryListener instances, which will be activated immediately even before the UPnP stack listens on any network interface. This means that you can be notified of all incoming device and service registrations as soon as the network stack is ready. Note that this is rarely useful, you'd typically send search requests after the stack is up and running anyway - after adding listeners to the registry.


//


The second constructor supports customization of the UPnP stack configuration:


UpnpService upnpService =
    new UpnpServiceImpl(new DefaultUpnpServiceConfiguration(8081));


This example configuration will change the TCP listening port of the UPnP stack to 8081, the default being an ephemeral (system-selected free) port. The UpnpServiceConfiguration is also an interface, in the example above you can see how the bundled default implementation is instantiated.


The following section explain the methods of the UpnpService interface and what they return in more detail.


3.1.1. Customizing configuration settings


This is the configuration interface of the default UPnP stack in Cling Core, an instance of which you have to provide when creating the UpnpServiceImpl:


public interface UpnpServiceConfiguration {


    // NETWORK
    public NetworkAddressFactory createNetworkAddressFactory();


    public StreamClient createStreamClient();
    public StreamServer createStreamServer(NetworkAddressFactory naf);


    public MulticastReceiver createMulticastReceiver(NetworkAddressFactory naf);
    public DatagramIO createDatagramIO(NetworkAddressFactory naf);


    // PROCESSORS
    public DatagramProcessor getDatagramProcessor();
    public SOAPActionProcessor getSoapActionProcessor();
    public GENAEventProcessor getGenaEventProcessor();


    // DESCRIPTORS
    public DeviceDescriptorBinder getDeviceDescriptorBinderUDA10();
    public ServiceDescriptorBinder getServiceDescriptorBinderUDA10();


    // EXECUTORS
    public Executor getMulticastReceiverExecutor();
    public Executor getDatagramIOExecutor();
    public Executor getStreamServerExecutor();
    public Executor getAsyncProtocolExecutor();
    public Executor getSyncProtocolExecutor();
    public Executor getRegistryMaintainerExecutor();
    public Executor getRegistryListenerExecutor();


    // REGISTRY
    public Namespace getNamespace();
    public int getRegistryMaintenanceIntervalMillis();
    ...


}


This is quite an extensive SPI but you typically won't implement it from scratch. Overriding and customizing the bundled DefaultUpnpServiceConfiguration should suffice in most cases.


The configuration settings reflect the internal structure of Cling Core:


Network


    The NetworkAddressFactory provides the network interfaces, ports, and multicast settings which are used by the UPnP stack. At the time of writing, the following interfaces and IP addresses are ignored by the default configuration: any IPv6 interfaces and addresses, interfaces whose name is "vmnet*", "vnic*", "*virtual*", or "ppp*", and the local loopback. Otherwise, all interfaces and their TCP/IP addresses are used and bound.


    You can set the system property org.fourthline.cling.network.useInterfaces to provide a comma-separated list of network interfaces you'd like to bind exclusively. Additionally, you can restrict the actual TCP/IP addresses to which the stack will bind with a comma-separated list of IP address provided through the org.fourthline.cling.network.useAddresses system property.


    Furthermore, the configuration produces the network-level message receivers and senders, that is, the implementations used by the network Router.


    Stream messages are TCP/HTTP requests and responses, the default configuration will use the Sun JDK 6.0 webserver to listen for HTTP requests, and it sends HTTP requests with the standard JDK HttpURLConnection. This means there are by default no additional dependencies on any HTTP server/library by Cling Core. However, if you are trying to use Cling Core in a runtime container such as Tomcat, JBoss AS, or Glassfish, you might run into an error on startup. The error tells you that Cling couldn't use the Java JDK's HTTPURLConnection for HTTP client operations. This is an old and badly designed part of the JDK: Only "one application" in the whole JVM can configure URL connections. If your container is already using the HTTPURLConnection, you have to switch Cling to an alternative HTTP client. See Configuring network transports for other available options and how to change various network-related settings.


    UDP unicast and multicast datagrams are received, parsed, and send by a custom implementation bundled with Cling Core that does not require any particular Sun JDK classes, they should work an all platforms and in any environment.
Processors


    The payload of SSDP datagrams is handled by a default processor, you rarely have to customize it. SOAP action and GENA event messages are also handled by configurable processors, you can provide alternative implementations if necessary, see Switching XML processors. For best interoperability with other (broken) UPnP stacks, consider switching from the strictly specification-compliant default SOAP and GENA processors to the more lenient alternatives.
Descriptors


    Reading and writing UPnP XML device and service descriptors is handled by dedicated binders, see Switching descriptor XML binders. For best interoperability with other (broken) UPnP stacks, consider switching from the strictly specification-compliant default binders to the more lenient alternatives.
Executors


    The Cling UPnP stack is multi-threaded, thread creation and execution is handled through java.util.concurrent executors. The default configuration uses a pool of threads with a maximum size of 64 concurrently running threads, which should suffice for even very large installations. Executors can be configured fine-grained, for network message handling, actual UPnP protocol execution (handling discovery, control, and event procedures), and local registry maintenance and listener callback execution. Most likely you will not have to customize any of these settings.
Registry


    Your local device and service XML descriptors and icons can be served with a given Namespace, defining how the URL paths of local resources is constructed. You can also configure how frequently Cling will check its Registry for outdated devices and expired GENA subscriptions.


There are various other, rarely needed, configuration options available for customizing Cling's behavior, see the Javadoc of UpnpConfiguration.
3.1.2. The protocol factory
//协议工厂
Cling Core internals are modular and any aspect of the UPnP protocol is handled by an implementation (class) which can be replaced without affecting any other aspect. The ProtocolFactory provides implementations, it is always the first access point for the UPnP stack when a message which arrives on the network or an outgoing message has to be handled:
//Cling Core内部是模块化的,因此,UPnP协议的任意方面...
//协议工厂提供了实施,当一个协议到达网络,或者一个外发信息被处理,这是第一个访问点。
public interface ProtocolFactory {


    public ReceivingAsync createReceivingAsync(IncomingDatagramMessage message)
                            throws ProtocolCreationException;;


    public ReceivingSync createReceivingSync(StreamRequestMessage requestMessage);
                            throws ProtocolCreationException;;


    public SendingNotificationAlive createSendingNotificationAlive(LocalDevice ld);
    public SendingNotificationByebye createSendingNotificationByebye(LocalDevice ld);
    public SendingSearch createSendingSearch(UpnpHeader searchTarget);
    public SendingAction createSendingAction(ActionInvocation invocation, URL url);
    public SendingSubscribe createSendingSubscribe(RemoteGENASubscription s);
    public SendingRenewal createSendingRenewal(RemoteGENASubscription s);
    public SendingUnsubscribe createSendingUnsubscribe(RemoteGENASubscription s);
    public SendingEvent createSendingEvent(LocalGENASubscription s);
    
}


This API is a low-level interface that allows you to access the internals of the UPnP stack, in the rare case you need to manually trigger a particular procedure.
//这个API是一些低级接口,通过这些接口,你可以访问UPnP协议栈的内部,不过,你实际上很少主动触发这些例程的。


The first two methods are called by the networking code when a message arrives, either multicast or unicast UDP datagrams, or a TCP (HTTP) stream request. The default protocol factory implementation will then pick the appropriate receiving protocol implementation to handle the incoming message.
//


The local registry of local services known to the UPnP stack naturally also sends messages, such as ALIVE and BYEBYE notifications. Also, if you write a UPnP control point, various search, control, and eventing messages are send by the local UPnP stack. The protocol factory decouples the message sender (registry, control point) from the actual creation, preparation, and transmission of the messages.
//UPnP堆栈的本地服务的本地注册机也会发送消息,例如活动的和再见通知。


Transmission and reception of messages at the lowest-level is the job of the network Router.


3.1.3. Accessing low-level network services
//访问低层网络服务
The reception and se