通过Windows Mobile连接管理器建立网络连接

EstablishNetworkWithConnMgr.rar

  原文为Jim Wilson 的 Establishing Network Connectivity with the Windows Mobile Connection Manager

 

 概要 Summary

    本文主要讲述在托管程序中怎样使用连接管理器建立和释放网络连接。本文的重点是关于使用连接管理器建立和断开连接的概念,而不是如何封装连接管理器API。文章的目的在于涵盖概念使能够适用于任何连接管理器托管API。 

 

 适用 Applies to

Windows Mobile 6 Professional

Windows Mobile 6 Standard

Windows Mobile 6 Classic

Windows Mobile 5.0 for Pocket PC Phone Edition

Windows Mobile 5.0 for Smartphone

Windows Mobile 5.0 for Pocket PC 

 

 索引

1. Introduction

2. Accessing Connection Manager from Managed Code

3. Establishing a Connection

4. Conclusion 

 

 介绍 Introduction

     现代Windows Mobile设备包含了许多的网络连接选项,比如Wi-Fi和大量的cellular radios。而且,众所周知,所有Windows Mobile设备能够通过ActiveSync连接桌面计算机访问网络。


    所有这些网络选项提供不同的数据速度,并且,在任何时候,0、1或更多的网络连接可能都是可用的。当一个应用程序需要建立一个网络连接时,Windows Mobile提供了一个公共解决方案 —— 那就是连接管理器,而不必要求应用程序自己遍历所有可用的连接然后选择最合适的连接。


    就象名字表达的一样,连接管理器负责管理设备上所有的网络连接。当应用程序需要建立一个网络连接时,只需要简单的告诉连接管理器需要哪种连接(比如Internet),连接管理器自己会识别哪些连接可用、选择最佳连接并按照需要建立连接。连接管理器甚至可以在有相同连接需求的多个应用程序中共享一个连接。应用程序完全从细节中抽象出来并且无需考虑任何细节地使用连接。


    连接管理器功能强大并提供了大量不同的连接相关的性能。本文主要讲述怎样使用连接管理器建立一个网络连接并且在不需要的时候释放掉。


    虽然本文的例子直接p/invoke连接管理Native API,但是本文主要讲述关于使用连接管理建立和断开连接的概念,而不是如何封装连接管理器API。文章的目的在于涵盖概念使能够适用于任何连接管理器托管API。

 

 通过托管代码访问连接管理器 Accessing Connection Manager from Managed Code  

    连接管理器是Windows Mobile的基础部分,但是当前只以Native API的形式暴露。好消息是大多数API很简单并且能够通过.NET Compact Framework简单地访问。你需要定义一些枚举和结构,但大多数不必去定义,过程非常简单。

注意

    Microsoft Visual Studio 2008下.NET Compact Framework 3.5提供了连接管理器API的管理版本实现。即使你打算使用托管版本API,仍然鼓励你读完本文,因为无论是托管API还是P/Invoke本地API,他们的概念意义是一样的。

【李森 - listen附:Connection Manager wrapper 提及了:他本人向Jim Wilson 通过Email问及了Microsoft Visual Studio 2008下.NET Compact Framework 3.5 连接管理器的封装,Jim Wilson回答说微软放弃了添加连接管理器的封装,而Jim Wilson本人也多次要求微软方面删去本段,但是一直没有修改】

 

   函数Connection Manager Functions

    Connection Manager API由11个函数组成,但是只需要使用其中的5个就能完成建立和释放网络连接的工作。在许多情况下,你的程序可能只要其中的2个函数就可以了。表1显示了这5个函数

函数    

 描述

ConnMgrMapURL

取回指定URL的网络标识(Internet or Work) 。

ConnMgrEstablishConnection

为指定的网络标识选择和建立合适的连接。该方法直接返回,并不等待连接完整。使用ConnMgrConnectionStatus去判断网络状态。 

ConnMgrEstablishConnectionSync

为指定的网络标识选择和建立合适的连接。该方法直到尝试连接完成了才返回。 

ConnMgrReleaseConnection 

释放指定连接,可能会关闭连接(并不保证一定关闭)。 

ConnMgrConnectionStatus 

取回指定连接的状态。 

  表1 Connection Manager functions used to establish and release a network connection

 

    要从 .NET Compact Framework程序中访问这5个函数,需要P/Invoke,如下: 

  1. [DllImport("CellCore.dll")]
  2. static extern int ConnMgrMapURL(string url, ref Guid networkGuid, int passZero);
  3. [DllImport("CellCore.dll")]
  4. static extern int ConnMgrEstablishConnection(ConnMgrConnectionInfo connectionInfo, ref IntPtr connectionHandle);
  5. [DllImport("CellCore.dll")]
  6. static extern int ConnMgrEstablishConnectionSync(ConnMgrConnectionInfo connectionInfo, ref IntPtr connectionHandle, uint dwTimeout, ref ConnMgrStatus dwStatus);
  7. [DllImport("CellCore.dll")]
  8. static extern int ConnMgrReleaseConnection(IntPtr connectionHandle, int cache);
  9. [DllImport("CellCore.dll")]
  10. static extern int ConnMgrConnectionStatus(IntPtr connectionHandle, ref ConnMgrStatus status);

 

   枚举和结构 Connection Manager Enumerations and Structures 

    当使用连接管理器建立连接时,必须指定连接的特性。本地代码中需要使用CONNMGR_CONNECTIONINFO和几组 #define 宏来指定连接的特性。为了使结构和.NET的特性相容,可以用托管的类来实现结构并命名为,比如ConnMgrConnectionInfo。与其象.NET一样使用常量来定义宏,不如使用枚举来显式地定义每组相关的宏。

注意

  如果想查看所有连接管理器native声明,可参见%Program Files%\Windows Mobile 6 SDK\PocketPC\Include\Armv4i 下的connmgr.h头文件

 

    有几个结构依赖于宏,因此首先要定义宏对应的枚举。

 

    第一个枚举对应CONNMGR_PARAM_* 宏集。这些值标识了结构中可用的标准字段,这些字段指定了请求连接中想要的特性。一个连接请求可以指定多个条件,因此,枚举声明应该包含Flags属性。通过表示Flags属性,可以对成员进行位或运算。 

  1. [Flags]
  2. public enum ConnMgrParam : int
  3. {
  4.     GuidDestNet = 0x1,
  5.     MaxCost = 0x2,
  6.     MinRcvBw = 0x4,
  7.     MaxConnLatency = 0x8
  8. }

    

    每个枚举变量就像结构中对应的字段一样拥有相同的名字。下面对于结构定义的讨论将满足每个字段的意义。

  

    下一个枚举对应CONNMGR_FLAG_PROXY_*宏。这些值指定代理服务器的类型,连接管理器使用该类型建立连接。就象ConnMgrParam枚举一样,该声明包含Flags属性。 

  1. [Flags]
  2. public enum ConnMgrProxy : int
  3. {
  4.     NoProxy = 0x0,
  5.     Http = 0x1,
  6.     Wap = 0x2,
  7.     Socks4 = 0x4,
  8.     Socks5 = 0x8
  9. }

 

    下面需要考虑的就是连接请求的优先级。连接管理器负责设备上的所有连接,并尝试服务尽量多连接请求。为了辅助连接管理器决定每个请求的顺序和重要性,每个请求必须指定优先级。连接管理器支持几个不同优先级水平,尽管如此,程序一般只使用几个值。下面的枚举声明显示了通常使用的值: 

  1. public enum ConnMgrPriority
  2. {
  3.     UserInteractive = 0x8000,
  4.     HighPriorityBackground = 0x0200,
  5.     LowPriorityBackground = 0x0008
  6. }

 

 表2提供了每个优先级的描述:

优先级

描述 

UserInteractive  

主动请求连接,用户接口等待连接。该优先级高于大多数其他优先级。 

HighPriorityBackground 

该请求为高优先级,但是应用程序在后台运行,因此并不影响用户接口。

LowPriorityBackground  

该请求仅当更高优先级的程序已经使用了请求路径后才连接。使用该优先级,应用程序可能共享一个已经存在的连接而并不是建立一个新的连接。

  表2 Common Connection priorities

     连接管理器支持了更多的枚举声明,connmgr.h中的CONNMGR_PRIORITY_*宏提供了完整的列表。 在表2中没有提及的优先级值,参考Connection Manager Priority Constants

 

    最后一个枚举是连接状态值。大多数状态值名称无需说明便能理解。对于每个状态值的描述,参见Connection Manager Status Constants或者参见connmgr.h中的CONNMGR_STATUS_*宏。如下:

  1. public enum ConnMgrStatus
  2. {
  3.     Unknown = 0x00,
  4.     Connected = 0x10,
  5.     Suspended = 0x11,
  6.     Disconnected = 0x20,
  7.     ConnectionFailed = 0x21,
  8.     ConnectionCanceled = 0x22,
  9.     ConnectionDisabled = 0x23,
  10.     NoPathToDestination = 0x24,
  11.     WaitingForPath = 0x25,
  12.     WaitingForPhone = 0x26,
  13.     PhoneOff = 0x27,
  14.     ExclusiveConflict = 0x28,
  15.     NoResources = 0x29,
  16.     ConnectionLinkFailed = 0x2a,
  17.     AuthenticationFailed = 0x2b,
  18.     NoPathWithProperty = 0x2c,
  19.     WaitingConnection = 0x40,
  20.     WaitingForResource = 0x41,
  21.     WaitingForNetwork = 0x42,
  22.     WaitingDisconnection = 0x80,
  23.     WaitingConnectionAbort = 0x81
  24. }

 

    所有的枚举定义后,下面要做的就是理解连接请求结构体。可以使用结构或管理类来组织Native结构,一般,类比结构体更流畅,因为类允许提供默认构造函数和成员初始化。上面已经提过了,ConnMgrConnectionInfo管理类代表CONNMGR_CONNECTIONINFO,如下: 

  1. [StructLayout(LayoutKind.Sequential)]
  2. class ConnMgrConnectionInfo
  3. {
  4.     Int32 cbSize;                          // DWORD
  5.     public ConnMgrParam dwParams = 0;      // DWORD
  6.     public ConnMgrProxy dwFlags = 0;       // DWORD
  7.     public ConnMgrPriority dwPriority = 0; // DWORD
  8.     public Int32 bExclusive = 0;           // BOOL
  9.     public Int32 bDisabled = 0;            // BOOL
  10.     public Guid guidDestNet = Guid.Empty;  // GUID
  11.     public IntPtr hWnd = IntPtr.Zero;      // HWND
  12.     public UInt32 uMsg = 0;                // UINT
  13.     public Int32 lParam = 0;               // LPARAM
  14.     public UInt32 ulMaxCost = 0;           // ULONG
  15.     public UInt32 ulMinRcvBw = 0;          // ULONG
  16.     public UInt32 ulMaxConnLatency = 0;    // ULONG
  17. } ;

 

    表3描述了各个字段的意义 

字段  

描述 

cbSize 

类的大小,按照字节计算 

dwParams  

连接管理器参数常量枚举值。 

dwFlags  

连接请求的代理需求 

dwPriority  

连接请求的优先级 

bExclusive 

是否独占连接请求,非0值代表连接不能被其他程序共享 

bDisabled 

指定连接是否断开。非0值代表连接管理器决定连接是否进行,但并不真正执行连接。当连接管理器需要建立连接时,它会将连接状态设置为ConnMgrStatus.ConnectionDisabled。 

guidDestNet  

目标GUID。如果该字段可用,那么dwParams字段必须包含ConnMgrParam.GuidDestNet值。 

hWnd 

接受状态改变消息的窗口句柄,通常为MessageWindow派生类句柄。 

uMsg 

当发送状态改变消息时,发送该消息到hWnd字段代表的窗口。 

lParam 

发送给hWnd字段代表的窗口的状态改变消息中的值,该字段当且仅当hWnd字段被设置了一个可用窗口句柄时才可用。在消息中使用该参数包含应用程序指定的数据。

ulMaxCost  

可接收的最大连接。如果dwParams字段包含 ConnMgrParam.MaxCost 属性,此字段才可用(大多数程序取消该字段)。 

ulMinRcvBw  

可接收的最小带宽。如果dwParams字段包含 ConnMgrParam.MinRcvBw 属性,此字段才可用(大多数程序取消该字段)。 

ulMaxConnLatency  

最大等待时间。如果dwParams字段包含 ConnMgrParam.MaxConnLatency 属性,此字段才可用(大多数程序取消该字段)。

 表3 ConnMgrConnectionInfo class members

 

    为了简化ConnMgrConnectionInfo类的使用,该类定义了一组构造函数负责一些通用情况。第一个是默认构造函数,如下: 

  1. public ConnMgrConnectionInfo()
  2. {
  3.     cbSize = Marshal.SizeOf(typeof(ConnMgrConnectionInfo));
  4. }

    在给Connection Manager functions传递ConnMgrConnectionInfo类参数时,需要指定ConnMgrConnectionInfo类的大小,因此构造函数自动存储cbSize作为类的大小。 

 

    虽然连接管理器类有许多成员,应用程序通常只需要设置目标GUID、优先级,如果需要,还要设置代理。如果包含目标GUDI,那么dwParams字段必须要设置ConnMgrParam.GuidDestNet值。如下:

  1. public ConnMgrConnectionInfo(Guid destination, ConnMgrPriority priority, ConnMgrProxy proxy)
  2.     : this()
  3. {
  4.     guidDestNet = destination;
  5.     dwParams = ConnMgrParam.GuidDestNet;
  6.     dwPriority = priority;
  7.     dwFlags = proxy;
  8. }

 

    方便起见,该类包含了2个其他的构造函数。第一个接受目标GUID和优先级,不需要代理。第二个参数接受目标GUID,并设置优先级为ConnMgrPriority.UserInteractive,该优先级由程序用户接口等待。 

  1. public ConnMgrConnectionInfo(Guid destination, ConnMgrPriority priority)
  2.     : this(destination, priority, ConnMgrProxy.NoProxy) { }
  3.  
  4. public ConnMgrConnectionInfo(Guid destination)
  5.     : this(destination, ConnMgrPriority.UserInteractive) { }

 

 建立连接 Establishing a Connection

    使用Native方法,枚举和结构就可以通过连接管理器建立一个连接了。好消息是困难的工作已经做完了,使用连接管理器是非常容易的事情。建立实际的连接包括两步:确定网络目标标识和发送连接请求。 

 

   确定目标网络标识 Determining a Network Destination Identifier

    建立连接的第一步是标识你的程序是连接到Internet 还是单位网络(Work network)。每个网络目标有一个指定的GUID.Internet的GUID是436EF144-B4FB-4863-A041-8F905A62C572,单位网络(Work network)的GUID是A1182988-0D73-439e-87AD-2A5B369F808B。


    虽然我们都知道每个网络目标的GUID,但大多数程序并不直接使用这些GUID.相反,能够功过ConnMgrMapURL函数确定合适的网络GUID.


    连接管理器压缩了逻辑来确定url请求哪个网络。因此,与其硬编码一个网络GUID,不如简单的传递一个目标url给ConnMgrMapURL函数来返回一个合适的GUID。 

注意[说明,原文periods在此处翻译为:终结符。比如http:\\www.baidu.com,那么".com"就是periods]
    连接管理器根据目标url中是否包含终结符决定请求Internet还是单位网络(Work network)。如果包含终结符,那么请求连接Internet;如果不包含则链接到单位网络(Work network)。如果你的公司在内部网络地址中使用终结符,那么你就必须把那些地址添加到工作URL异常列表中(Work URL exception list)。进入“Start - setting - connections”页面, 选择Connections图标,接着选择Advanced页面,最后选择Exceptions按钮,就可以添加那些URL到工作URL异常列表中了。

 

    下面的代码演示了使用ConnMgrMapURL去确定不同URL的目标GUID。

  1. string url1 = "http://msdn2.microsoft.com/en-us/netframework/bb495180.aspx";
  2. string url2 = "http://hedgydev02/test/default.aspx";
  3. Guid destination1 = Guid.Empty;
  4. Guid destination2 = Guid.Empty;
  5.  
  6. ConnMgrMapURL(url1, ref destination1, 0);
  7. ConnMgrMapURL(url2, ref destination2, 0);
  8.  
  9. // Write to debug window
  10. Debug.WriteLine(url1);
  11. Debug.WriteLine(destination1.ToString());
  12. Debug.WriteLine("");
  13. Debug.WriteLine(url2);
  14. Debug.WriteLine(destination2.ToString());

 

    在上面例子中,有两个URL,第一个为Microsoft's MSDN网址,显然是在Internet上。另一个URL则指向内部网络上的某个电脑,因此是在单位网络(Work network)上。图一显示在运行上述代码后,Visual Studio输出窗口上显示的信息。就像看到的那样,Microsoft URL 返回Internet GUID (436EF144-B4FB-4863-A041-8F905A62C572),而指向内部网的URL则返回Work GUID(A1182988-0D73-439e-87AD-2A5B369F808B)。 

 

 

图一 The destination network identifiers for an Internet URL and a Work URL 

 

    多亏ConnMgrMapURL函数,这也许是你最后一次见到这些GUID了。以后,你只需让ConnMgrMapURL函数为你生成GUID即可。 

 

   建立连接 Making the Connection 

    在这里,只需使用合适的连接标准并且调用合适的连接管理器函数,你就可以创建一个ConnMgrConnectionInfo类实例了。

   

    当请求连接时,你只需调用ConnMgrEstablishConnectionSync函数,该函数将保持阻塞直到连接过程完成;或者使用ConnMgrEstablishConnection函数,该函数立即返回,调用线程无需等待连接过程完成。如果你的应用程序在后台运行,或者你在后台建立连接,可以使用ConnMgrEstablishConnectionSync函数,因为当函数返回时你就能够知道是否成功地建立了连接。下面的代码演示了如何使用ConnMgrEstablishConnectionSync函数建立一个连接: 

  1. IntPtr _connectionHandle = IntPtr.Zero; // Class-level field
  2. const int _syncConnectTimeout = 60000; // 60 seconds
  3. void DoConnect(string url)
  4. {
  5.     Guid networkGuid = Guid.Empty;
  6.     ConnMgrStatus status = ConnMgrStatus.Unknown;
  7.     ConnMgrMapURL(url, ref networkGuid, 0);
  8.     ConnMgrConnectionInfo info = new ConnMgrConnectionInfo(networkGuid, ConnMgrPriority.HighPriorityBackground);
  9.     ConnMgrEstablishConnectionSync(info, ref _connectionHandle, _syncConnectTimeOut, ref status);
  10.  
  11.     if (status == ConnMgrStatus.Connected)
  12.         Debug.WriteLine("Connect Succeeded");
  13.     else
  14.         Debug.WriteLine("Connect failed: " + status.ToString());
  15. }

 

    注意,前面的代码给ConnMgrEstablishConnectionSync函数传递了一个延时值。你应该提供一个足够大的延时值,因为,在建立连接时,尤其在一个很差的网络时,有时候要花上几十秒时间。如果函数因为时间到了而返回,相比于连接错误,此时的状态值为ConnMgrStatus.WaitingConnection。当你取回ConnMgrStatus.WaitingConnection状态值时,那么如果你提供了足够的延时值,连接就有可能成功。因此,你应该在尝试建立连接时给与足够的延时时间。

 

    为了测试前面的连接模式,我们使用Windows Mobile 6 Professional Device Emulator 和 Cellular Emulator。Cellular Emulator模拟cellular radio,给Device Emulator提供GPRS连接。开始前,你必须连接Device Emulator到Cellular Emulator,具体参照 Cellular Emulator section 里的Developer's Guide to the Arm 。

 

    你需要为模拟器定义GRPS连接,这和使用真实设备连接到服务网络的步骤一样。按照下面的步骤定义GPRS连接:

        1. 在设备模拟器中 “Start - setting”

        2. 在“setting”页面选择“Connections” 页面

        3. 点击“Connections”图标

        4. 然后,选择上面“My ISP”下的“Add a new modem connection”

        5. 将连接命名为GPRS Internet。 该名字对连接行为并不起任何作用。

        6. 从“Select a modem”下拉列表中选择“Cellular (GPRS) ”

        7. 选择“Next”

        8. 保持“Access point name”为空,选择Next

        9. 保持本页面所有字段为空(User name, Password, Domain),选择“Finish”

 

    现在就定义好了Internet GPRS连接。同样,定义一个单位网络(Work network)GPRS连接,和上面的一样,只需在第4步选择“My Work Network”下“Add a new modem connection”,接着在第5步将连接命名为“GPRS Work”即可。

注意

    之所以能将访问点(Access point)和登录信息设置为空,是因为我们通过Cellular Emulator连接GPRS。如果在真实设备上,你就需要将这些字段设置上信息了。

 

    运行之前的代码后,你就会在几秒后看到Cellular Emulator上“Data Channels”下面的列表有一行上显示有连接活动了。

 

图二 The Cellular Emulator with an active GPRS connection 

 

    Cellular Emulator显示连接以后,ConnMgrEstablishConnectionSync函数就会立即返回了。你应该每次都检查返回状态以确定连接是否真的成功。一个成功的连接返回ConnMgrStatus.Connected状态。

 

注意  要关闭Cellular Emulator的连接,在“Network”下点击“Disconnect GPRS”按钮。


    因为ConnMgrEstablishConnectionSync函数在连接进程完全完成以前一直在阻断进程,所以该连接并不适合在面向用户的程序主线程运行,否则将造成程序界面假死现象。为了避免这种情况,就要使用ConnMgrEstablishConnection函数。ConnMgrEstablishConnection 函数并不等待连接完成就直接返回,它只是初始化连接。调用ConnMgrEstablishConnection函数和调用ConnMgrEstablishConnectionSync函数的操作非常相似,如下: 

  1. Guid networkGuid = Guid.Empty;
  2. ConnMgrMapURL(url, ref networkGuid, 0);
  3. ConnMgrConnectionInfo info = new ConnMgrConnectionInfo(networkGuid);
  4. ConnMgrEstablishConnection(info, ref _connectionHandle);

 

    如上代码,ConnMgrEstablishConnection函数只是初始化连接然后在连接管理器真正建立连接前就直接返回了。为了检测连接是否真的完成,应该使用SystemState类和ConnMgrConnectionStatus函数协同使用来监测。 

 

    下面的代码显示了当连接数发生改变时,SystemState类如何通知应用程序: 

  1. SystemState _connectionsCount;
  2. private void Form1_Load(object sender, EventArgs e)
  3. {
  4.     _connectionsCount = new SystemState(SystemProperty.ConnectionsCount);
  5.     _connectionsCount.Changed += new ChangeEventHandler(ConnectionsCount_Changed);
  6. }

 

    这段代码构造了_connectionsCount实例,并把它和ConnectionsCount属性关联起来,将ConnectionsCount_Changed方法和_connectionsCount.Changed事件关联起来。只要连接数量发生改变时,_connectionsCount实例就调用ConnectionsCount_Changed函数。 

 

    一旦你知道活动连接的数量发生改变,你需要检测发生的改变是否是由于你请求的连接造成的。下面的代码描述了如何使用ConnMgrConnectionStatus函数来决定连接数量的改变是否是由你之前通过ConnMgrEstablishConnection 函数建立的连接引起的。 

  1. void ConnectionsCount_Changed(object sender, ChangeEventArgs args)
  2. {
  3.     ConnMgrStatus status = ConnMgrStatus.Unknown;
  4.     ConnMgrConnectionStatus(_connectionHandle, ref status);
  5.     if (status == ConnMgrStatus.Connected)
  6.     {
  7.         // Connection Established
  8.     }
  9. }

 

    通过上面的代码,当连接状态为ConnMgrStatus.Connected时,你就知道你的连接已成准备好并可以使用了。必须要记住,系统在主线程里调用ConnectionsCount_Changed方法,因此,你不能在该方法中执行任何长时间运行的任务。相反,你应该在后台线程执行任务或者使用异步方法,就像SqlCeReplication类中的BeginSynchronize方法一样。


    你可以使用Device Emulator和Cellular Emulator测试你的代码。 

 

注意    有许多方法可以检测当前连接的状态。你可以在任何时间使用ConnMgrConnectionStatus 函数检测连接状态以确认连接没有遇到任何异常问题,这很必要。 你也可以创建MessageWindow派生类,并且将该类的窗口句柄赋值给ConnMgrConnectionInfo类的hWnd字段,这样,连接管理器每次都会在连接状态发生改变时给MessageWindow派生类发送一个消息。当然你也可以使用ConnMgrRegisterForStatusNotification 函数(将MessageWindow派生类的窗口句柄传递给ConnMgrRegisterForStatusNotification函数的第二个参数,这样连接管理器在连接状态发生改变时也会给MessageWindow派生类发送一个消息)。
 

 

   释放连接 Releasing the Connection 

    释放连接的操作非常简单。大多数情况下,你只需要简单的给ConnMgrReleaseConnection函数传递之前从ConnMgrEstablishConnectionSync或ConnMgrEstablishConnection 函数返回的连接句柄即可。ConnMgrReleaseConnection函数给连接管理器发送信号告诉它程序已经使用完连接,但是该函数并没有真正的关闭它。要理解什么时候连接才会真正的关闭,你需要理解ConnMgrReleaseConnection的lCache参数以及连接管理器是如何管理连接缓存的。 

 

   缓存连接 Caching the Connection 

    正如我们所知道的,建立连接会很消耗时间,为了避免在重复建立相同的连接上的花费,有时候在最后一个程序释放完连接以后,连接管理器还会继续维持该连接。这就是连接缓冲机制。应用程序能通过设置ConnMgrReleaseConnection 函数的lCache参数来改变连接管理器缓冲一个连接的时间,但最终做决定的还是连接管理器。表4显示了lCache参数可能的值: 

参数值 

 描述

不缓冲连接 

默认的缓冲时间  

大于 1 

缓冲用户指定的时间,以秒为单位

  表4 ConnMgrReleaseConnection cache parameter values

 

    如表4所示,如果参数设置为0,那么连接管理器立即关闭连接。设置为1则缓冲默认的时间,如下: 

  1. // Release the connection and cache it for the default time period.
  2. ConnMgrReleaseConnection(_connectionHandle, 1);

 

     默认的缓冲时间定义在[HKEY_LOCAL_MACHINE\Comm\ConnMgr\Planner\Settings]注册表下的某个键值中。默认缓冲时间依赖于连接是否以独占模式创建。CacheTime键的值定义了非独占模式的默认缓冲时间,在大多数设备中被设置为600秒(10分钟)。对于独占模式,VPNCacheTime键定义了缓冲时间,大多数设备中位60秒(1分钟)。 

 

注意    独占连接只能被创建该连接的程序使用,而不能被其他程序使用。可以通过设置ConnMgrConnectionInfo.bExclusive字段为非0值来设置独占模式。大多数情况下,可以通过设置连接为共享模式(非独占)来更高效的利用设备资源。
 

 

    Windows Mobile 5.0以前,程序只能指定连接是否能够缓存。Windows Mobile 5.0和Windows Mobile 6以后,你能够指定连接管理器缓存连接的时间。而这些只需要简单给ConnMgrReleaseConnection函数的lCache参数设置想要的时间数,而不是设置为0或1。如下代码,设置缓冲时间为300秒(5分钟)。

  1. // Release the connection and cache it for 5 minutes.
  2. ConnMgrReleaseConnection(_connectionHandle, 300);

 

    应该时刻记住,应用程序给ConnMgrReleaseConnection函数的lCache参数传递值只是一个请求,而连接管理可以选择忽略该请求。大多数情况下连接管理器不会考虑你的缓冲请求,连接管理器会将连接保持更长的缓冲时间。比如,当通过Ethernet cable连接网络或设备通过ActiveSync连接到桌面网路,那么设备将会有一个持续的连接。象这些类型的连接,网络连接是由Ethernet cable或依托设备(cradled device)建立的,他们会一直保持连接直到物理设备断开了连接。连接管理器管理这些连接,但是实际上却不打开或关闭它们。 

 

    另一种情况,连接管理器会在程序发送断开连接请求时保持更长的缓冲时间,那就是在维持该连接并不需要任何花费时。最普通的例子就是GPRS连接,GPRS radios并不消耗电源除非在进行数据传输时才会。而且,大多数情况下,移动运营商是根据连接的流量而不是连接时间来对GPRS收费的。 

 

    你可以通过Cellular Emulator看到连接管理器缓冲GPRS连接的过程,此时要确保Device Emulator核Cellular Emulator时刻保持连接。如果Cellular Emulator仍然保持活动的连接,可以通过点击Network页面下Disconnect GPRS按钮来断开连接。现在,运行以下代码: 

  1. Guid networkGuid = Guid.Empty;
  2. ConnMgrStatus status = ConnMgrStatus.Unknown;
  3. ConnMgrMapURL(url, ref networkGuid, 0);
  4. ConnMgrConnectionInfo info = new ConnMgrConnectionInfo(networkGuid, ConnMgrPriority.HighPriorityBackground);
  5. Debug.WriteLine("Attempting Sync Connect");
  6. ConnMgrEstablishConnectionSync(info, ref _connectionHandle, _syncConnectTimeOut, ref status);
  7.  
  8. if (status == ConnMgrStatus.Connected)
  9.     ConnMgrReleaseConnection(_connectionHandle, 0);
  10. else
  11.     Debug.WriteLine("Connection failed: " + status.ToString());

 

    代码先调用过ConnMgrEstablishConnectionSync 函数,然后紧接着调用ConnMgrReleaseConnection函数。注意,我们给ConnMgrReleaseConnection函数的Cache参数传递0,来请求连接管理器立即关闭连接。 

 

    运行该代码后,你会在Cellular Emulator发现,之前的连接和以前一样还是处于活动状态,你会发现连接根本就没终止。Time(s)列的时间值一直保持增长,连接维持活动状态但是却不传输任何数据,而且,在真实设备中这并不会消耗任何电源。连接管理器忽略了断开请求中的不缓冲请求,而是继续连接而不消耗任何有效资源。


    这种连接类型有时被称作“suspend/resume”(挂起/恢复)连接,也就是,当不再使用连接时,连接仍然被保持,只是进入高效的挂起状态。GPRS连接一般会保持无限持续连接状态,直到连接管理器返回该连接给应用程序并使用它时才活动。 

 

 结论 Conclusion 

    连接管理器是Windows Mobile中最强大的因素之一,它压缩了管理连接的细节,自动选择最好的连接,甚至跨程序共享连接。它支持大量特性,有时这会吓到用户,但是,大多数程序不必关心连接管理器的扩展因素。只需使用其中几个函数就可以轻松的建立连接,而让连接管理器去处理内部的细节。 

 

 其他资源 See Also

Connection Manager Reference

Windows Mobile Cellular Emulator

Windows Mobile Device Emulator 

 

翻译完了,有点累,呵呵! 以上译文,如有错误,欢迎指出!

我将文章所有的代码添加到了下面的工程中,上面的文章在获取连接状态时使用的是SystemState,我在代码里做了添加。另外,我还添加了使用MessageWindow派生类来获取连接状态的方法,并将其窗口句柄传递给ConnMgrConnectionInfo类的hWnd字段。当然,如上所说,你也可以将窗口句柄传递给ConnMgrRegisterForStatusChangeNotification函数的第二个参数,也能达到同样的效果!

 

代码下载:

 EstablishNetworkWithConnMgr.rar 

 

posted on 2009-12-23 14:28  listenlisten  阅读(4056)  评论(22编辑  收藏  举报

导航