WCF开山篇__图片传输
一. 简介
二. 实例
今天要讲解的是一个WCF实现的小功能——图片传输,实现服务器端向客户端发送图片,客户端显示接收到的图片....闲话少说...直接切入正题......
WCF编程模式基于两个实体:WCF服务端、WCF客户端,但是这两者在通信的时候是基于某种契约,只有同时满足契约的条件的双方才能通信,这时就有了WCF服务....所以我们首先从契约入手.....
契约是基于一个定义服务与客户端之间协定的接口,它是用ServiceContractAttribute 属性来进行标记的;服务简单说,就是继承并实现了接口中定义的方法的一个类
首先贴上整个框架结构,如图:
【模块介绍】
Holyknihgt.PicPass.Client_SendPic:发送图片(客户端)
Holyknihgt.PicPass.Contract:定义契约(接口)
Holyknihgt.PicPass.ReceivePic:接受图片(客户端)
首先从契约入手,贴上代码:
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的问题,一开始在发送的方法中加上了
接下来贴上服务端的代码:
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开山篇__图片传输的更多相关文章
- 利用WCF与Android实现图片上传并传参
利用WCF与Android实现图片上传并传参 最近做一个项目后端使用WCF接收Android手机拍照并带其它参数保存到服务器里:刚好把最近学习的WCF利用上,本以为是个比较简单的功能应该很好实现,没想 ...
- 我们一起学习WCF 第一篇初识WCF(附源码供对照学习)
前言:去年由于工作需要我学习了wcf的相关知识,初期对wcf的作用以及为何用怎么样都是一知半解,也许现在也不是非常的清晰.但是通过项目对wcf的运用在脑海里面也算有了初步的模型.今天我就把我从开始wc ...
- [高并发]Java高并发编程系列开山篇--线程实现
Java是最早开始有并发的语言之一,再过去传统多任务的模式下,人们发现很难解决一些更为复杂的问题,这个时候我们就有了并发. 引用 多线程比多任务更加有挑战.多线程是在同一个程序内部并行执行,因此会对相 ...
- Android和WCF通信 - 大数据压缩后传输
Android和WCF通信 - 大数据压缩后传输 本帖来源:http://www.cnblogs.com/lykbk/archive/2013/08/15/3259045.html 最近一直在优化项目 ...
- [老老实实学WCF] 第九篇 消息通信模式(上) 请求应答与单向
老老实实学WCF 第九篇 消息通信模式(上) 请求应答与单向 通过前两篇的学习,我们了解了服务模型的一些特性如会话和实例化,今天我们来进一步学习服务模型的另一个重要特性:消息通信模式. WCF的服务端 ...
- [老老实实学WCF] 第二篇 配置WCF
老老实实学WCF 第二篇 配置WCF 在上一篇中,我们在一个控制台应用程序中编写了一个简单的WCF服务并承载了它.先回顾一下服务端的代码: using System; using System.Col ...
- [老老实实学WCF] 第一篇 Hello WCF
老老实实学WCF 第一篇 Hello WCF WCF(Windows Communication Foundation)是微软公司推出的面向服务技术的集大成者,涵盖继承了其之前发布的所有的分布式应用 ...
- 老老实实学习WCF[第二篇] 配置wcf
老老实实学WCF 第二篇 配置WCF 在上一篇中,我们在一个控制台应用程序中编写了一个简单的WCF服务并承载了它.先回顾一下服务端的代码: using System; using System.Col ...
- 老老实实学WCF[第一篇] Hell wcf
老老实实学WCF 第一篇 Hello WCF WCF(Windows Communication Foundation)是微软公司推出的面向服务技术的集大成者,涵盖继承了其之前发布的所有的分布式应用 ...
随机推荐
- QML的Window与ApplicationWindow
ApplicationWindow需要导入QtQuick.Controls Window需要导入QtQuick.Window . 默认不可见,需要设置visible:true才可见. 主要区别就是Ap ...
- 给sublime设置格式化代码的快捷键
sublime中自建的有格式化按钮: Edit -> Line -> Reindent 只是sublime并没有给他赋予快捷键,所以只需加上快捷键即可 Preference -& ...
- Convert DataFrame string complex i to j python // “Cloning” row or column vectors
https://stackoverflow.com/questions/30808286/convert-dataframe-string-complex-i-to-j-python https:// ...
- oracle 之 定时任务,存储过程和游标等语法案例
--定时任务 declare job20 number; begin sys.dbms_job.submit(job20,'test1;',sysdate,'sysdate+1'); end; --存 ...
- oracle函数之 minus
“minus”直接翻译为中文是“减”的意思,在Oracle中也是用来做减法操作的 Oracle的minus是按列进行比较的,所以A能够minus B的前提条件是结果集A和结果集B需要有相同的列数,且相 ...
- Docker save & load
docker save Estimated reading time: 1 minute Description Save one or more images to a tar archive (s ...
- 【译】第8节---EF Code First中配置类
原文:http://www.entityframeworktutorial.net/code-first/configure-classes-in-code-first.aspx 前面的章节中我们知道 ...
- SAP本地化-银企直连
SAP本地化-银企直连 http://blog.sina.com.cn/s/blog_a0de8cc80101dee1.html 一.发展历史 2011年,在SAP ECC6 Ehp5中,通过功能增强 ...
- HDU 4557 Tree(可持久化字典树 + LCA)
http://acm.hdu.edu.cn/showproblem.php?pid=4757 题意: 给出一棵树,每个结点有一个权值,现在有多个询问,每次询问包含x,y,z三个数,求出在x到y的路径上 ...
- 【BZOJ】3295: [Cqoi2011]动态逆序对
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3295 mamaya,弱鸡xrdog终于会写树套树啦.... 将树状数组中每一个节点看成一棵 ...