相关文章
第零回.序和属性
第一回.真的了解.NET CF吗?
第二回.初窥CF类型加载器
第三回.让.NET CF CLR有条不紊
第四回.多窗体应用的性能与编程调试
第五回.WCF Mobile--Part 1

第五回. WCF Mobile(Part 2)

摘要
对于Windows Mobile来说,WCF是一个崭新的概念,在.NET CF v3.5中提供了对WCF的支持,它是桌面WCF(Windows Communication Foundation,也称作Indigo)的一个子集。本文阐述了Compact WCF的功能和模型以及如何使用WCF轻松创建通信程序。
Keywords
Windows Mobile, WCF, .NET CF, Web Service, BasicHttpBinding ,C#

本文是上一篇随笔WCF Mobile的第二部分。本文主要阐述如何使用Compact WCF的HTTP Transport的消息模型在我们的应用程序中进行实际的开发。如果您对WCF for .NET CF还没有概念的话推荐您先看看之前这篇随笔。

WCF for .NET CF-- BasicHttpBinding

上一篇随笔介绍了Compact WCF的一些背景知识,下面我们就通过一个例子来看看WCF For.NET CF的Message工作流程。这个例子采用的是BasicHttpBinding的方式,清晰地展示了该绑定方式下的应答式服务模型。不过非常建议您先看看之前的这两篇随笔作为热身:

Ø         WCF for .NET CF快速入门

Ø         WCF Mobile(Part 1)

前面已经提到过,BasicHttpBinding的方式还是比较好理解的,因为它跟传统的Web-Service是如此地类似(本质上其实就是调用WebService来实现的)。如图所示:

你的应用程序通过WiFi,有线同步网络或者移动运营商的网络向WCF服务发出一个SOAP消息形式的HTTP请求,并等待服务端的HTTP响应。与Email存储转发的传输方式(见这篇日志)不同的是,BasicHttpBinding这种应答模型使得服务端仅在客户端发送一个请求的条件下才会发送消息,而不会是客户端一旦在线就主动发送,事实上这种方式下服务端也并不关心客户端的任何状态。

好了,现在我们可以开始试着coding了。

打开VS,创建一个的Visual C#工程(我这里是用一个Windows控制台应用的工程),命名为WCFServer。先为项目添加以下引用:

Ø         System.ServiceModel.dll

Ø         System.XML.dll

Ø         System.Runtime.Serialization.dll

(在后面的代码中你将会看到他们的作用)

在上一篇文章中已经提到,所谓的Message是WCF服务的核心纽带,那么这里我们首先要定义一个我们自己的Message对象,姑且把它叫做消息体对象吧。右键你的项目,添加一个新的类,命名为TransmittedObject,内容如下:

这个可以XML序列化的类就是我们的消息体对象,为了简便,这里只给它两个字段。

接下来,理所当然的,我们需要一个用来在服务两端对这个消息对象进行序列化/反序列化的工具。于是,我们还得再添加一个新类,命名为XMLSerialHelper。首先添加以下using目录:

Ø         using System.Xml.Serialization;

Ø         using System.Xml;

Ø         using System.Runtime.Serialization;

然后让我们的XMLSerialHelper继承自XmlObjectSerializer这个抽象类,并对它做一点点扩展。

Tips:如果你使用的是Visual Studio的集成开发环境,键入如图这样继承一个抽象类的代码时:

Visual Studio会在抽象类下面标记来提示您操作,这时只需点击小标记,会有一个“Implement XmlobjectSerializer”的选项,我们选中它,VS会为我们生成需要实现的代码段。

 这个类完整的代码如下:

    public sealed class XMLSerialHelper:XmlObjectSerializer
    {
        Private Members#region Private Members
        XmlSerializer serializer;
        String defaultNameSpace;
        Type objectType;
        #endregion

        我们扩展的构造器重载#region 我们扩展的构造器重载
        public XMLSerialHelper(Type type)
            : this(type, null, null)
        {
        }

        public XMLSerialHelper(Type type, string name, string ns)
        {
            this.objectType = type;
            /**//*
             * 为指定类型实例化一个XmlSerializer
             * 并指定存放Xml元素的命名空间
             * 下面的类似
             */
            if (!String.IsNullOrEmpty(ns))
            {
                this.defaultNameSpace = ns;
                this.serializer = new XmlSerializer(type, ns);
            }
            else
            {
                this.defaultNameSpace = "";
                this.serializer = new XmlSerializer(type);
            }
        }
        #endregion

        我们实现的方法#region 我们实现的方法
        public override object ReadObject(XmlDictionaryReader reader)
        {
            string readersNS;            
           readersNS = (String.IsNullOrEmpty(reader.NamespaceURI)) ? "" : reader.NamespaceURI;
            if (String.Compare(this.defaultNameSpace, readersNS) != 0)
            {
                this.serializer = new XmlSerializer(this.objectType, readersNS);
                this.defaultNameSpace = readersNS;
            }
            return (this.serializer.Deserialize(reader));
        }

        public override void WriteObject(XmlDictionaryWriter writer, object graph)
        {
            this.serializer.Serialize(writer, graph);
        }
        #endregion

        待实现的方法(暂不用)#region 待实现的方法(暂不用)
        public override bool IsStartObject(XmlDictionaryReader reader)
        {
            throw new NotImplementedException();
        }

        public override object ReadObject(XmlDictionaryReader reader, bool verifyObjectName)
        {
            throw new NotImplementedException();
        }

        public override void WriteEndObject(XmlDictionaryWriter writer)
        {
            throw new NotImplementedException();
        }

        public override void WriteObjectContent(XmlDictionaryWriter writer, object graph)
        {
            throw new NotImplementedException();
        }

        public override void WriteStartObject(XmlDictionaryWriter writer, object graph)
        {
            throw new NotImplementedException();
        }
        #endregion
}

提示:上面两个类是服务双方(Client&Server)都要用到的,以维护同一类型的消息对象。待会儿您可能要Copy这两个类到你的客户端工程中去。

好了,现在可以开始写WCF服务的HOST了:

你需要添加以下两个引用先

Ø         using System.ServiceModel.Channels;

Ø         using System.ServiceModel;

服务的Main函数如下:

static void Main(string[] args)
        {
            //构建序列化消息体所需对象
            XMLSerialHelper xmlSerialHelper = new XMLSerialHelper(typeof(TransmittedObject));
            //按BasicHttpBinding的方式构建传输信道
            BasicHttpBinding binding = new BasicHttpBinding();
            BindingParameterCollection parameters = new BindingParameterCollection();
            IChannelListener<IReplyChannel> listener = 
            binding.BuildChannelListener<IReplyChannel>(new Uri("http://localhost:10008"), parameters);
            //启用Channel Listener
            listener.Open();   
            //接受请求
            IReplyChannel channel = listener.AcceptChannel();
            //打开响应信道
            channel.Open(TimeSpan.MaxValue);
            Console.WriteLine("****************************************");
            Console.WriteLine("Server is running at: "+listener.Uri.ToString());
            Console.WriteLine("Press <ENTER> to terminate");
            Console.WriteLine("****************************************");
            //接受Message,如果没有消息就等待
            RequestContext rContext = channel.ReceiveRequest(TimeSpan.MaxValue);
            //获得消息体
            TransmittedObject to = rContext.RequestMessage.GetBody<TransmittedObject>(xmlSerialHelper);
            Console.WriteLine(to.StrContent+" | from Freesc");
            //处理Message
            ProcessMsg(to);
            //以Message的形式发送回应. 
            Message replyMessage = Message.CreateMessage(MessageVersion.Soap11, "urn:test", to, xmlSerialHelper);
            rContext.Reply(replyMessage, TimeSpan.MaxValue);
            Console.ReadLine();
           channel.Close();
        }

//处理消息
        private static void ProcessMsg(TransmittedObject tranObj)
        {
            if (tranObj != null)
            {
                tranObj.StrContent = tranObj.StrContent + " fox23";
                tranObj.Count = tranObj.Count + 1;
            }
}

WCF的HOST相对比较简单,应该说是很少很简单但是很好很强大。WCF与传统的Web Service相比一个主要优势就是,在WCF运行时的支持下,如果你的代码得当,你完全可以让你的WCF服务跑在任何进程上,而不像原来总是需要依赖IIS。比如,你可以创建一个Windows服务或者一个简单的Winform的应用程序或者控制台程序等等,然后像本文中这样在你的程序中创建信道,监听端口,响应请求,不需要IIS介入。

至此,我们的服务端写好了已经,运行一下会看到以下输出:


Tips
如果您跟我一样是Windows Vista的用户,而且跟我一样不嫌UAC麻烦的话,您可能需要在项目所在文件夹下找到刚刚编译出来的可执行程序,右键à以管理员身份运行,不然可能会出现权限问题。

下面来看移动设备部分。在设备端我们首先要搞清楚的是在Compact WCF中,并不支持用配置文件来定义请求发往的地址以及绑定行为,这点与完整版的WCF不同。这就意味着我们可能需要在设备端的工程中引用包含消息对象和帮助序列化的类库或者那个工程。

然后编码定义信道,发送请求,等待响应……

我们首先为原来的解决方案添加一个VC#-->Smart Device的工程,为了方便,我们直接把原先写好的消息对象和用于序列化的类Copy过去(别忘了把他们的命名空间改成刚创建的智能设备工程的命名空间),并在工程中添加对以下项的引用:

Ø         using System.Runtime.Serialization;

Ø         using System.ServiceModel;

现在来看看背后主要用到的代码:

        public void TestMessage()
{
            //创建消息体
            TransmittedObject wcfMsg = new TransmittedObject();
            wcfMsg.StrContent = "hello";
            wcfMsg.Count = 0;
            //创建序列化器
            XMLSerialHelper wrapper =
                new XMLSerialHelper(typeof(TransmittedObject));
            //创建WCF消息对象
            Message m =
    Message.CreateMessage(MessageVersion.Soap11, "urn:test", wcfMsg, wrapper);
            // 创建一个BasicHttpBinding模式的信道
            BasicHttpBinding binding = new BasicHttpBinding();
            BindingParameterCollection parameters =
                new BindingParameterCollection();
            IChannelFactory<IRequestChannel> channelFactory =
                binding.BuildChannelFactory<IRequestChannel>(parameters);
            channelFactory.Open();
            //从指定的服务节点创建并打开信道
            IRequestChannel outChannel = 
                channelFactory.CreateChannel(
                new EndpointAddress(
                    new Uri(http://192.168.0.232:10008)));
            outChannel.Open(TimeSpan.MaxValue);
            //发送消息并等待回应.
            Message reply = outChannel.Request(m, TimeSpan.MaxValue);
            //获取并处理返回的消息体
            TransmittedObject to1 = reply.GetBody<TransmittedObject>(new
                XMLSerialHelper(typeof(TransmittedObject)));
            MessageBox.Show(to1.StrContent + " | from Server");
            //别忘了释放资源
            m.Close();
            reply.Close();
            outChannel.Close();
            channelFactory.Close();
        }

Tips:代码很简单,不过还有更简单的办法,就是像这篇文章中提到的使用.NET Compact Framework 3.5 Power Toys中的小工具为我们生成上述一开始写的那两段代码。这里我们采用人工编写是为了更清晰的反映事情的经过。

好了,现在来测试一下吧,先让服务端跑起来,再从设备端发送消息。程序运行效果如下:

示例代码在此处下载
下一部分我们将通过另外一种方式(Email Transport)来实现类似的功能。
Enjoy!

©Freesc Huang
     黄季冬<fox23>@HUST
     2008/3/30

Windows Mobile 进阶系列.WCF Mobile(Part 2)相关推荐

  1. Windows Mobile 进阶系列.第一回.真的了解.NET CF吗?

    第一回. 真的了解.NET Compact Framework吗? 作为系列文章的开篇,有必要先详细了解一下基于CE.NET的.NET Compact Framework(以后简称.NET CF),本 ...

  2. Windows Mobile 开发系列文章收藏 - Windows Mobile 6.x

    收集整理一些Windows Mobile 6.x开发相关文章, 文章及相关代码大部分搜集自网络,版权属于原作者! 智能手机      手机词汇      研发手机基本流程 WAP协议分析(1)     ...

  3. Dojo mobile TweetView 系列教程之三——Tweets和Mentions视图

    Dojo mobile TweetView 系列教程之三--Tweets和Mentions视图 分类: Javascript Dojo扩展 (dojox)2011-05-18 19:13 2211人阅 ...

  4. 大疆 DJI mobile SDK系列详细教程——运行实例代码(跑通大疆官方提供Mobile SDK里的sample code)

    大疆 DJI mobile SDK系列详细教程--运行实例代码(跑通大疆官方提供Mobile SDK里的sample code) 文章目录 一.官方文献与资源地址 二.操作步骤 提示:昨天在尝试跑通大 ...

  5. windows程序员进阶系列:《软件调试》之O--- WinDbg使用介绍

    windows程序员进阶系列:<软件调试>之O--- WinDbg使用介绍 拥有一个顺手的武器是每一个武林高手梦寐以求的.对于windows程序员来说,WinDbg调试器就是我们的武器.熟 ...

  6. WCF4.0进阶系列--第四章 保护企业内部的WCF服务(转)

    http://www.cnblogs.com/yang_sy/archive/2011/05/24/2054834.html [摘要] 安全是任何系统至关重要的一个方面,尤其当该系统由分布式的程序和服 ...

  7. Mobile net系列总结(V1、V2、V3)

    一.Mobile Net V1 主要贡献: (1)使用了深度可分离卷积构建轻量级卷积神经网络,由depthwise(DW)和pointwise(PW)两个部分结合起来,用来提取特征feature ma ...

  8. DotNet进阶系列

    一. 回顾历史 回顾个人发展历程,自2012年初次接触开发至今(2018年)已经有六个年头,这期间陆陆续续学习并掌握了不少技术,C#语言.ORM框架.多线程技术.设计模式.前端技术.MVC.MVVM框 ...

  9. 一步步实现windows版ijkplayer系列文章之三——Ijkplayer播放器源码分析之音视频输出——音频篇

    https://www.cnblogs.com/harlanc/p/9693983.html 目录 OpenSL ES & AudioTrack 源码分析 创建播放器音频输出对象 配置并创建音 ...

  10. C#进阶系列——WebApi 身份认证解决方案:Basic基础认证

    阅读目录 一.为什么需要身份认证 二.Basic基础认证的原理解析 1.常见的认证方式 2.Basic基础认证原理 三.Basic基础认证的代码示例 1.登录过程 2./Home/Index主界面 3 ...

最新文章

  1. 在centos 下安装配置基于gitosis 的git 服务
  2. python.re模块
  3. Google Chrome v48.0.2564.
  4. Mycat关键特性,什么是MYCAT,MYCAT监控,版本架构,长期规划2.0,Mycat高可用方案
  5. shell 删除七日内日志_shell日志删除(超容量自动)
  6. Windows XP系统安装SQL Server 2005(开发版)图解
  7. 后续的C++测试并不一定会成功
  8. centos yum出错:Cannot find a valid baseurl for repo
  9. wgan 不理解 损失函数_[图像盲去噪与GAN]GCBD翻译理解
  10. ORA-03113: 通信通道的文件结尾 进程 ID: 764 会话 ID: 125 序列号: 5
  11. pdfjs实现pdf预览
  12. Android 补间动画之平移动画TranslateAnimation
  13. 日常水文章之Linux+arm+阿里IOT sdk+Cmake
  14. 保险初识经验汇总(重疾、医疗、寿险、意外)
  15. serviceBattery mac换电池 mac怎么换电池mac拆机
  16. MathType中/英文版下载地址汇总(适用于Mathtype6.9)
  17. Project Ara 手机,拼装的DIY手机
  18. JavaScript 数据类型之 Symbol、BigInt
  19. 美多(web)后台第五天
  20. c语言换零钱程序,换零钱问题。将一元钱换成1分,2分,或5分的零钱有多少换法。vb编程...

热门文章

  1. 【图像分割】基于matlab GUI二值化+灰白质医学影像分割【含Matlab源码 184期】
  2. 【C++】演讲比赛流程管理系统
  3. python open函数用法_python中open函数的用法详解
  4. 选择mysql开发的原因_MySQL开发技巧
  5. python基础:变量与数据类型
  6. read.table--R语言
  7. selenium.common.exceptions.WebDriverException:Message: 'chromedriver' executable needs to be in PATH
  8. leecode---40---数组,dfs---求所有的组合为target,有重复数组
  9. Angular和Vue.js 深度对比
  10. Intel DCM 携手DELL共同推出关于DCIM的联合调研