3、运行结果

一、UPnP简介

1、 WCF分布式事务例子

这里也用转账的例子说事。

用户在系统A和系统B都有账户,账户间的资金可以互转,系统A的资金减少多少,系统B的相应账户的资金就增加多少。

系统A机器上有数据库AccountA,系统B机器上有数据库AccountB,数据库的结构一样,都有一个数据表Account,结构如下:

字段

数据类型

含义

depositorID

int

账户id

amount

decimal(18, 2)

金额

 

为了演示TxF事务性文件,在系统B中增加了一个写文件的操作,记录本次转账操作的信息。转账的所有操作步骤:系统A上账户上减少金额,系统B上记录转账信息文件,系统B上相应账户资金增加这三个操作都在一个事务流中,要么全部完成,要么全部回滚。

系统A和系统B分别在服务器A和服务器B上。系统A在账户上减少金额后调用系统B的WCF服务,在系统B中继续增加账户资金,生成转账信息文件。

下面开始这个例子的完整过程。

Build并运行上述程序,从Memo1里可以看到整个工作过程,可以看出,搜索设备、取控制页URL、增加端口映射项、查看映射项参数、删除映射项等关键步骤均符合我们预期的设想。因此,完全可以在您的应用中把本程序的有关内容集成进去。

UPnP(Universal Plug and
Play)技术是一种屏蔽各种数字设备的硬件和操作系统的通信协议。它是一种数字网络中间件技术,建立在TCP/IP、HTTP协议之上,采用XML来描述设备和控制信息。这种技术最开始是被微软,因特尔等公司使用在数字家庭中的,用来在家庭网络中完成多媒体资源共享,家电一体化等功能,例如在各种家电上实现IP数字化,加入网络传输和控制部分,从而让家电能组建UPnP网络,这种技术能让人们在上班或者外地出差时能查看家庭情况,如:是否有家电忘记关闭;监控家庭情况;或者在回家之前打开家里的热水器并设置水温;在天热时提前打开空调;打开电饭煲开始蒸饭……

1.1.   建立系统B转账WCF服务

建立服务契约:

[ServiceContract]

public interface IAccountB

{

    [OperationContract]

    [TransactionFlow(TransactionFlowOption.Allowed)]

    void deposit(int depositorid, double amount);

}

服务契约就一个方法deposit,其中depositorid表示账户id,amount表示要从系统A转账到系统B的金额。

TransactionFlow这个属性指示operation是否跟随调用端的事务流,参数含义:

TransactionFlowOption.NotAllowed – 表示此operation不跟随传入的事务流,不参与分布式事务。

TransactionFlowOption.Allowed – 表示此opreation可以跟随传入的事务流,如果有传入的事务流则参与,如果没有传入的事务流则不参与,但是可以启动本地的事务。

TransactionFlowOption.Mandatory – 表示此operation必须跟随传入的事务,参与分布式事务,如果调用此operation的客户端没有事务流则抛出异常。

 

下面是服务实现:

[ServiceBehavior(InstanceContextMode =
InstanceContextMode.Single)]

public class AccountBService : IAccountB

{

    [OperationBehavior(TransactionScopeRequired
= true)]

    public void
deposit(int depositorid, double amount)

    {

       
#region 新建事务性文件

        string path = @”c:test.txt”;

        FileStream fs = TransactedFile.Open(path, System.IO.FileMode.Create,

           
System.IO.FileAccess.ReadWrite, System.IO.FileShare.ReadWrite);

        string fileContent = string.Format(“从系统A转账到系统Brn用户ID:{0}rn转账金额为:{1}”,           
depositorid.ToString(), amount.ToString());

        byte[] byteArrar = Encoding.UTF8.GetBytes(fileContent);

       
fs.Write(byteArrar, 0, byteArrar.Count());

       
fs.Flush();

       
fs.Close();

       
#endregion

 

       
#region 数据访问,在指定账户上增加存款

        string connstr = ConfigurationManager.ConnectionStrings[“ConnStr”].ToString();

        SqlCommand mySqlCommand = new SqlCommand(“update account set amount = amount +        @amount where depositorid =
@depositorid “);

       
mySqlCommand.Connection = new
SqlConnection(connstr);

        SqlParameter par1 = new SqlParameter(“@amount”, SqlDbType.Decimal);

        par1.Value =
amount;

       
mySqlCommand.Parameters.Add(par1);

        par1 = new SqlParameter(“@depositorid”, SqlDbType.Int);

        par1.Value =
depositorid;

       
mySqlCommand.Parameters.Add(par1);

       
mySqlCommand.Connection.Open();

       
mySqlCommand.ExecuteNonQuery();

       
mySqlCommand.Connection.Close();

       
#endregion

     }

}

服务实现了deposit操作。

[OperationBehavior(TransactionScopeRequired
= true)],这里的TransactionScopeRequired = true表示这个操作在TransactionScope内执行,加上前面OperationContract上的TransactionFlowOption.Allowed 允许跟随事务的设置,这个deposit的操作将会参与客户端发起的分布式事务。

实现的deposit操作中完成两个任务,先转账信息写入c:test.txt文件,这里写文件操作使用了TxF事务性文件操作TransactedFile.Open,关于TxF的操作部分的代码微软有提供,在本文中提供的代码中包含了这部分源码。使用事务性文件操作,在事务中的其他事务资源操作失败后,文件操作也会回滚。

 


这些应用由于家电厂商的不积极(目前能连接到网络的家电可能只有电视吧),或许是成本问题等,UPnP设计之初想要达到的数字家庭并未得到普及和广泛应用。但是UPnP的特有特性还是很吸引人的,它最大的特性是它的消息发送是通过HTTP协议发送,所有的消息内容都是通过XML包装的,这种消息传输模式使得我们能通过浏览器访问和控制支持UPnP协议的设备,再来让我们看看传统的嵌入式网络的通信吧,大多数设备之间网络通信直接使用TCP/IP上的socket编程,这种通信模式需要通信的双方制定好通信数据的格式包,在客户端和服务器端都需要进行编程,而且一旦项目需求发生变化,通信格式发生变化时,这时通信两端的程序都需要修改,如果客户还提出跨平台的问题,那需要修改的就更多了。UPnP协议本身是利用的现有的通用协议而来的,它占用资源少,特别适用于嵌入式网络领域,能通过浏览器访问和控制设备的特性使得UPnP的编程通常只涉及一方,即设备端。通信数据格式的修改和客户需求的变化也只涉及到设备端的修改,它便于设备的升级,UPnP设备在添加到网络中时,就可以立即被网络中拥有浏览器的客户端访问和控制。

1.2.   建立系统A转账客户端

系统A是个Console应用:

static void Main(string[] args)

{

  ChannelFactory<IAccountB> myFactory = new ChannelFactory<IAccountB>(“endpointConfig”);

  IAccountB myClient =
myFactory.CreateChannel();

  Double amount = 500;

  int depositorid = 1;

  using (TransactionScope scop = new TransactionScope())

  {

   
#region 数据访问,在指定账户上减少存款

    string connstr = ConfigurationManager.ConnectionStrings[“ConnStr”].ToString();

    SqlCommand mySqlCommand = new SqlCommand(“update account set amount = amount – @amount
where depositorid = @depositorid “);

   
mySqlCommand.Connection = new
SqlConnection(connstr);

    SqlParameter par1 = new SqlParameter(“@amount”, SqlDbType.Decimal);

    par1.Value =
amount;

   
mySqlCommand.Parameters.Add(par1);

    par1 = new SqlParameter(“@depositorid”, SqlDbType.Int);

    par1.Value =
depositorid;

   
mySqlCommand.Parameters.Add(par1);

   
mySqlCommand.Connection.Open();

   
mySqlCommand.ExecuteNonQuery();

   
mySqlCommand.Connection.Close();

   
#endregion

    try

    {

     
myClient.deposit(depositorid, amount);

     
scop.Complete();

    }

    catch (Exception e)

    {

      Transaction.Current.Rollback();

    }

  }

}

 

(1)、以下是“搜索设备”的工作过程信息:

二、UPnP的组成

1.3.   配置使用OleTransactions协议

先测试使用OleTransactions分布式事务协议,下面是使用OleTransactions时在客户端和服务端需要的服务和配置。


UPnP网络是指通过采用UPnP协议的设备所组成的网络,它的基本组件是服务、设备和控制点:

1.3.1.   配置文件

系统B上WCF服务的配置文件:

澳门新葡萄京官网注册,<?xml version=”1.0″ encoding=”utf-8″
?>

<configuration>

  <system.web>

    <compilation debug=”true” />

  </system.web>

  <connectionStrings>

    <add name=”ConnStr” connectionString=”Server=.;Integrated security=true;initial
catalog=AccountB” />

  </connectionStrings>

  <system.serviceModel>

    <bindings>

     
<customBinding>

       
<binding name=”customBindingConfig”>

         
<transactionFlow transactionProtocol=”OleTransactions” />

          <httpTransport />

       
</binding>

     
</customBinding>

   
</bindings>

    <services>

     
<service behaviorConfiguration=”WCF_ATTransTest.Service.Service1Behavior”

         
name=”WCF_ATTransTest.Service.AccountBService”>

       
<endpoint address=”” binding=”customBinding”
bindingConfiguration=”customBindingConfig”

           
name=”serviesEndpoint” contract=”WCF_ATTransTest.Service.IAccountB” />

       
<host>

         
<baseAddresses>

           
<add baseAddress=”” />

         
</baseAddresses>

       
</host>

     
</service>

   
</services>

    <behaviors>

     
<serviceBehaviors>

       
<behavior name=”WCF_ATTransTest.Service.Service1Behavior”>

         
<serviceMetadata httpGetEnabled=”true”/>

         
<serviceDebug includeExceptionDetailInFaults=”true” />

       
</behavior>

     
</serviceBehaviors>

   
</behaviors>

  </system.serviceModel>

</configuration>

系统A
WCF客户端的配置文件

<?xml version=”1.0″ encoding=”utf-8″
?>

<configuration>

  <connectionStrings>

    <add name=”ConnStr” connectionString=”Server=.;Integrated security=true;initial
catalog=AccountA” />

  </connectionStrings>

  <system.serviceModel>

    <client>

     
<endpoint address=””

         
binding=”customBinding” bindingConfiguration=”customBindingConfig”

         
contract=”WCF_ATTransTest.Service.IAccountB” name =”endpointConfig”/>

   
</client>

    <bindings>

     
<customBinding>

       
<binding name=”customBindingConfig”>

         
<transactionFlow transactionProtocol=”OleTransactions” />

         
<httpTransport />

       
</binding>

     
</customBinding>

   
</bindings>

  </system.serviceModel>

</configuration>

 

发出了如下命令:
M-SEARCH * HTTP/1.1
HOST: 239.255.255.250:1900
MAN: “ssdp:discover”
MX: 3
ST: upnp:rootdevice

服务是UPnP网络中最小逻辑功能单元,由状态表、事件和控制服务器组成。状态表描述服务的当前状态;事件服务器用来管理来自控制点的事件订阅请求,并在服务的状态改变时向订阅者发送状态更新事件;而控制服务器则负责处理来自控制点的动作请求。

1.3.2.   配置RPC

使用OleTx协议时,要通过RPC协议在DTC事务管理器之间通讯,RPC本身使用135端口,还要使用数量不定的1024端口以上的动态端口。所以防火墙必须要开放135端口和1024端口以后的所有端口。

但是要开放所有1024以上的端口又是很危险的事情,最好能把RPC使用的端口限制在一定的范围,然后防火墙只开放这个范围的端口。

可以通过修改注册表来达到这个目的:

运行Regedt32.exe打开注册表,在以下注册表项下添加 Internet
项:HKEY_LOCAL_MACHINESoftwareMicrosoftRpc,在 Internet
项下,添加值“Ports”(MULTI_SZ)、“PortsInternetAvailable”(REG_SZ)
和“UseInternetPorts”(REG_SZ)。

在本示例中,使用了端口 5000

5100(含 5000

5100),因此该新注册表项将显示为以下形式:

Ports:REG_MULTI_SZ: 5000-5100

PortsInternetAvailable:REG_SZ:Y

UseInternetPorts:REG_SZ:Y

      
澳门新葡萄京官网注册 1

在所有参与事务的机器上都必须启动RPC服务,并配置RPC使用的动态端口,防火墙开放这些动态端口。

从192.168.0.1:1900收到289个字节的数据!
收到的信息如下:
HTTP/1.1 200 OK
Cache-Control: max-age=1800
Date: Thu, 01 Jan 1970 00:37:12 GMT
Ext:
Location:
Server: os/ver 2.0 UPnP/1.0 router/(5+20).1.2.99
ST: upnp:rootdevice
USN: uuid:7fff6211-b0fa-4075-a15c-0b624d01bfb4::upnp:rootdevice

UPnP设备由一组子设备或服务构成,同时多个设备下的子设备也通过逻辑组合构成新的设备。设备中的每个功能也可以当做一个逻辑子设备向外发布。

1.3.3.   DTC服务

发起事务的服务器的DTC服务必须启动,参与事务的服务器的DTC服务可以不启动。

在事务内要调用跨越进程或机器的服务,事务需要提升级别到DTC管理器,如果发起事务的服务器的DTC服务未启动,则会抛出异常:“试图提升事务时失败。服务器xxx上的MSDTC不可用。”

 

控制点的功能是发现和控制其它设备,在控制点发现一个网络设备后,它会发出请求先获取设备描述和服务列表,再获取感兴趣的服务描述,然后通过动作请求来控制服务。控制点订阅设备的服务事件时,服务会在状态变化时向控制点发送的状态更新事件。一个控制点可以控制多个UPnP设备,也可以作为一个设备被其它控制点控制。

1.3.4.   测试

将系统B的WCF服务发布至IIS,系统A调用WCF的地址指向系统B发布的服务地址,测试结果,发送到WCF的消息和返回的消息:

<s:Envelope xmlns:s=”” xmlns:a=”;

  <s:Header>

    <a:Action s:mustUnderstand=”1″>;

    <a:MessageID>urn:uuid:6d8931ab-f79e-4de8-a377-4d8acdcf3545</a:MessageID>

    <a:ReplyTo>

     
<a:Address>;

   
</a:ReplyTo>

    <OleTxTransaction s:mustUnderstand=”1″ xmlns=”;

     
<PropagationToken>     
AQAAAAMAAACxK7vUESf1RJ8BoxrUWJUvAAAQAAAAAABsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGU0NDljY2MzLWY3OWQtNDVjMi05YjQzLTI1OTRmY2JhZTQ2NwAALAAHAAAAZM1kzSEAAABXSU43LVBDABAAAABXAEkATgA3AC0AUABDAAAAAQAAAAAAAAAOAAAAdGlwOi8vV2luNy1QQy8AAA==

     
</PropagationToken>

    </OleTxTransaction>

    <a:To s:mustUnderstand=”1″>;

  </s:Header>

  <s:Body>

    <deposit xmlns=”;

     
<depositorid>1</depositorid>

     
<amount>500</amount>

   
</deposit>

  </s:Body>

</s:Envelope>

注意在请求消息的header中有<OleTxTransaction>标签,这个标签用来标示采用OleTxTransaction分布式事务协议。

<s:Envelope xmlns:s=”” xmlns:a=”;

  <s:Header>

    <a:Action s:mustUnderstand=”1″>;

    <a:RelatesTo>urn:uuid:6d8931ab-f79e-4de8-a377-4d8acdcf3545</a:RelatesTo>

  </s:Header>

  <s:Body>

    <depositResponse xmlns=”;

  </s:Body>

</s:Envelope>

 

*******************************************
Location=
Device IP=192.168.0.1
Device Port=1780
Server=os/ver 2.0 UPnP/1.0 router/(5+20).1.2.99
USN=uuid:7fff6211-b0fa-4075-a15c-0b624d01bfb4::upnp:rootdevice
*******************************************

三、UPnP的工作过程

1.4.   配置使用WS-AtomicTransaction协议

 

澳门新葡萄京官网注册 2

1.4.1.   配置文件

将服务端和客户端的配置文件中的:

         
<transactionFlow transactionProtocol=”OleTransactions” />

改成:

         
<transactionFlow transactionProtocol=”WSAtomicTransactionOctober2004″ />


UPnP的工作过程

1.4.2.   配置WS-AT

要让DTC支持WS-AT协议需要配置参与分布式事务的所有机器的证书,安装WS-AT的DTC的UI等等,配置过程还比较复杂,详细步骤参见微软文档:配置 WS-Atomic
事务支持()

配置好的界面如下:

澳门新葡萄京官网注册 3

 

WS-AT事务管理器之间的通讯使用SSL安全通道传输,所以每台参与WS-AT事务的机器都必须配置证书和相应的https的端口。

WS-AT的UI界面虽然安装在DTC中,但是WS-AT事务并不依赖于DTC服务,所以所有参与WS-AT事务的机器的DTC服务不必启动。

(2)、以下是“获取控制页URL”的工作过程信息:

澳门新葡萄京官网注册 4

1.4.3.   测试

抓通讯数据包,这时的消息如下:

<s:Envelope xmlns:s=”” xmlns:a=”;

  <s:Header>

    <a:Action s:mustUnderstand=”1″>;

    <a:MessageID>urn:uuid:1c111341-b7ef-4e21-91b9-a00de9aa8eea</a:MessageID>

    <a:ReplyTo>

     
<a:Address>;

   
</a:ReplyTo>

    <CoordinationContext s:mustUnderstand=”1″ xmlns=”” xmlns:mstx=”;

     
<wscoor:Identifier xmlns:wscoor=”;

     
<Expires>3600000</Expires>

     
<CoordinationType>;

     
<RegistrationService>

       
<Address xmlns=”;

       
<ReferenceParameters xmlns=”;

         
<mstx:RegisterInfo>

           
<mstx:LocalTransactionId>c7932c29-6b96-4961-b6d5-ce3507a54f1e</mstx:LocalTransactionId>

         
</mstx:RegisterInfo>

       
</ReferenceParameters>

     
</RegistrationService>

     
<mstx:IsolationLevel>0</mstx:IsolationLevel>

     
<mstx:LocalTransactionId>c7932c29-6b96-4961-b6d5-ce3507a54f1e</mstx:LocalTransactionId>

     
<PropagationToken xmlns=”;

   
</CoordinationContext>

    <a:To s:mustUnderstand=”1″>;

  </s:Header>

  <s:Body>

    <deposit xmlns=”;

     
<depositorid>1</depositorid>

     
<amount>500</amount>

   
</deposit>

  </s:Body>

</s:Envelope>

可以看出使用WS-AT协议时,消息的header部分比使用OleTransactions协议时复杂的多,注意header部分里的:

<Address xmlns=”;

这是发起WS-AT事务机器的WS-AT事务管理器的地址,参与WS-AT事务的别的机器都要向这个地址来注册本地事务和报告本地事务的完成情况,以便WS-AT事务管理器协调整个分布式事务,来决定提交整个事务还是回滚整个事务。

返回消息:

<s:Envelope xmlns:s=”” xmlns:a=”;

  <s:Header>

    <a:Action s:mustUnderstand=”1″>;

    <a:RelatesTo>urn:uuid:1c111341-b7ef-4e21-91b9-a00de9aa8eea</a:RelatesTo>

  </s:Header>

  <s:Body>

    <depositResponse xmlns=”;

  </s:Body>

</s:Envelope>

 

但是在抓数据包是发现一个问题,就是从消息的header看是显示使用WS-AT协议,但是从抓的数据包看依然是使用的OleTransactions协议处理的事务,通过RPC协议协调事务,下面是部分截图:

       
澳门新葡萄京官网注册 5

DCERPC显示是RPC协议,ctx_id: 1
906b0ce0-c70b-1067-b317-00dd010662da显示了事务id。

原来由于OleTransactions协议的开销远比WS-AT协议小,所以微软的默认设计是即使指定使用WS-AT协议,仍然义无反顾的使用OleTransactions协议。所以这时你的机器上要是DTC和RPC的端口没有配置好,就会报错。

要想真正使用WS-AT协议需要手工在注册表中增加一个key:

在注册表HKLMSOFTWAREMicrosoftWSAT3.0下增加一个名为“OleTxUpgradeEnabled”的DWord值,这个key表示在指定WS-AT协议时是否升级使用OleTransactions协议,0表示不升级(即表示使用WS-AT协议),1表示升级(即表示使用OleTransactions协议),默认是1,所以默认时即使你指定使用WS-AT协议,实际上还是被OleTransactions协议替换了。设置为0即可使用WS-AT协议了。

使用WS-AT协议时,事务协调不依赖于DTC,也不走RPC协议,只通过WS-AT配置中的https端口通过SSL进行事务管理器之间的通讯。WS-AT协议是业界标准,只要实现了这个协议的系统都能进行互操作,当然不能依赖于微软的专有RPC协议。

 

代码下载:

WCF-ATTransTest客户端.rar

WCF-ATTransTest服务端.rar


UPnP协议栈

向192.168.0.1:1780设备发起连接…

四、UPnP的描述文档

与192.168.0.1:1780设备成功连接

在UPnP的工作是基于UPnP描述文件的。UPnP描述文件分为设备描述文件和服务描述文件。

向192.168.0.1:1780提交如下查询请求:
GET /InternetGatewayDevice.xml HTTP/1.1
Host: 192.168.0.1:1780

设备描述文件包括设备属性和它所提供的服务,一个标准设备描述文档如下所示:

 

  1. <?xml version=”1.0″?>  
  2. <root xmlns=”urn:schemas-upnp-org:XXXXXX”>  
  3.  ……  
  4.   <device>  
  5.     <deviceType>urn:schemas-upnp-org:device: XXXXXX: X </deviceType>  
  6.  ……  
  7.     <UDN>uuid:XXXXXX</UDN>  
  8.     <UPC>……</UPC>  
  9.     <serviceList>  
  10.       <service>…… </service>  
  11.     </serviceList>  
  12.    <presentationURL>/设备展示URL地址</presentationURL>  
  13. </device>  
  14. </root>

从192.168.0.1:1780收到如下应答数据:
HTTP/1.1 200 OK
Content-Type: text/xml
Content-Length: 3778
Connection: close
Pragma: no-cache

澳门新葡萄京官网注册 6

<?xml version=”1.0″?>
<root xmlns=”urn:schemas-upnp-org:device-1-0″>
 <specVersion>
  <major>1</major>
  <minor>0</minor>
 </specVersion>
 <URLBase></URLBase>
 <device>
  <deviceType>urn:schemas-upnp-org:device:InternetGatewayDevice:1</deviceType>
  <friendlyName>DI-504</friendlyName>
  <manufacturer>D-Link Sytems, Inc.</manufacturer>
  <manufacturerURL>;
  <modelDeion>Internet Access Router</modelDeion>
  <modelName>DI-504</modelName>
  <modelURL>;
  <UDN>uuid:7fff6211-b0fa-4075-a15c-0b624d01bfb4</UDN>
  <serviceList>
   <service>
    <serviceType>urn:schemas-upnp-org:service:Layer3Forwarding:1</serviceType>
    <serviceId>urn:upnp-org:serviceId:L3Forwarding1</serviceId>
    <SCPDURL>/x_layer3forwarding.xml</SCPDURL>
    <controlURL>/control?Layer3Forwarding</controlURL>
    <eventSubURL>/event?Layer3Forwarding</eventSubURL>
   </service>
  </serviceList>
  <deviceList>
   <device>
    <deviceType>urn:schemas-upnp-org:device:WANDevice:1</deviceType>
    <friendlyName>WANDevice</friendlyName>
    <manufacturer>D-Link Sytems, Inc.</manufacturer>
    <manufacturerURL>;
    <modelDeion>Internet Access Router</modelDeion>
    <modelName>DI-504</modelName&