WCF开山篇__图片传输

 

一.  简介

Windows Communication Foundation(WCF)是由微软发展的一组数据通信的应用程序接口,可以翻译为Windows通讯接口,它是.NET框架的一部分,由 .NET Framework 3.0 开始引入,与 Windows Presentation Foundation及 Windows Workflow Foundation并行为新一代 Windows 操作系统以及 WinFX 的三个重大应用程序开发类库。WCF由于集合了几乎由.NET Framework所提供的通信方法,因此学习曲线比较陡峭,开发人员必须针对各个部分做深入了解,才能操控WCF来开发应用程序,通信的双方的沟通方式由合约来约定,双方所遵循的通信方法有绑定方法来约定。。。。。

二.  实例

今天要讲解的是一个WCF实现的小功能——图片传输,实现服务器端向客户端发送图片,客户端显示接收到的图片....闲话少说...直接切入正题......

WCF编程模式基于两个实体:WCF服务端、WCF客户端,但是这两者在通信的时候是基于某种契约,只有同时满足契约的条件的双方才能通信,这时就有了WCF服务....所以我们首先从契约入手.....

契约是基于一个定义服务与客户端之间协定的接口,它是用ServiceContractAttribute 属性来进行标记的;服务简单说,就是继承并实现了接口中定义的方法的一个类

首先贴上整个框架结构,如图:

【模块介绍】

Holyknihgt.PicPass.Client_SendPic:发送图片(客户端)

Holyknihgt.PicPass.Contract:定义契约(接口)

Holyknihgt.PicPass.Hosting:主机(服务端)

Holyknihgt.PicPass.ReceivePic:接受图片(客户端)

Holyknihgt.PicPass.Service:服务

首先从契约入手,贴上代码:

using System.ServiceModel;

using System.IO; 

namespace HolyKnight.PicPass.Contract

{

    [ServiceContract]

    public interface IContracts

    {

        //获取图片

        [OperationContract]

        Stream GetPic(); 

        //发送图片

        [OperationContract]

        void SendPic(Stream picPass); 

    }

}

契约中主要定义了两个方法:获取图片方法(GetPic) 和 发送图片方法(SendPic),并添加了对应的ServiceContractAttribute特性

接下去就是对这个接口的实现,即服务,贴上代码:

using HolyKnight.PicPass.Contract;

namespace HolyKnight.PicPass.Service
{
public class MyService:IContracts
{
//定义一个静态内存流 用于存储图片
public static Stream picStream = new MemoryStream(); //获取图片
public Stream GetPic()
{
//实例化一个内存流对象
MemoryStream ms = new MemoryStream(); //设置静态内存流的位置为0 为了后面进行拷贝操作时可以从头开始拷贝内容
//【这里的Position不一样了,用了多态的思想,子类已经重写了父类的Position属性】
picStream.Position = ; //把内存流中的内容拷贝到当前内存流中
picStream.CopyTo(ms); //返回内存流对象
return ms;
} //发送图片
public void SendPic(Stream picPass)
{
//【出错】:设置位置为0 -- 因为stream类的position属性是抽象属性的 不能直接复制使用
//picPass.Position = 0; //【再次报错】--考虑不全:下面这句代码没指定,所以发送的时候没有把Position设为0 而接受的时候一直是从0开始接受 所以一直接受不到后面发送的图片
picStream.Position = ; //拷贝到静态内存流中 保存
picPass.CopyTo(picStream);
}

这块对我来说是"重灾区",因为我一开始在这个地方出了两次同样的错误,,悲哀,,错误已经在注释中写明了,,,就是Position的问题,一开始在发送的方法中加上了

picPass.Position = 0;这句代码,后来运行一直报内部错误,经过几次调试之后终于发现了错误点,就是这个Position造成的,但一开始始终不明白为什么会报错呢??我的想法是把picPass的Position设置为0,可以实现从头开始拷贝,,,但事实证明我想错了,,通过对Stream类转到定义(F12)查看才发现了特殊的地方,因为Position是Abstract,是抽象属性,不能直接对抽象属性赋值.....这样总算之后为什么错了,,,当然获取图片方法中也有Position属性,Stream picStream = new MemoryStream()也有Position,为什么这里不报错呢,,,这就是运用了多态了,其实他们都是Stream的一个派生类,派生类重写了父类的Position属性了,有了自身的扩展了,所以可以对其赋值了,,,代码中还会碰到FileStream类,也是同样道理。。第二个错误就是我在发送方法中漏写了picStream.Position = 0;没了这句话,给我的结果是程序都可以跑起来,,但是我发送你第一张图片的时候,接受端可以接到,但当我后面再次发送不同图片的时候,我却怎么都接不到了,页面显示的始终是第一张图片,,为什么呢???而又不报错,很难找错,,终于,黄天不负有心人啊,,经过百般的调试之后终于发现了这个错误了,,但为什么会这样呢??原来,当你发送完一张图片的时候,由于图片是存放到内存流里的,图片大小不一样,放到内存之后的Position也是不一样的,随着图片的增加而增加,但我接收端接受的是Position=0处的图片,所以只有第一张图片的position为0的,故我只能接受到第一张图片了,加了这句代码之后,就是说我发送的时候是发送到Position=0的位置,接收的时候也是接收Position=0位置的图片,这样就没有任何问题了,,好了,至此,"重灾区"的两个问题都解决了.....

接下来贴上服务端的代码:

class Hosting
{
static void Main(string[] args)
{
//设置 绑定方式为Tcp
NetTcpBinding tcpBind = new NetTcpBinding();
//设置 用于存储消息的缓冲区的 最大大小
tcpBind.MaxBufferSize = ;
//设置服务器 使用流式处理模式 传输消息
tcpBind.TransferMode = TransferMode.Streamed;
//设置绑定可以处理的最大接受消息大小
tcpBind.MaxReceivedMessageSize = ; tcpBind.Security.Mode = SecurityMode.None; //BasicHttpBinding httpBind = new BasicHttpBinding();
//httpBind.MaxBufferSize = 1234567;
//httpBind.TransferMode = TransferMode.Streamed;
//httpBind.MaxReceivedMessageSize = 1234567; //发布
//新建服务主机
using (ServiceHost host = new ServiceHost(typeof(MyService)))
{
//为主机添加服务终结点 配置ABC A:address B:bind C:contract 并且基址为net.tcp地址
host.AddServiceEndpoint(typeof(IContracts), tcpBind, "net.tcp://192.168.29.223:8000/MyService"); //控制服务行为
ServiceMetadataBehavior behavior = host.Description.Behaviors.Find<ServiceMetadataBehavior>();
//如果为空 则实例化一个对象 并设置对应属性
if (behavior == null)
{
behavior = new ServiceMetadataBehavior();
behavior.HttpGetEnabled = true; //这里的地址为http地址 且端口号不能和上面定义的一样 否则会端口占用
behavior.HttpGetUrl = new Uri("http://192.168.29.223:9999/MyService/Medata"); //将该行为添加到主机
host.Description.Behaviors.Add(behavior);
} host.Opened += delegate
{
Console.WriteLine("图片传输服务已启动,按任意键关闭服务.....");
}; //打开通信
host.Open(); Console.ReadKey();
}
}

服务端很简单,大致过程为先添加一个服务主机,参数为服务的类型;然后为该服务主机添加一个服务终结点,服务终结点要配置三个信息即A,B,C,其中A就是Address,表示基址地址,由于这里是通过tcp传输,所以这里的地址为tcp地址,并设定端口号;B就是Binding,表示绑定的类型和方式;C就是Contract,表示终结点定义的契约,这三点配置好了终结点算配置好了,然后添加一个服务行为behavior,首先查询主机中是否含有了服务行为,如果没有则添加一个行为,并设置该行为的HttpGetEnabled属性为true,表示可以通过Http来检索发布的元数据,并设置元数据发布的地址HttpGetUrl,注意这里的地址是Http地址,并设定端口,这里的端口不能和上面的基址的端口相同,否则会端口占用,然后将该行为添加到主机就完成了配置了,然后利用Opened事件,用匿名函数来打印一段信息,然后打开通信,这样服务器端就配置完成了,代码上的语句都做了相应的注释....

当然服务端我们也可以不用手写代码的方式,我们可以选择使用配置文件,下面讲讲使用配置文件来搭建主机

首先我们为主机模块添加一个应用程序配置文件app.config,然后利用vs工具中的wcf配置工具打开该配置文件,如图:

然后进去配置界面,首先新建一个服务

然后进入服务配置界面,选择对应的服务的dll文件,点击添加

然后选择该服务即可,然后点击下一步,会自动为你匹配好对应的契约,直接下一步,到基址配置,如图:

输入正确的基址地址,服务就算建好了.....然后添加服务行为,如图:

然后选择添加,然后选择对应的服务元数据项

然后双击该元数据项,进行配置,配置如图:

最后再在主机中添加该配置好的行为就可以了,,如图:

这样,我们的配置文件算是配好了,,贴上配置文件结果:

服务端就告一段落了,接下来搭建我们的客户端,首先搭建发送端,贴上代码:

namespace HolyKnight.PicPass.Client_SendPic
{
public partial class Send : Form
{
public Send()
{
InitializeComponent();
} //选择图片事件
private void btn_selectPic_Click(object sender, EventArgs e)
{
string fileName = "";
OpenFileDialog open = new OpenFileDialog();
if (open.ShowDialog() == DialogResult.OK)
{
//文本框显示图片路径
fileName = open.FileName;
//图片框显示图片
pb_Pic.Load(fileName);
}
else
{
return;
} //新建一个文件流 读取图片信息
FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
//新建一个内存流 用于存放图片
Stream strPic = new MemoryStream();
//设置位置为0 从头开始拷贝
fs.Position = ;
//拷贝
fs.CopyTo(strPic); //网络地址
EndpointAddress address = new EndpointAddress("net.tcp://192.168.29.223:8000/MyService"); #region TCP //设置 绑定方式为Tcp
NetTcpBinding tcpBind = new NetTcpBinding();
//设置 用于存储消息的缓冲区的 最大大小
tcpBind.MaxBufferSize = ;
//设置服务器 使用流式处理模式 传输消息
tcpBind.TransferMode = TransferMode.Streamed;
//设置绑定可以处理的最大接受消息大小
tcpBind.MaxReceivedMessageSize = ; tcpBind.Security.Mode = SecurityMode.None; #endregion //BasicHttpBinding httpBind = new BasicHttpBinding();
//httpBind.MaxBufferSize = 1234567;
//httpBind.TransferMode = TransferMode.Streamed;
//httpBind.MaxReceivedMessageSize = 1234567; //创建通信通道
IContracts client = ChannelFactory<IContracts>.CreateChannel(tcpBind, address);
//设置位置为0
strPic.Position = ;
//调用SendPic方法 发送图片
client.SendPic(strPic); }
}
}

代码语句都有对应注释,就不加赘述了,思路就是先获取到本地图片,然后转换成内存流,再连接到主机,创建代理类,通过代理类调用服务中的SendPic方法,实现发送图片

再贴上接收端的代码,思路同发送端雷同,调用服务的GetPic方法:

namespace HolyKnight.PicPass.ReceivePic
{
public partial class Receive : Form
{
public Receive()
{
InitializeComponent();
} public void ShowPic()
{
//网络地址
EndpointAddress address = new EndpointAddress("net.tcp://192.168.29.223:8000/MyService"); #region TCP //设置 绑定方式为Tcp
NetTcpBinding tcpBind = new NetTcpBinding(); //设置 用于存储消息的缓冲区的 最大大小
tcpBind.MaxBufferSize = ; //设置服务器 使用流式处理模式 传输消息
tcpBind.TransferMode = TransferMode.Streamed; //设置绑定可以处理的最大接受消息大小
tcpBind.MaxReceivedMessageSize = ; //设置无安全性检验
tcpBind.Security.Mode = SecurityMode.None; #endregion #region HTTP //BasicHttpBinding httpBind = new BasicHttpBinding();
//httpBind.MaxBufferSize = 1234567;
//httpBind.TransferMode = TransferMode.Streamed;
//httpBind.MaxReceivedMessageSize = 1234567; #endregion //获取服务代理类
IContracts prox = ChannelFactory<IContracts>.CreateChannel(tcpBind, address); //用死循环持续监听
while (true)
{
//通过调用GetPic()方法获取图片
Stream strPic = prox.GetPic(); //新建一个内存流
MemoryStream ms = new MemoryStream(); //将获取到得图片拷贝到内存流
strPic.CopyTo(ms);
if (ms.Length == )
{
//线程阻塞300毫秒
System.Threading.Thread.Sleep();
continue;
}
//将图片信息加载到位图
Bitmap bm = new Bitmap(ms); //显示图片
pb_showPic.Image = bm; //线程阻塞
System.Threading.Thread.Sleep(); } } private void Form1_Load(object sender, EventArgs e)
{
//利用线程监听GetPic方法
Thread threadPic = new Thread(ShowPic);
threadPic.IsBackground = true;
threadPic.Start();
}
}
}

这样,我们服务端,客户端的配置都完成了,接下来看看演示效果......直接上图:

首先打开服务:

然后再开启两个客户端,首先贴上客户端页面:

发送端:

接收端:

然后开始发送图片:

测试:我们在客户端更换图片

OK。。。运行,测试都没有问题.....那么这个例子就算完成了..............

WCF开山篇__图片传输的更多相关文章

  1. 利用WCF与Android实现图片上传并传参

    利用WCF与Android实现图片上传并传参 最近做一个项目后端使用WCF接收Android手机拍照并带其它参数保存到服务器里:刚好把最近学习的WCF利用上,本以为是个比较简单的功能应该很好实现,没想 ...

  2. 我们一起学习WCF 第一篇初识WCF(附源码供对照学习)

    前言:去年由于工作需要我学习了wcf的相关知识,初期对wcf的作用以及为何用怎么样都是一知半解,也许现在也不是非常的清晰.但是通过项目对wcf的运用在脑海里面也算有了初步的模型.今天我就把我从开始wc ...

  3. [高并发]Java高并发编程系列开山篇--线程实现

    Java是最早开始有并发的语言之一,再过去传统多任务的模式下,人们发现很难解决一些更为复杂的问题,这个时候我们就有了并发. 引用 多线程比多任务更加有挑战.多线程是在同一个程序内部并行执行,因此会对相 ...

  4. Android和WCF通信 - 大数据压缩后传输

    Android和WCF通信 - 大数据压缩后传输 本帖来源:http://www.cnblogs.com/lykbk/archive/2013/08/15/3259045.html 最近一直在优化项目 ...

  5. [老老实实学WCF] 第九篇 消息通信模式(上) 请求应答与单向

    老老实实学WCF 第九篇 消息通信模式(上) 请求应答与单向 通过前两篇的学习,我们了解了服务模型的一些特性如会话和实例化,今天我们来进一步学习服务模型的另一个重要特性:消息通信模式. WCF的服务端 ...

  6. [老老实实学WCF] 第二篇 配置WCF

    老老实实学WCF 第二篇 配置WCF 在上一篇中,我们在一个控制台应用程序中编写了一个简单的WCF服务并承载了它.先回顾一下服务端的代码: using System; using System.Col ...

  7. [老老实实学WCF] 第一篇 Hello WCF

    老老实实学WCF  第一篇 Hello WCF WCF(Windows Communication Foundation)是微软公司推出的面向服务技术的集大成者,涵盖继承了其之前发布的所有的分布式应用 ...

  8. 老老实实学习WCF[第二篇] 配置wcf

    老老实实学WCF 第二篇 配置WCF 在上一篇中,我们在一个控制台应用程序中编写了一个简单的WCF服务并承载了它.先回顾一下服务端的代码: using System; using System.Col ...

  9. 老老实实学WCF[第一篇] Hell wcf

    老老实实学WCF  第一篇 Hello WCF WCF(Windows Communication Foundation)是微软公司推出的面向服务技术的集大成者,涵盖继承了其之前发布的所有的分布式应用 ...

随机推荐

  1. vc++读取文件属性的详细信息描述 通过读取QQ的注册表和EXE路径两种方式

    转载:http://www.cnblogs.com/pythonschool/archive/2012/10/18/2729872.html // File: GetFileVersion.cpp / ...

  2. topcoder srm list

    300 305 310 315 320 325 330 335 340 350 360 370 380 390 400 410 415 420 425 430 435 440 445 450 455 ...

  3. Oracle错误——ORA-01691: Lob 段SFZXP.SYS_LOB0000030381C00004$$无法通过8192(在表空间USERS中)扩展

    问题 Oracle报错:ORA-01691: Lob 段SFZXP.SYS_LOB0000030381C00004$$无法通过8192(在表空间USERS中)扩展 问题原因 Oracle数据表空间不足 ...

  4. php高级开发参考地址

    高级开发 : http://www.cnblogs.com/bananaplan/p/The-Right-Way-For-PHPer.html

  5. 关于 RabbitMQ 的 Dead-Letters-Queue “死信队列”

      来自一个队列的消息可以被当做‘死信’,即被重新发布到另外一个“exchange”去,这样的情况有: 消息被拒绝 (basic.reject or basic.nack) 且带 requeue=fa ...

  6. Unsupervised Image-to-Image Translation Networks --- Reading Writing

    Unsupervised Image-to-Image Translation Networks --- Reading Writing 2017.03.03 Motivations: most ex ...

  7. Java中sort实现降序排序

    利用Collections的reverseOrder方法: import java.util.Arrays; import java.util.Collections; public class Ma ...

  8. eclipse创建maven web项目工程步骤示例

    参考链接:https://www.cnblogs.com/noteless/p/5213075.html 需求表均同springmvc案例 此处只是使用maven 注意,以下所有需要建立在你的ecli ...

  9. 1、My Scripts

    1.写一个包含命令.变量和流程控制的语句来清除/var/log的messages日志文件的shell脚本.(P26)(11-21) 2.利用$0和(dirname.basename)取出当前路径的目录 ...

  10. WinForm 中 comboBox控件数据绑定

    一.IList 现在我们直接创建一个List集合,然后绑定 IList<string> list = new List<string>(); list.Add("11 ...