WCF开发之消息模式(Message Pattern)

Request/Reply操作
• 所有服务操作的缺省行为
• WSDL描述操作所涉及到的<input>与<output>消息
• 消息能够包含请求参数,返回数据,<body>元素,或者返回的SOAP错误

Client---request-->Service

Client<--Reply--Service

One-Way操作(1)
• 使用OperationContractAttribute将操作标记为one-way
• 在WSDL只包括<input> 消息
• 没有回应(response),不会报告异常

Client--OneWay-->Service

One-Way操作(2)
[ServiceContract(Namespace=http://www.cnblogs.com/charlesliu/2010)]
public interface IHelloIndigoService
{
     [OperationContract(IsOneWay=true)]
     void HelloIndigo(string message);
}

One-Way操作(3)
• 客户端虽然不关心消息响应,但是有些情况下希望保证消息能够确实送达
– 可靠性消息提供暂时性的可靠性 (解决不了服务器出现崩溃,重起等问题)
– Microsoft® Message Queuing (MSMQ) 能够提供可持久的可靠性(可以把消息存放到硬盘上的,持久化的)
• 如果服务器端信道发生了错误,客户端只有在下次调用时才能发现这个错误

One-Way应用场景
• Fire-and-forget: 不需要报告成功与否的操作(发射后不管),如:日志活动的记录或者事件发布。
• Polling: 客户端通过另一个请求来获得长时间运行的操作的结果。即,轮循的。客户端会调用另一个请求去不停的去尝试查看服务端处理状态的场景。服务器端不会主动告诉客户端服务处理的结果,而中断和Callback的话,服务器端会主动告诉客户端服务处理的结果。
• Duplex: One-way操作特别适用于当涉及到回调操作时的双向通讯,后面具体讲解。。请看下文。。。。

Demo: OneWay

Service 代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;

namespace WcfServiceLibrary7
{
    [ServiceContract]
    
public interface IService
    {
        [OperationContract]
        
void ThrowException();

        [OperationContract(IsOneWay
=true)]
        
void ThrowExceptionOnWay();
    }

    
public class Service : IService
    {
        
public void ThrowException()
        {
            
throw new Exception("Exception IsOneWay is false");
        }

        
public void ThrowExceptionOnWay()
        {
            
throw new Exception("Exception IsOneWay is true");
        }
    }
}

 

Config代码
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  
<system.web>
    
<compilation debug="true" />
  
</system.web>
  
<!-- When deploying the service library project, the content of the config file must be added to the host's 
  app.config file. System.Configuration does not support config files for libraries. 
-->
  
<system.serviceModel>
    
<services>
      
<service behaviorConfiguration="WcfServiceLibrary7.Service1Behavior"
        name
="WcfServiceLibrary7.Service">
        
<endpoint address="" binding="wsHttpBinding" contract="WcfServiceLibrary7.IService">
          
<identity>
            
<dns value="localhost" />
          
</identity>
        
</endpoint>
        
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
        
<host>
          
<baseAddresses>
            
<add baseAddress="http://localhost:8731/Design_Time_Addresses/WcfServiceLibrary7/Service/" />
          
</baseAddresses>
        
</host>
      
</service>
    
</services>
    
<behaviors>
      
<serviceBehaviors>
        
<behavior name="WcfServiceLibrary7.Service1Behavior">
          
<!-- To avoid disclosing metadata information, 
          set the value below to false and remove the metadata endpoint above before deployment 
-->
          
<serviceMetadata httpGetEnabled="True"/>
          
<!-- To receive exception details in faults for debugging purposes, 
          set the value below to true.  Set to false before deployment 
          to avoid disclosing exception information 
-->
          
<serviceDebug includeExceptionDetailInFaults="true" />
        
</behavior>
      
</serviceBehaviors>
    
</behaviors>
  
</system.serviceModel>
</configuration>

 

Client 代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    
class Program
    {
        
static void Main(string[] args)
        {
            ServiceReference1.ServiceClient proxy 
= new ConsoleApplication1.ServiceReference1.ServiceClient();

            
try
            {
                proxy.ThrowException();
            }
            
catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }

            
try
            {
                proxy.ThrowExceptionOnWay();
            }
            
catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }

            
try
            {
                proxy.Close();
            }
            
catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
    }
}

 

上边的例子用来说明Request/Reply和OneWay的区别,运行后的结果:

Exception IsOneWay is false
The communication object, System.ServiceModel.Channels.ServiceChannel, cannot be
 used for communication because it is in the Faulted state.
The communication object, System.ServiceModel.Channels.ServiceChannel, cannot be
 used for communication because it is in the Faulted state.
Press any key to continue . . .

可以看到只有第一个Catch里的Exception显示出来了,OneWay的那个Exception没有出来,为什么那?我们可以看一下这个服务的wsdl,在IE里输入http://localhost:8731/Design_Time_Addresses/WcfServiceLibrary7/Service/ 可以但到这个服务,然后点击那个wsdl的http连接,注意这段

- <wsdl:message name="IService_ThrowException_InputMessage">
  <wsdl:part name="parameters" element="tns:ThrowException" />
  </wsdl:message>
- <wsdl:message name="IService_ThrowException_OutputMessage">
  <wsdl:part name="parameters" element="tns:ThrowExceptionResponse" />
  </wsdl:message>
- <wsdl:message name="IService_ThrowExceptionOnWay_InputMessage">
  <wsdl:part name="parameters" element="tns:ThrowExceptionOnWay" />
  </wsdl:message>
- <wsdl:portType name="IService">
- <wsdl:operation name="ThrowException">
  <wsdl:input wsaw:Action="http://tempuri.org/IService/ThrowException" message="tns:IService_ThrowException_InputMessage" />
  <wsdl:output wsaw:Action="http://tempuri.org/IService/ThrowExceptionResponse" message="tns:IService_ThrowException_OutputMessage" />
  </wsdl:operation>
- <wsdl:operation name="ThrowExceptionOnWay">
  <wsdl:input wsaw:Action="http://tempuri.org/IService/ThrowExceptionOnWay" message="tns:IService_ThrowExceptionOnWay_InputMessage" />
  </wsdl:operation>
  </wsdl:portType>

可以看出OneWay是没有output的,说明OneWay没有回应(response),不会报告异常。

 

Duplex操作
• 操作可以是request/reply类型或者one-way类型
• 在ServiceContractAttribute属性上提供回调契约
• WSDL只在带有回调契约的操作上包含<output>消息
• 必须与支持双向(duplex)通讯的绑定在一起部署

回调契约(1)
• 定义一个回调接口
•添加OperationContractAttribute属性到每个方法上
– ServiceContractAttribute不再需要
• 操作应该为one-way类型
public interface IHelloIndigoServiceCallback
{
     [OperationContract(IsOneWay=true)]
     void HelloIndigoCallback(string message);
}

回调契约(2)
• 将回调契约与服务契约联系起来
– CallbackContract 属性
• 触发回调的操作通常应该是one-way操作,或者在服务端打开重入(reentrancy)特性
[ServiceContract(Name="HelloIndigoContract", Namespace="http://www.thatindigogirl.com/samples/2006/06", CallbackContract=typeof(IHelloIndigoServiceCallback))]
public interface IHelloIndigoService
{
    [OperationContract(IsOneWay=true)]
    void HelloIndigo(string message);
}

双向绑定(Duplex Binding)
• 回调契约只能在支持双向通讯的端点上暴露
TCP与命名管道(named pipes)支持duplex
• HTTP无法提供传输级别的双向通讯
WSDualHttpBinding提供了两个信道以支持双向通讯
– 混合双向通讯
– 两个one-way WSHttpBinding 信道

有个概念要强调一下,上下文环境的重入,当客户端把一个context发给服务端时,服务端通过callback把context又传回客户端,如果上下文环境不可重入就会发生死锁,可重入又分为两种单线程的和多线程的,对于多线程的要程序员来自己控制线程的保护。

Duplex代理(1)对于代理类可以通过服务引用自动生成
• 代理生成器为客户端生成支持duplex通讯的基础代码与配置项:
– 为每个端点生成配置设置
–创建服务契约与回调契约的客户端版本
– 代理继承DuplexClientBase<T>来替代
ClientBase<T>
public partial class HelloIndigoContractClient :
System.ServiceModel.
DuplexClientBase<Client.localhost.HelloIndigoContract>,
Client.localhost.HelloIndigoContract
{…}

Duplex代理(2)
• 为代理提供回调实例
• 在类型上实现回调契约
class CallbackType: HelloIndigoContractCallback
{
    public void HelloIndigoCallback(string message)
    {
        Console.WriteLine("HelloIndigoCallback on thread{0}", Thread.CurrentThread.GetHashCode());
    }
}

Duplex代理(3)
• 在InstanceContext中包装类型的实例
CallbackType cb = new CallbackType();
InstanceContext context = new InstanceContext(cb);
using (HelloIndigoContractClient proxy = new HelloIndigoContractClient(context))
{
    proxy.HelloIndigo("Hello from Client.");
}

客户端端点(Client Endpoints)
• Duplex协议,如:TCP或者命名管道,为回调提供了backchannel
• WSDualHttpBinding提供了为回调端点设置基地址的方法
– 能够在配置项中显式地进行设置:
<wsDualHttpBinding>
<binding name="WSDualHttpBinding_HelloIndigoContract" clientBaseAddress "http://localhost:8100/" />

</wsDualHttpBinding>
– 能够设置代理的ClientBaseAddress属性

调用回调
• 在每次调用服务时,客户端的回调信道可用
• GetCallbackChannel<T>()返回一个强类型的引用
IHelloIndigoServiceCallback callback = OperationContext.Current.GetCallbackChannel<IHelloIndigoServiceCallback>();
callback HelloIndigoCallback(message);
• 可以用于对客户端的带外调用(out-of-band call),服务的调用请求排队Call1,Call2,Call3..., 此时有一个紧急的请求会排到Call1之前,这个请求就是out-of-band call,它是一种高优先级的调用请求。
–只在客户端信道的生命周期内有效

Demo:双向通讯

 

Service 代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;

namespace WcfServiceLibrary8
{
    [ServiceContract(CallbackContract 
= typeof(IMyServiceCallback))]
    
public interface IMyService
    {
        [OperationContract]
        
void Hello(string message);
    }

    [ServiceBehavior(ConcurrencyMode 
= ConcurrencyMode.Reentrant)]
    
public class MyService : IMyService
    {
        
public void Hello(string message)
        {
            IMyServiceCallback callback 
= OperationContext.Current.GetCallbackChannel<IMyServiceCallback>();
            callback.HelloCallBack(message);
        }
    }

    
public interface IMyServiceCallback
    {
        [OperationContract]
        
void HelloCallBack(string message);
    }
}

 

 

Config 代码
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  
<system.web>
    
<compilation debug="true" />
  
</system.web>
  
<!-- When deploying the service library project, the content of the config file must be added to the host's 
  app.config file. System.Configuration does not support config files for libraries. 
-->
  
<system.serviceModel>
    
<services>
      
<service behaviorConfiguration="WcfServiceLibrary8.Service1Behavior"
        name
="WcfServiceLibrary8.MyService">
        
<endpoint address="" binding="wsDualHttpBinding" contract="WcfServiceLibrary8.IMyService">
          
<identity>
            
<dns value="localhost" />
          
</identity>
        
</endpoint>
        
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
        
<host>
          
<baseAddresses>
            
<add baseAddress="http://localhost:8731/Design_Time_Addresses/WcfServiceLibrary8/Service/" />
          
</baseAddresses>
        
</host>
      
</service>
    
</services>
    
<behaviors>
      
<serviceBehaviors>
        
<behavior name="WcfServiceLibrary8.Service1Behavior">
          
<!-- To avoid disclosing metadata information, 
          set the value below to false and remove the metadata endpoint above before deployment 
-->
          
<serviceMetadata httpGetEnabled="True"/>
          
<!-- To receive exception details in faults for debugging purposes, 
          set the value below to true.  Set to false before deployment 
          to avoid disclosing exception information 
-->
          
<serviceDebug includeExceptionDetailInFaults="False" />
        
</behavior>
      
</serviceBehaviors>
    
</behaviors>
  
</system.serviceModel>
</configuration>

 

Clent Console app 代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;

namespace ConsoleApplication1
{
    
class Program
    {
        
static void Main(string[] args)
        {
            HelloCallback hcb 
= new HelloCallback();
            InstanceContext context
= new InstanceContext(hcb);

            
using (ServiceReference1.MyServiceClient proxy = new ConsoleApplication1.ServiceReference1.MyServiceClient(context))
            {
                proxy.Hello(
"Charles");
            }
        }
    }

    
public class HelloCallback : ServiceReference1.IMyServiceCallback
    {
        
public void HelloCallBack(string message)
        {
            Console.WriteLine(message 
+ " " + DateTime.Now);
        }
    }
}

 

 

Client win app代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.ServiceModel;

namespace WindowsFormsApplication1
{
    
public partial class Form1 : Form
    {
        ServiceReference1.MyServiceClient proxy;

        
public Form1()
        {
            InitializeComponent();
        }

        
private void button1_Click(object sender, EventArgs e)
        {
            HelloCallback hcb 
= new HelloCallback();
            InstanceContext context 
= new InstanceContext(hcb);

            proxy 
= new ServiceReference1.MyServiceClient(context);

            proxy.Hello(
"Charles");

        }
    }

    
[CallbackBehavior(UseSynchronizationContext=false)]
    
public class HelloCallback : ServiceReference1.IMyServiceCallback
    {
        
public void HelloCallBack(string message)
        {
            MessageBox.Show(message 
+ " " + DateTime.Now);
        }
    }
}

对于Win APP的callback要特别注意一下,如果callback用到了UI上的控件对象,要保证客户端的回调函数是在当前的UI线程里被调用的,因为UI上的控件对象只能在UI线程下工作。

 

发布/订阅(Publish/Subscribe):比如有一个Server,同时有多个客户端向他进行了注册,如果Server要有事情通知客户端的话,Server会根据Client对它的订阅的情况通知所有的客户端。应用比较广泛的是UI界面的更新,事件的通知。
• 回调能够用于短暂的“订阅/发布”场景
–服务端保存客户端的端点
– 在客户端信道关闭时失效
• 可靠的会话改进了消息传递的可靠性
• 使用MSMQ能够实现可持续的可靠性与更好的传递保证

 

下面通过一个Demo来介绍这部分的东西,学习的时候发现视频里的Code在下载的Demo code里没有,于是自己写了个简易的聊天工具作为Demo:

Service Interface 代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;

namespace WcfServiceLibrary9
{
    [ServiceContract(Namespace 
= "http://www.cnblogs.com/charlesliu", CallbackContract = (typeof(IPublisherEvents)))]
    
public interface IPublisherService
    {
        [OperationContract]
        
void Subscribe(Guid id);

        [OperationContract]
        
void Unsubscribe(Guid id);

        [OperationContract]
        
void NotifyAllSubscribers(string message);
    }

    
public interface IPublisherEvents
    {
        [OperationContract(IsOneWay
=true)]
        
void Notify(string message);
    }
}

 

 

Service Class代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System.Windows.Forms;

namespace WcfServiceLibrary9
{
    [ServiceBehavior(UseSynchronizationContext
=true,ConcurrencyMode=ConcurrencyMode.Reentrant)]
    
public class PublisherService : IPublisherService
    {
        
public void Subscribe(Guid id)
        {
            IPublisherEvents callback 
= OperationContext.Current.GetCallbackChannel<IPublisherEvents>();
            PublisherOperationHelper pubHelper 
= new PublisherOperationHelper();
            pubHelper.AddSubscriber(id, callback);
        }

        
public void Unsubscribe(Guid id)
        {
            IPublisherEvents callback 
= OperationContext.Current.GetCallbackChannel<IPublisherEvents>();
            PublisherOperationHelper pubHelper 
= new PublisherOperationHelper();
            pubHelper.RemoveSubscriber(id, callback);
        }

        
public void NotifyAllSubscribers(string message)
        {
            PublisherOperationHelper pubHelper 
= new PublisherOperationHelper();
            pubHelper.NotifyAllSubscribers(message);
        }
    }
}

 

 

PublisherOperationHelper 代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace WcfServiceLibrary9
{
    
internal class PublisherOperationHelper
    {
        
private static Dictionary<Guid, IPublisherEvents> m_listCallbacks = new Dictionary<Guid, IPublisherEvents>();

        
public void AddSubscriber(Guid id, IPublisherEvents e)
        {
            
if (!m_listCallbacks.ContainsKey(id))
            {
                m_listCallbacks.Add(id, e);
            }
        }

        
public void RemoveSubscriber(Guid id, IPublisherEvents e)
        {
            
if (m_listCallbacks.ContainsKey(id))
            {
                m_listCallbacks.Remove(id);
            }
        }

        
public void NotifyAllSubscribers(string message)
        {
            
foreach (KeyValuePair<Guid, IPublisherEvents> obj in m_listCallbacks)
            {
                obj.Value.Notify(message);
            }
        }
    }
}

 

 

Service config 代码
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  
<system.web>
    
<compilation debug="true" />
  
</system.web>
  
<!-- When deploying the service library project, the content of the config file must be added to the host's 
  app.config file. System.Configuration does not support config files for libraries. 
-->
  
<system.serviceModel>
    
<services>
      
<service behaviorConfiguration="WcfServiceLibrary9.Service1Behavior"
        name
="WcfServiceLibrary9.PublisherService">
        
<endpoint address="" binding="wsDualHttpBinding" contract="WcfServiceLibrary9.IPublisherService">
          
<identity>
            
<dns value="localhost" />
          
</identity>
        
</endpoint>
        
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
        
<host>
          
<baseAddresses>
            
<add baseAddress="http://localhost:8731/Design_Time_Addresses/WcfServiceLibrary9/Service1/" />
          
</baseAddresses>
        
</host>
      
</service>
    
</services>
    
<behaviors>
      
<serviceBehaviors>
        
<behavior name="WcfServiceLibrary9.Service1Behavior">
          
<!-- To avoid disclosing metadata information, 
          set the value below to false and remove the metadata endpoint above before deployment 
-->
          
<serviceMetadata httpGetEnabled="True"/>
          
<!-- To receive exception details in faults for debugging purposes, 
          set the value below to true.  Set to false before deployment 
          to avoid disclosing exception information 
-->
          
<serviceDebug includeExceptionDetailInFaults="False" />
        
</behavior>
      
</serviceBehaviors>
    
</behaviors>
  
</system.serviceModel>
</configuration>

 

 

Client 代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using WindowsFormsApplication1.MyServiceReference;
using System.ServiceModel;

namespace WindowsFormsApplication1
{
    [CallbackBehavior(UseSynchronizationContext
=false)]
    
public partial class Form1 : Form, IPublisherServiceCallback
    {
        
public Guid m_myGuid = Guid.NewGuid();

        PublisherServiceClient proxy;
        
        
public Form1()
        {
            InitializeComponent();
            InstanceContext callbackInstance 
= new InstanceContext(this);
            proxy 
= new PublisherServiceClient(callbackInstance);
        }
        
        
public void Notify(string message)
        {
            
this.listBox1.Items.Add(message + " - " + DateTime.Now.ToString());
        }

        
private void Form1_Load(object sender, EventArgs e)
        {
            proxy.Subscribe(m_myGuid);
        }

        
private void button1_Click(object sender, EventArgs e)
        {
            proxy.NotifyAllSubscribers(
this.textBox1.Text.Trim());
        }

        
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            proxy.Unsubscribe(m_myGuid);
        }
    }
}

上边的例子实际上是通过一个观察者模式,把每个客户端的订阅记录到一个list里,然后通过调用Notify方法,把list中的所有callback方法调用一遍,这样就实现了在所有clients端的UI更新。

 

大型消息
• 在绑定上指定消息大小的配额
– 能够增加对于较大消息的支持
• 下列操作会产生大消息
– 文件上传或者下载,二进制数据传输
– 大量的请求结果,XML数据
• 开销问题可以尝试下列解决方法:
– 消息传输优化机制(MTOM)
– 流传输
– 将消息切分成较小的分片

MTOM(WCF标准的大型消息解决方法)
• 降低二进制数据传输开销的互操作标准
–删除过多无用的信息并且处理base64编码所带来的数据开销
–改进了消息传输的整体性能
• SOAP消息以多用途互联网邮件扩展(MIME)的文档多部分相关(multipart/related)类型方式来发送

配置MTOM(使用很简单,只要作相应配置即可,代码是不用作任何修改,只是修改配置项。)
• 打开MTOM作为消息的编码器
• 相应地增加消息的配额
<wsHttpBinding>
<binding name="wsHttpLargeMessage" maxReceivedMessageSize="5000000" messageEncoding="Mtom">
<readerQuotas maxArrayLength="5000000"/>
</binding>
</wsHttpBinding>

流传输(1)
• MTOM处理传输开销以及性能问题
–整个消息仍然被加载到内存中
• 对于大量数据传输,数据流传递降低了内存的使用量
• 会使得某些特性有所损失,如:可靠性消息与消息的安全性

流传输(2)
• 配置操作以接收或返回一个合法的Stream类型
[ServiceContract(Name="MediaStreamingContract", Namespace = "http://www.thatindigogirl.com/samples/2006/06")]
public interface IMediaStreaming
{
    [OperationContract]
    Stream GetMediaStream(string media);
}
• 在绑定上打开流传输
<basicHttpBinding>
<binding name="basicHttpStreaming" transferMode="Streamed" />
</basicHttpBinding>

流传输(3)
Stream IMediaStreaming.GetMediaStream(string media)
{
  FileInfo fi = new FileInfo(media);
  FileStream fs = null;
  try
  {
    fs = new FileStream(mediaFile, FileMode.Open,
    FileAccess.Read, FileShare.Read);
  }
  catch
  {
    if (fs != null)
      fs.Close();
  }
  return fs;
}

流传输(4)
• 下列绑定支持数据流传输:
– NetNamedPipeBinding, NetTcpBinding, BasicHttpBinding
• 设置TransferMode 属性:
– Buffered
– Streamed
– StreamedRequest
– StreamedResponse

流传输(5)
• WCF在最后一次读取后会关闭数据流(当WCF服务端返回给客户端数据流到结尾的时候 或者 客户端向服务端传输数据流到结尾的时候 WCF会自动关闭数据流)
–用于服务返回数据流时
– 用于客户端发送数据流时
• 在最后读取之后,开发者必须关闭数据流 (与上边的相反的对应操作,只要记住读完了关,发完了不管)
– 用于客户端读取返回的数据流
– 用于服务读取进入的数据流

Demo:流方式

 

IMediaManagerService 代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;

namespace WcfServiceLibrary10
{
    [ServiceContract]
    
public interface IMediaManagerService
    {
        [OperationContract]
        
string[] GetMediaList();
    }
}

 

 

IMediaStreaming 代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.IO;

namespace WcfServiceLibrary10
{
    [ServiceContract]
    
public interface IMediaStreaming
    {
        [OperationContract]
        Stream GetMediaStream(
string media);
    }
}

 

Service Class 代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System.IO;

namespace WcfServiceLibrary10
{
    
// NOTE: If you change the class name "Service1" here, you must also update the reference to "Service1" in App.config.
    public class MediaManagerService : IMediaManagerService, IMediaStreaming
    {
        
public string[] GetMediaList()
        {
            
string[] mediaList = new string[3];
            mediaList[
0= "1.wav";
            mediaList[
1= "2.wav";
            mediaList[
2= "3.wav";

            
return mediaList;
        }
        
public Stream GetMediaStream(string media)
        {
            
string mediaFile = String.Format("{0}\\{1}", System.Configuration.ConfigurationSettings.AppSettings["mediaPath"], media);

            FileInfo fi 
= new FileInfo(mediaFile);
            
if (!fi.Exists)
            {
                
throw new FileNotFoundException("File Not Found");
            }

            FileStream fs 
= null ;
            
try
            {
                fs 
= new FileStream(mediaFile, FileMode.Open, FileAccess.Read, FileShare.Read);
            }
            
catch
            {
                
if (fs != null)
                {
                    fs.Close();
                }
            }
            
return fs;
        }
    }
}

 

 

Service Config代码
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  
<appSettings>
    
<add key="mediaPath" value="D:\Training Files\Silverlight\WcfServiceLibrary10\WcfServiceLibrary10\MediaPath"/>
  
</appSettings>
  
<system.web>
    
<compilation debug="true" />
  
</system.web>
  
<!-- When deploying the service library project, the content of the config file must be added to the host'
  app.config file. System.Configuration does not support config files for libraries. -->
  
<system.serviceModel>
    
<services>
      
<service behaviorConfiguration="WcfServiceLibrary10.Service1Behavior" name="WcfServiceLibrary10.MediaManagerService">
        
<endpoint address="MediaManager" binding="basicHttpBinding" contract="WcfServiceLibrary10.IMediaManagerService">
          
<identity>
            
<dns value="localhost" />
          
</identity>
        
</endpoint>
        
<endpoint contract="WcfServiceLibrary10.IMediaStreaming" binding="basicHttpBinding" bindingConfiguration="basicHttpStreaming"></endpoint>
        
<endpoint name="my_netTcpStreaming" contract="WcfServiceLibrary10.IMediaStreaming" binding="netTcpBinding" bindingConfiguration="netTcpStreaming"></endpoint>
        
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
        
<host>
          
<baseAddresses>
            
<add baseAddress="http://localhost:8731/Design_Time_Addresses/WcfServiceLibrary10/Service1/" />
            
<add baseAddress="net.tcp://localhost:8733/Design_Time_Addresses/WcfServiceLibrary10/Service1/ "/>
          
</baseAddresses>
        
</host>
      
</service>
    
</services>
    
<bindings>
      
<basicHttpBinding>
        
<binding name="basicHttpStreaming" messageEncoding="Mtom" maxReceivedMessageSize="40000000" transferMode="Streamed"></binding>
      
</basicHttpBinding>
      
<netTcpBinding>
        
<binding name="netTcpStreaming" transferMode="Streamed" maxReceivedMessageSize="40000000"></binding>
      
</netTcpBinding>
    
</bindings>
    
<behaviors>
      
<serviceBehaviors>
        
<behavior name="WcfServiceLibrary10.Service1Behavior">
          
<!-- To avoid disclosing metadata information, 
          
set the value below to false and remove the metadata endpoint above before deployment -->
          
<serviceMetadata httpGetEnabled="True"/>
          
<!-- To receive exception details in faults for debugging purposes, 
          
set the value below to true.  Set to false before deployment 
          to avoid disclosing exception information 
-->
          
<serviceDebug includeExceptionDetailInFaults="true" />
        
</behavior>
      
</serviceBehaviors>
    
</behaviors>
  
</system.serviceModel>
</configuration>

 

 

Client 代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Media;
using System.IO;

namespace WindowsFormsApplication1
{
    
public partial class Form1 : Form
    {
        SoundPlayer m_mediaPlayer 
= new SoundPlayer();

        
public Form1()
        {
            InitializeComponent();
        }

        MyServiceReference.MediaManagerServiceClient proxy_manager 
= new WindowsFormsApplication1.MyServiceReference.MediaManagerServiceClient();
        MyServiceReference.MediaStreamingClient proxy_stream 
= new WindowsFormsApplication1.MyServiceReference.MediaStreamingClient("my_netTcpStreaming");


        
private void button1_Click(object sender, EventArgs e)
        {
            
this.listBox1.DataSource = proxy_manager.GetMediaList();
        }

        
private void button2_Click(object sender, EventArgs e)
        {
            
string media = this.listBox1.SelectedItem as string;
            
if(!string.IsNullOrEmpty(media))
            {
                Stream serverStream 
= proxy_stream.GetMediaStream(media);
                
this.m_mediaPlayer.Stream = serverStream;
                
this.m_mediaPlayer.Play();
            }
        }

        
private void button3_Click(object sender, EventArgs e)
        {
            
this.m_mediaPlayer.Stop();
        }
    }
}

 

(完)

posted @ 2010-04-02 17:27  烟鬼  阅读(4098)  评论(0编辑  收藏  举报