WCF开山篇__图片传输

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文件,点击添加

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

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

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

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

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

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

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

发送端

1 namespace HolyKnight.PicPass.Client_SendPic  2 {  3     public partial class Send : Form  4     {  5         public Send()  6         {  7             InitializeComponent();  8         }  9  10         //选择图片事件 11         private void btn_selectPic_Click(object sender, EventArgs e) 12         { 13             string fileName = ""; 14             OpenFileDialog open = new OpenFileDialog(); 15             if (open.ShowDialog() == DialogResult.OK) 16             { 17                 //文本框显示图片路径 18                 fileName = open.FileName; 19                 //图片框显示图片 20                 pb_Pic.Load(fileName); 21             } 22             else 23             { 24                 return; 25             } 26  27             //新建一个文件流 读取图片信息 28             FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read); 29             //新建一个内存流 用于存放图片 30             Stream strPic = new MemoryStream(); 31             //设置位置为0 从头开始拷贝 32             fs.Position = 0; 33             //拷贝 34             fs.CopyTo(strPic); 35  36             //网络地址 37             EndpointAddress address = new EndpointAddress("net.tcp://192.168.29.223:8000/MyService"); 38  39             #region TCP 40  41             //设置 绑定方式为Tcp 42             NetTcpBinding tcpBind = new NetTcpBinding(); 43             //设置 用于存储消息的缓冲区的 最大大小 44             tcpBind.MaxBufferSize = 217736174; 45             //设置服务器 使用流式处理模式 传输消息 46             tcpBind.TransferMode = TransferMode.Streamed; 47             //设置绑定可以处理的最大接受消息大小 48             tcpBind.MaxReceivedMessageSize = 217736174; 49  50             tcpBind.Security.Mode = SecurityMode.None; 51  52             #endregion 53  54             //BasicHttpBinding httpBind = new BasicHttpBinding(); 55             //httpBind.MaxBufferSize = 1234567; 56             //httpBind.TransferMode = TransferMode.Streamed; 57             //httpBind.MaxReceivedMessageSize = 1234567; 58  59  60             //创建通信通道 61             IContracts client = ChannelFactory<IContracts>.CreateChannel(tcpBind, address); 62             //设置位置为0 63             strPic.Position = 0; 64             //调用SendPic方法 发送图片 65             client.SendPic(strPic); 66              67         } 68     } 69 }

代码语句都有对应注释,就不加赘述了,思路就是先获取到本地图片,然后转换成内存流,再连接到主机,创建代理类,通过代理类调用服务中的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。。。运行,测试都没有问题.....那么这个例子就算完成了..............

时间: 2024-10-10 11:07:25

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

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

前言:去年由于工作需要我学习了wcf的相关知识,初期对wcf的作用以及为何用怎么样都是一知半解,也许现在也不是非常的清晰.但是通过项目对wcf的运用在脑海里面也算有了初步的模型.今天我就把我从开始wcf所遇到的困难以及解决方案一点点记下来,可能还有很多不足之处那我们一起共同学习.在学习之前我们有必要初步的看下百度百科对wcf的解释,让我们心中有这一个概念,然后建立项目,然后再次看概念,然后在去更深层的建立项目,然后再去理解这样反复的去做我相信可以做好wcf.那么下面我就会从初识wcf,wcf消息

php应用篇-百度图片的防盗链

众所周知,一个网站如果的图片流量很占用网络带宽,因此,防止图片的被盗连就显得重中之重.这里,简单讲解下百度如何防止图片的盗链的原理,接着介绍下如何通过php来盗取图片,正所谓,道高一尺魔高一丈,防守和进攻总是不断地相互促进发展. 首先,简单介绍下盗链,按照百度百科的介绍"盗链是指服务提供商自己不提供服务的内容,通过技术手段绕过其它有利益的最终用户界面(如广告),直接在自己的网站上向最终用户提供其它服务提供商的服务内容,骗取最终用户的浏览和点击率.受益者不提供资源或提供很少的资源,而真正的服务提供

C#.NET通过Socket实现平行主机之间网络通讯(含图片传输的Demo演示)

在程序设计中,涉及数据存储和数据交换的时候,不管是B/S还是C/S模式,都有这样一个概念:数据库服务器.这要求一台性能和配置都比较好的主机作为服务器,以满足数目众多的客户端进行频繁访问.但是对于一些数据交换的要求不主同,而且涉及到的通讯个体数目不多,如果还采用“一主机多客户机”的模式,便要求一台硬件配置良好而且软件上安装了相关数据服务软件,这样会造成硬件和软件上的很多不必要的成本,这时Socket在点对点的平行对象之间的网络通讯的优势就就发挥出来了. 其实对于Socket通讯来说,服务器和客户端

C# Socket 简易的图片传输

关于网络的数据传输我就是个小白,所以今天学习一下简易的Socket图片传输. 客户端和服务器的连接咱们上次已经学过了,咱们先从简易的文件传输入手.下面开始代码分析了. Server.cs using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Drawing.Imaging; using Sy

黑马程序员——“编码人生”之开山篇

------Java培训.Android培训.iOS培训..Net培训.期待与您交流! ------- 一.自我介绍 1.称呼:肖某 2.性别:男 3.年龄:23(1992年) 4.职业:项目实施工程师 5.为什么要选择编码? 首先,我不排斥编码,反而还有点喜欢:其次,程序员的生活相对于项目实施要稳定许多,项目实施需要经常出差,年龄大了肯定受不了,到时候想转行都难,既然要转为何不早点转呢?最后,程序员的工资相比项目实施要高很多,程序员的不可替代性比项目实施要高. 二.学习笔记 在未来的10天里,

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

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

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

老老实实学WCF 第二篇 配置WCF 在上一篇中,我们在一个控制台应用程序中编写了一个简单的WCF服务并承载了它.先回顾一下服务端的代码: [csharp] view plaincopy using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; using System.ServiceModel.Description; name

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

老老实实学WCF 第九篇 消息通信模式(上) 请求应答与单向 通过前两篇的学习,我们了解了服务模型的一些特性如会话和实例化,今天我们来进一步学习服务模型的另一个重要特性:消息通信模式. WCF的服务端与客户端在通信时有三种模式:单向模式.请求/应答模式和双工模式. 如果选用了单向模式,调用方在向被调用方进行了调用后不期待任何回应,被调用方在执行完调用后不给调用方任何反馈.如客户端通过单向模式调用了一个服务端的操作后,就去干别的了,不会等待服务端给他任何响应,他也无从得知调用是否成功,甚至连发生了

使用Net.Sockets.TcpListener和Net.Sockets.TcpClient进行图片传输时如何精确控制接收缓存数组大小

<span style="font-size:18px;">在dotnet平台Net.Sockets.TcpListener和Net.Sockets.TcpClient已经为我们封装了所有Socket关于tcp部分,操作也更为简单,面向数据流.使用TcpClient的GetStream方法获取数据流后可以方便的对数据流进行读写操作,就如同本地磁盘的文件读写一样,使得程序员在设计程序时更为便捷简单.</span> 但如果你使用过这两个对象进行数据传输的时候,你会发