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:服务

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

 1 using System.ServiceModel;
 2 using System.IO;
 3 
 4 namespace HolyKnight.PicPass.Contract
 5 {
 6     [ServiceContract]
 7     public interface IContracts
 8     {
 9         //获取图片
10         [OperationContract]
11         Stream GetPic();
12 
13         //发送图片
14         [OperationContract]
15         void SendPic(Stream picPass);
16 
17     }
18 }

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

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

 1 using HolyKnight.PicPass.Contract;
 2 
 3 namespace HolyKnight.PicPass.Service
 4 {
 5     public class MyService:IContracts
 6     {
 7         //定义一个静态内存流 用于存储图片
 8         public static Stream picStream = new MemoryStream();
 9 
10         //获取图片
11         public Stream GetPic()
12         {
13             //实例化一个内存流对象
14             MemoryStream ms = new MemoryStream();
15 
16             //设置静态内存流的位置为0 为了后面进行拷贝操作时可以从头开始拷贝内容
17             //【这里的Position不一样了,用了多态的思想,子类已经重写了父类的Position属性】
18             picStream.Position = 0;
19 
20             //把内存流中的内容拷贝到当前内存流中
21             picStream.CopyTo(ms);
22 
23             //返回内存流对象
24             return ms;
25         }
26 
27         //发送图片
28         public void SendPic(Stream picPass)
29         {
30             //【出错】:设置位置为0 --  因为stream类的position属性是抽象属性的 不能直接复制使用
31             //picPass.Position = 0;
32 
33             //【再次报错】--考虑不全:下面这句代码没指定,所以发送的时候没有把Position设为0 而接受的时候一直是从0开始接受 所以一直接受不到后面发送的图片
34             picStream.Position = 0;
35 
36             //拷贝到静态内存流中 保存
37             picPass.CopyTo(picStream);
38         }
39   

这块对我来说是"重灾区",因为我一开始在这个地方出了两次同样的错误,,悲哀,,错误已经在注释中写明了,,,就是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位置的图片,这样就没有任何问题了,,好了,至此,"重灾区"的两个问题都解决了.....

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

 1     class Hosting
 2     {
 3         static void Main(string[] args)
 4         {
 5             //设置 绑定方式为Tcp
 6             NetTcpBinding tcpBind = new NetTcpBinding();
 7             //设置 用于存储消息的缓冲区的 最大大小
 8             tcpBind.MaxBufferSize = 217736174;
 9             //设置服务器 使用流式处理模式 传输消息
10             tcpBind.TransferMode = TransferMode.Streamed;
11             //设置绑定可以处理的最大接受消息大小
12             tcpBind.MaxReceivedMessageSize = 217736174;
13 
14             tcpBind.Security.Mode = SecurityMode.None;
15 
16             //BasicHttpBinding httpBind = new BasicHttpBinding();
17             //httpBind.MaxBufferSize = 1234567;
18             //httpBind.TransferMode = TransferMode.Streamed;
19             //httpBind.MaxReceivedMessageSize = 1234567;
20 
21             //发布
22             //新建服务主机
23             using (ServiceHost host = new ServiceHost(typeof(MyService)))
24             {
25                 //为主机添加服务终结点 配置ABC A:address B:bind C:contract  并且基址为net.tcp地址
26                 host.AddServiceEndpoint(typeof(IContracts), tcpBind, "net.tcp://192.168.29.223:8000/MyService");
27 
28                 //控制服务行为
29                 ServiceMetadataBehavior behavior = host.Description.Behaviors.Find<ServiceMetadataBehavior>();
30                 //如果为空 则实例化一个对象 并设置对应属性
31                 if (behavior == null)
32                 {
33                     behavior = new ServiceMetadataBehavior();
34                     behavior.HttpGetEnabled = true;
35 
36                     //这里的地址为http地址 且端口号不能和上面定义的一样 否则会端口占用
37                     behavior.HttpGetUrl = new Uri("http://192.168.29.223:9999/MyService/Medata");
38 
39                     //将该行为添加到主机
40                     host.Description.Behaviors.Add(behavior);
41                 }
42 
43                 host.Opened += delegate
44                 {
45                     Console.WriteLine("图片传输服务已启动,按任意键关闭服务.....");
46                 };
47 
48                 //打开通信
49                 host.Open();
50 
51                 Console.ReadKey();
52             }
53         }

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

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

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

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

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

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

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

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

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

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

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

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

发送端

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

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

 1 namespace HolyKnight.PicPass.ReceivePic
 2 {
 3     public partial class Receive : Form
 4     {
 5         public Receive()
 6         {
 7             InitializeComponent();
 8         }
 9 
10         public void ShowPic()
11         {
12             //网络地址
13             EndpointAddress address = new EndpointAddress("net.tcp://192.168.29.223:8000/MyService");
14 
15             #region TCP
16 
17             //设置 绑定方式为Tcp
18             NetTcpBinding tcpBind = new NetTcpBinding();
19 
20             //设置 用于存储消息的缓冲区的 最大大小
21             tcpBind.MaxBufferSize = 217736174;
22 
23             //设置服务器 使用流式处理模式 传输消息
24             tcpBind.TransferMode = TransferMode.Streamed;
25 
26             //设置绑定可以处理的最大接受消息大小
27             tcpBind.MaxReceivedMessageSize = 217736174;
28 
29             //设置无安全性检验
30             tcpBind.Security.Mode = SecurityMode.None;
31 
32             #endregion
33 
34             #region HTTP
35 
36             //BasicHttpBinding httpBind = new BasicHttpBinding();
37             //httpBind.MaxBufferSize = 1234567;
38             //httpBind.TransferMode = TransferMode.Streamed;
39             //httpBind.MaxReceivedMessageSize = 1234567;
40 
41             #endregion
42 
43             //获取服务代理类
44             IContracts prox = ChannelFactory<IContracts>.CreateChannel(tcpBind, address);
45            
46             //用死循环持续监听
47             while (true)
48             {
49                 //通过调用GetPic()方法获取图片
50                 Stream strPic = prox.GetPic();
51 
52                 //新建一个内存流
53                 MemoryStream ms = new MemoryStream();
54 
55                 //将获取到得图片拷贝到内存流
56                 strPic.CopyTo(ms);
57                 if (ms.Length == 0)
58                 {
59                     //线程阻塞300毫秒
60                     System.Threading.Thread.Sleep(300);
61                     continue;
62                 }
63                 //将图片信息加载到位图
64                 Bitmap bm = new Bitmap(ms);
65 
66                 //显示图片
67                 pb_showPic.Image = bm;
68 
69                 //线程阻塞
70                 System.Threading.Thread.Sleep(300);
71 
72             }
73 
74 
75         }
76 
77         private void Form1_Load(object sender, EventArgs e)
78         {
79             //利用线程监听GetPic方法
80             Thread threadPic = new Thread(ShowPic);
81             threadPic.IsBackground = true;
82             threadPic.Start();
83         }
84     }
85 }

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

首先打开服务:

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

发送端:

接收端:

然后开始发送图片:

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

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

http://www.cnblogs.com/holyknight-zld/archive/2012/08/09/wcf_uploadpic.html相关推荐

  1. 第四次游戏革命:全息游戏 from:http://www.cnblogs.com/alamiye010/archive/2012/08/15/2640881.html...

    第四次游戏革命:全息游戏 最近一个月,把国内外十数款单机/网游大作横扫一遍,感慨颇多.国内游戏,抄袭遍地,十足的坑爹,浪费青春.反观国外,韩国网游经典而耐玩,<C9>+<洛奇英雄传& ...

  2. github上的优秀项目和开发环境配置【转http://www.cnblogs.com/2018/archive/2012/11/09/2763119.html】...

    github上的优秀项目和开发环境配置 国外的几个公司开放的资源  https://github.com/google https://github.com/facebook  https://git ...

  3. CBitMap的用法 from http://www.cnblogs.com/toconnection/archive/2012/08/04/mfc.html

    CBitMap的用法 MFC提供了位图处理的基础类CBitmap,可以完成位图(bmp图像)的创建.图像数据的获取等功能.虽然功能比较少,但是在对位图进行一些简单的处理时,CBitmap类还是可以胜任 ...

  4. 迷难的北京行 – 2012.08.19

    内容中包含 base64string 图片造成字符过多,拒绝显示 转载于:https://www.cnblogs.com/hehe123/archive/2012/08/21/html5dw_trav ...

  5. Daily Report 2012/11/09 陈伯雄(step 9)

    今天的工作是完成把之前建立的倒排索引和数据库搜索匹配模块嵌入到主体工程中,等待运行和测试. 但是,现在的数据库搜索方法精度还不够,天真把每个关键词一视同仁地处理了,这样的后果可能回造成用户搜索体验不佳 ...

  6. Daily Scrum 2012/12/09

    TeamSHIT 欠着一一片Scrum Meeting,本来说是要昨天补的,奈何昨天出去吃饭是醉回来,所以-- 经过周末的突击,Pipeline的大致部分已经完成,中文网页用的是朴素的贝叶斯分词,英文 ...

  7. [Buzz Today]2012.08.08

    # Dark Reign 2 源代码现身Google Code Pandemic工作室开发的即时战略游戏<Dark Reign 2>源代码被泄露到了Google Code http://c ...

  8. mysql基本操作 [http://www.cnblogs.com/ggjucheng/archive/2012/11/03/2752082.html]

    创建表 简单的方式 CREATE TABLE person ( number INT(11), name VARCHAR(255), birthday DATE ); 或者是 CREATE TABLE ...

  9. http://www.cnblogs.com/ITtangtang/archive/2012/05/21/2511749.html

    http://www.cnblogs.com/ITtangtang/archive/2012/05/21/2511749.html http://blog.sina.com.cn/s/blog_538 ...

最新文章

  1. 用Handler的post()方法来传递线程中的代码段到主线程中执行
  2. html防止iOS将数字识别为电话号码
  3. 【Python】Python字典的高级用法-统计计数
  4. 二维高斯曲面拟合法求取光斑中心及算法的C++实现
  5. 微课|中学生可以这样学Python(例8.25):二分法查找
  6. DS实验题 最大最小
  7. RSA加密与签名的区别
  8. 一个免费提升独立站转化率神器-tidio实时在线客服聊天工具
  9. RSA非对称加密算法介绍及其简单数学原理
  10. Lisp-Stat翻译 —— 第十章 一些动态绘图实例
  11. M2M、物联网应用开发的好助手——Wavecom Sierra 无线MODEM( GSM/GPRS/EDGE MODEM)
  12. 【快应用】菜单遮挡内容?教你一招快速搞定!
  13. 快给你的对象做一个微信公众号播报吧-java版
  14. ICP算法进行点云匹配
  15. NetworkX学习及使用
  16. 蚂蚁双链通:基于区块链的供应链协作网络
  17. 微信开放平台扫码登陆
  18. OpenCV 分割斜体文字(包括旋转,垂直投影,水平投影,透视变换等)
  19. android dumpsys 分析,Android内存分析工具-dumpsys meminfo
  20. JS案例-仿京东放大镜效果

热门文章

  1. python入门之文件的读写
  2. matlab中的length函数
  3. 面向企业应用的Android开发从入门到精通
  4. PTA 天梯赛L1-035 情人节 (15 分) C语言AC题解【仅供学习交流】
  5. (信号与系统 作业)卷积积分结合律性质的证明
  6. 八.面向对象的编程(下)
  7. 虚拟串口软件和串口调试助手的简单使用
  8. query和params传参的区别
  9. 第一讲 详解实现Typecho前台登录
  10. 开发新产品离不开CRM需求分析