WCF服务编程中使用SvcMap实现类型共享等技巧

【菜科解读】
调用过WCF服务的同学可能都会遇到这样的问题,同一个实体类型,不同的服务Visual Studio生成了不同的版本,例如Service1.User和Service2.User,对于C#来说,这是两个不同的类型,Service1获得的User是放不到Service2服务里去的。
手动的属性赋值来转换显然是不可取的,所以就共享类型了。
方法一、服务端和客户端共享数据契约程序集
这个方法最常用,也是大家最熟悉的方法,把WCF的数据契约放在一个独立类库里,服务端,客户端都引用这个程序集,然后在生成WCF时,选择重新使用引用程序集中的类型即可。
这个方法缺点很明显,它只有在客户端和服务端在同一个Visual Studio解决方案内才方便,否则要不断手动更新数据契约程序集。
更不用说是第三方的服务。
方法二,暴力转换类型
这个其实不是类型共享,不过也是解决这个问题的一种手段。
就是借助AutoMapper,EmitMapper这样的类库帮助快速转换类型。
下面是一个例子。
Money类型包含User实体和Currency枚举和一个数字的Amount,Money的定义
[DataContract(Namespace = Consts.Namespace)]public class Money [DataMember] public decimal Amount { get; set; } [DataMember] public Currency Currency { get; set; } [DataMember] public UserInfo User { get; set; }}Currency:
[DataContract(Namespace = Consts.Namespace)]public enum Currency [EnumMember] Euro, [EnumMember] Usd, [EnumMember] PoundSterling}UserInfo:
[DataContract(Namespace = Consts.Namespace)]public class UserInfo [DataMember] public string FirstName { get; set; } [DataMember] public string LastName { get; set; } [DataMember] public string Email { get; set; } [DataMember] public string Phone { get; set; } [DataMember] public string Id { get; set; }}对于DepositServiceNoSharp和WithdrawalServiceNoSharp这两个WCF服务版本的Money和User,可以这样添加一些扩展方法
using AutoMapper;using DepositService = Client.DepositServiceNoSharp;using Client.WithdrawalServiceNoSharp;namespace Client public static class Extensions static Extensions() Mapper.CreateMap(); Mapper.CreateMap(); Mapper.CreateMap(); Mapper.CreateMap(); public static Money ToWithdrawal(this DepositService.Money money) return Mapper.Map(money); public static DepositService.Money ToDeposit(this Money money) return Mapper.Map(money);}然后就可以轻松转换
var money = new Money Amount = 1, Currency = Currency.Usd, User = new UserInfo Email = "zhww@outlook.com", FirstName = "zhang", Id = "123", LastName = "weiwen", Phone = "110"var depositMoney = money.ToDeposit();方法三、使用SvcMap实现类型共享
其实这个才是文章的重点,前面可以忽略。
生成第一个WCF服务后,点击”显示所有文件“去编辑SvcMap文件:
找到MetadataSources节点,原来只有一个,现在把其他要引用的服务添加到这里,例如:
再右击服务,”更新服务引用“,所有服务都会生成到同一个命名空间里,实现类型共享。
WCF,服务,编程,中,使用,SvcMap,实现,类型,共享C#开发之Socket网络编程TCP/IP层次模型、端口及报文等探讨
http协议在应用层运行。
02,传输层(Tanspot):传输层包括UDP和TCP,UDP几乎不对报文进行检查,而TCP提供传输保证。
03,网络层(Netwok):网络层协议由一系列协议组成,包括ICMP、IGMP、RIP、OSPF、IP(v4,v6)等。
04,链路层(Link):又称为物理数据网络接口层,负责报文传输。
然后我们来看下tcp层次模型图 从上图中可以看出,应用程序在应用层运行,在传输层,在数据前加上了TCP头,在网络层加上的IP头,在数据链路层加上了帧。
2、端口端口号范围:0-65535,总共能表示65536个数。
按端口号可分为3大类 (1)公认端口(WellKnownPorts):从0到1023,它们紧密绑定(binding)于一些服务。
通常这些端口的通讯明确表明了某种服务的协议。
例如:80端口实际上总是HTTP通讯。
(2)注册端口(RegisteredPorts):从1024到49151。
它们松散地绑定于一些服务。
也就是说有许多服务绑定于这些端口,这些端口同样用于许多其它目的。
例如:许多系统处理动态端口从1024左右开始。
(3)动态和/或私有端口(Dynamicand/orPrivatePorts):从49152到65535。
理论上,不应为服务分配这些端口。
实际上,机器通常从1024起分配动态端口。
3.TCP和UDP报文 下面一起来看下TCP和UDP的报文图 从图中我们可以看出TCP和UDP中都有校验和,但是在UDP报文中,一般不使用校验和,这样可以加快数据传输的速度,但是数据的准确性可能会受到影响。
换句话说,Tcp协议都有校验和,为了保证传输数据的准确性。
3.Socket Socket包括Ip地址和端口号两部分,程序通过Socket来通信,Socket相当于操作系统的一个组件。
Socket作为进程之间通信机制,通常也称作”套接字”,用于描述IP地址和端口号,是一个通信链的句柄。
说白了,就是两个程序通信用的。
生活案例对比: Socket之间的通信可以类比生活中打电话的案例。
任何用户在通话之前,首先要占有一部电话机,相当于申请一个Socket,同时要知道对方的号码,相当于对方有一个固定的Socket,然后向对方拨号呼叫,相当于发出连接请求。
假如对方在场并空闲,拿起 电话话筒,双方就可以进行通话了。
双方的通话过程,是一方向电话机发出信号和对方从电话机接收信号的过程,相当于向socket发送数据和从socket接收数据。
通话结束后,一方挂起电话机,相当于关闭socket,撤销连接。
注意:Socket不仅可以在两台电脑之间通信,还可以在同一台电脑上的两个程序间通信。
4,端口进阶(深入) 通过IP地址确定了网络中的一台电脑后,该电脑上可能提供很多提供服务的应用,每一个应用都对应一个端口。
在Internet上有很多这样的主机,这些主机一般运行了多个服务软件 ,同时提供几种服务,每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务(应用程序) 例如:http 使用80端口, ftp使用21端口 smtp使用25端口5.Socket分类 Socket主要有两种类型:流式Socket 是一种面向连接的Socket,针对于面向连接的TCP服务应用,安全,但是效率低 2,数据报式Socket 是一种无连接的Socket,对应于无连接的UDP服务应用,不安全,但效率高6. Socket一般应用模式(服务器端和客户端) 服务器端的Socket(至少需要两个) 01.一个负责接收客户端连接请求(但不负责与客户端通信) 02.每成功接收到客户端的连接便在服务器端产生一个对应的复杂通信的Socket 021.在接收到客户端连接时创建 022. 为每个连接成功的客户端请求在服务器端都创建一个对应的Socket(负责和客户端通信) 客户端的Socket必须指定要连接的服务器地址和端口通过创建一个Socket对象来初始化一个到服务器端的TCP连接 通过上图,我们可以看出,首先服务器会创建一个负责监听的socket,然后客户端通过socket连接到服务器指定端口,最后服务器端负责监听的socket,监听到客户端有连接过来了,就创建一个负责和客户端通信的socket。
下面我们来看下Socket更具体的通信过程:Socket的通讯过程 服务器端: 01,申请一个socket 02,绑定到一个IP地址和一个端口上 03,开启侦听,等待接收连接 客户端: 01,申请一个socket 02,连接服务器(指明IP地址和端口号) 服务器端接收到连接请求后,产生一个新的socket(端口大于1024)与客户端建立连接并进行通信,原监听socket继续监听。
注意:负责通信的Socket不能无限创建,创建的数量和操作系统有关。
7.Socket的构造函数 Public Socket(AddressFamily addressFamily,SocketType socketType,ProtocolType protocolTYpe) AddressFamily:指定Socket用来解析地址的寻址方案。
例如:InterNetWork指示当Socket使用一个IP版本4地址连接 SocketType:定义要打开的Socket的类型 Socket类使用ProtocolType枚举向Windows Sockets API通知所请求的协议注意: 1,端口号必须在 1 和 65535之间,最好在1024以后。
2,要连接的远程主机必须正在监听指定端口,也就是说你无法随意连接远程主机。
如:IPAddress addr = IPAddress.Parse("127.0.0.1");IPEndPoint endp = new IPEndPoint(addr,,9000); 服务端先绑定:serverWelcomeSocket.Bind(endp) 客户端再连接:clientSocket.Connect(endp) 3,一个Socket一次只能连接一台主机 4,Socket关闭后无法再次使用 5,每个Socket对象只能与一台远程主机连接。
如果你想连接到多台远程主机,你必须创建多个Socket对象。
8.Socket常用类和方法 相关类: IPAddress:包含了一个IP地址 IPEndPoint:包含了一对IP地址和端口号方法: Socket():创建一个Socket Bind():绑定一个本地的IP和端口号(IPEndPoint) Listen():让Socket侦听传入的连接吃那个病,并指定侦听队列容量 Connect():初始化与另一个Socket的连接 Accept():接收连接并返回一个新的Socket Send():输出数据到Socket Receive():从Socket中读取数据 Close():关闭Socket,销毁连接 接下来,我们同一个简单的服务器和客户端通信的案例,来看下Sokcet的具体用法,效果图如下:关键代码:服务器端代码: 1 private void Form1_Load(object sender, EventArgs e) 2 5 Control.CheckForIllegalCrossThreadCalls = false; 11 private void btnListen_Click(object sender, EventArgs e) 13 { 15 //ip地址 17 IPAddress ip = IPAddress.Parse(txtIP.Text); 19 // IPAddress ip = IPAddress.Any; 21 //端口号 23 IPEndPoint point=new IPEndPoint(ip,int.Parse(txtPort.Text)); 25 //创建监听用的Socket 27 /* 29 * AddressFamily.InterNetWork:使用 IP4地址。
31 SocketType.Stream:支持可靠、双向、基于连接的字节流,而不重复数据。
此类型的 Socket 与单个对方主机进行通信,并且在通信开始之前需要远程主机连接。
Stream 使用传输控制协议 (Tcp) ProtocolType 和 InterNetworkAddressFamily。
33 ProtocolType.Tcp:使用传输控制协议。
35 */ 37 //使用IPv4地址,流式socket方式,tcp协议传递数据 39 Socket socket=new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp); 41 //创建好socket后,必须告诉socket绑定的IP地址和端口号。
43 //让socket监听point 45 try 47 { 49 //socket监听哪个端口 51 socket.Bind(point); 53 //同一个时间点过来10个客户端,排队 55 socket.Listen(10); 57 ShowMsg("服务器开始监听"); 59 Thread thread = new Thread(AcceptInfo); 61 thread.IsBackground = true; 63 thread.Start(socket); 65 } 67 catch (Exception ex) 69 { 73 ShowMsg(ex.Message); 75 } 77 } 79 //记录通信用的Socket 81 Dictionary dic=new Dictionary(); 83 // private Socket client; 85 void AcceptInfo(object o) 87 { 89 Socket socket = o as Socket; 91 while (true) 93 { 95 //通信用socket 97 try 99 {101 //创建通信用的Socket103 Socket tSocket = socket.Accept();105 string point = tSocket.RemoteEndPoint.ToString();107 //IPEndPoint endPoint = (IPEndPoint)client.RemoteEndPoint;109 //string me = Dns.GetHostName();//得到本机名称111 //MessageBox.Show(me);113 ShowMsg(point + "连接成功!");115 cboIpPort.Items.Add(point);117 dic.Add(point, tSocket);119 //接收消息121 Thread th = new Thread(ReceiveMsg);123 th.IsBackground = true;125 th.Start(tSocket);127 }129 catch (Exception ex)131 {133 ShowMsg(ex.Message);135 break;137 }139 }141 }143 //接收消息145 void ReceiveMsg(object o)147 {149 Socket client = o as Socket;151 while (true)153 {155 //接收客户端发送过来的数据157 try159 {161 //定义byte数组存放从客户端接收过来的数据163 byte[] buffer = new byte[1024 * 1024];165 //将接收过来的数据放到buffer中,并返回实际接受数据的长度167 int n = client.Receive(buffer);169 //将字节转换成字符串171 string words = Encoding.UTF8.GetString(buffer, 0, n);175 ShowMsg(client.RemoteEndPoint.ToString() + ":" + words);177 }179 catch (Exception ex)181 {183 ShowMsg(ex.Message);185 break;187 }189 }191 }195 void ShowMsg(string msg)197 {199 txtLog.AppendText(msg+"\r\n");201 }205 private void Form1_FormClosing(object sender, FormClosingEventArgs e)207 {209 //主窗体关闭时关闭子线程213 }215 //给客户端发送消息217 private void btnSend_Click(object sender, EventArgs e)219 {221 try223 {225 ShowMsg(txtMsg.Text);227 string ip = cboIpPort.Text;229 byte[] buffer = Encoding.UTF8.GetBytes(txtMsg.Text);231 dic[ip].Send(buffer);233 // client.Send(buffer);235 }237 catch (Exception ex)239 {241 ShowMsg(ex.Message);243 }247 }客户端代码: 1 Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 2 3 private void btnConnection_Click(object sender, EventArgs e) 7 //连接到的目标IP 9 IPAddress ip = IPAddress.Parse(txtIP.Text); 11 //IPAddress ip = IPAddress.Any; 13 //连接到目标IP的哪个应用(端口号!) 15 IPEndPoint point=new IPEndPoint(ip,int.Parse(txtPort.Text)); 17 try 19 { 21 //连接到服务器 23 client.Connect(point); 25 ShowMsg("连接成功"); 27 ShowMsg("服务器" + client.RemoteEndPoint.ToString()); 29 ShowMsg("客户端:" + client.LocalEndPoint.ToString()); 31 //连接成功后,就可以接收服务器发送的信息了 33 Thread th=new Thread(ReceiveMsg); 35 th.IsBackground = true; 37 th.Start(); 39 } 41 catch (Exception ex) 43 { 45 ShowMsg(ex.Message); 47 } 49 } 51 //接收服务器的消息 53 void ReceiveMsg() 55 { 57 while (true) 59 { 61 try 63 { 65 byte[] buffer = new byte[1024 * 1024]; 67 int n = client.Receive(buffer); 69 string s = Encoding.UTF8.GetString(buffer, 0, n); 71 ShowMsg(client.RemoteEndPoint.ToString() + ":" + s); 73 } 75 catch (Exception ex) 77 { 79 ShowMsg(ex.Message); 81 break; 83 } 85 } 89 } 93 void ShowMsg(string msg) 95 { 97 txtInfo.AppendText(msg+"\r\n"); 99 }103 private void btnSend_Click(object sender, EventArgs e)105 {107 //客户端给服务器发消息109 if (client!=null)111 {113 try115 {117 ShowMsg(txtMsg.Text);119 byte[] buffer = Encoding.UTF8.GetBytes(txtMsg.Text);121 client.Send(buffer);123 }125 catch (Exception ex)127 {129 ShowMsg(ex.Message);131 }133 }137 }141 private void ClientForm_Load(object sender, EventArgs e)143 {145 Control.CheckForIllegalCrossThreadCalls = false;147 } 好了,到这里我们对Socket的讨论就告一个段落了。
开,发之,Socket,网络编程,TCP,层次,模型,、,端
WindowsPhone8蓝牙编程开发
开发者可以利用蓝牙的相关API来创建应用程序,在应用程序里面使用手机的蓝牙技术来进行近距离的文件传输和发送接收消息,创造出更加有趣和方便的应用软件。
在Windows Phone 8里面可以在应用程序里面利用蓝牙进行通信,使用蓝牙相关的API,可以让应用程序连接到另外的一个应用程序,也可以让应用程序连接到一个设备上。
Windows Phone 8的蓝牙技术支持两个蓝牙方案:一个是应用程序到应用程序的通信,另外一个是应用程序到设备的通信。
1.应用程序到应用程序的通信应用程序到应用程序的通信的过程是,应用程序使用蓝牙去查找正在广播蓝牙服务的对等的应用程序,如果在应用程序提供服务的范围内发现一个应用程序,那么该应用程序可以发起连接请求。
当这两个应用程序接受连接,它们之间就可以进行通信了,通信的过程是使用socket的消息发送接收机制。
在Windows Phone 8中使用到应用程序到应用程序的蓝牙通讯技术,需要在项目的WMAppManifest.xml文件中添加ID_CAP_PROXIMITY的功能选项,表示支持临近的设备通信能力,否则程序会出现异常。
2.应用程序到设备的通信在应用程序到设备的通信过程时,应用程序使用蓝牙去查找提供服务的设备,如果提供的服务范围之内发现一个可以连接的蓝牙设备,那么该应用程序可以发起连接请求。
当应用程序和设备同时接受该连接,它们之间就可以进行通信了,通信的过程也是使用socket的消息发送接收机制,类似于应用程序到应用程序的通信。
在Windows Phone 8中使用到应用程序到设备的蓝牙通讯技术,需要在项目的WMAppManifest.xml文件中添加ID_CAP_PROXIMITY和ID_CAP_NETWORKING的功能选项,表示支持临近的设备通信能力和网络通信能力,否则程序会出现异常。
蓝牙编程类在Windows Phone 8里面使用到蓝牙编程主要会用到PeerFinder类,PeerInformation类,StreamSocket类和ConnectionRequestedEventArgs类,这些类的说明如表19.1所示。
因为蓝牙也是基于TCP协议进行消息传递了,所以需要用到Socket的相关的编程知识,以及StreamSocket类。
PeerFinder类是蓝牙查找类,它的主要成员如表19.2所示。
表19.1 蓝牙编程类的说明类名说明PeerFinder用于去查找附近的设备是否有运行和当前应用程序相同的应用程序,并且可以在两个应用程序之间建立起socket连接,从而可以进行通信。
对等应用程序是在其他设备上运行的应用程序的另一个实例。
PeerInformation包含对等应用程序或设备的识别信息。
StreamSocket支持使用一个TCP的Socket流的网络通信。
ConnectionRequestedEventArgs表示传递到一个应用程序的ConnectionRequested事件的属性表 19.2 PeerFinder类的成员成员说明bool AllowBluetooth指定 PeerFinder 类的此实例是否可以通过使用 Bluetooth 来连接 ProximityStreamSocket 对象。
如果PeerFinder 的此实例可以通过使用 Bluetooth 来连接 ProximityStreamSocket 对象,则为 true;否则为false。
默认为 true。
bool AllowInfrastructure是否使用TCP/IP协议连接到StreamSocketbool AllowWiFiDirect指定 PeerFinder 类的此实例是否可以通过使用 Wi-Fi Direct 来连接 ProximityStreamSocket 对象。
如果 PeerFinder 的此实例可以通过使用 Wi-Fi Direct 来连接 ProximityStreamSocket 对象,则为 true;否则为false。
默认为 true。
IDictionary AlternateIdentities获取要与其他平台上的对等应用程序匹配的备用 AppId 值列表。
返回要与其他平台的对等类应用程序匹配的备用 AppId 值列表。
string DisplayName获取或设置标识计算机到远程对等类的名称。
PeerDiscoveryTypes SupportedDiscoveryTypes获取一个值,该值指示哪些发现选项可与 PeerFinder 类一同使用event TypedEventHandler ConnectionRequested远程对等类使用 ConnectAsync 方法请求连接时发生。
event TypedEventHandler TriggeredConnectionStateChanged在远程对等类的轻击笔势期间发生。
IAsyncOperation ConnectAsync(PeerInformation peerInformation)连接已发现了对 FindAllPeersAsync 方法的调用的对等类。
peerInformation:表示连接到的对等类的对等类信息对象。
返回通过使用所提供的临近StreamSocket 对象连接远程对等类的异步操作。
IAsyncOperation FindAllPeersAsync()适用于无线范围内运行相同应用程序的对等计算机的异步浏览。
返回通过使用 Wi-Fi直连技术浏览对等类的异步操作。
void Start(string peerMessage)向临近设备上的对等类应用程序传递消息。
void Stop()停止查找对等类应用程序或广播对等类连接的过程查找蓝牙设备和对等项查找在服务范围内的蓝牙设备和对等项是蓝牙编程的第一步,查找蓝牙设备和对等项中会使用到PeerFinder类的FindAllPeersAsync方法去进行查找,然后以异步的方式返回查找到的对等项列表的信息IReadOnlyList,注意要使查找对等的应用程序时,在调用FindAllPeersAsync方法前必须先调用PeerFinder类的Start方法,主要的目的是启动广播服务,让对方的应用程序也能查找到自己。
PeerInformation包含三个属性:一个是DisplayName表示对等项的名字,这个名字一般都是由对方的设备的名称或者查找到的应用程序自身设置的现实名字,一个是HostName表示主机名字或者IP地址,还有一个属性是ServiceName表示服务名称或者TCP协议的端口号。
然后可以利用查找到的PeerInformation信息进行连接和通信。
查找对等的应用程序的代码示例:async void AppToApp() // 启动查找服务 PeerFinder.Start(); //开始查找 ObservableCollection peers = await PeerFinder.FindAllPeersAsync(); if (peers.Count == 0) //未找到任何的对等项 else //处理查找到的对等项,可以使用PeerFinder类的ConnectAsync方法来连接选择的要进行通信的对等项}查找蓝牙设备的代码示例:private async void AppToDevice() // 设置查找所匹配的蓝牙设备 PeerFinder.AlternateIdentities["Bluetooth:Paired"] = ""; // 开始查找 ObservableCollection pairedDevices = await PeerFinder.FindAllPeersAsync(); if (pairedDevices.Count == 0) // 没有找到可用的蓝牙设备 else //处理查找到的蓝牙设备,可以新建一个StreamSocket对象,然后使用StreamSocket类的ConnectAsync方法通过HostName和ServiceName来连接蓝牙设备}123在本页阅读全文 本文导航 第1页: 首页 第2页: 蓝牙发送接收信息 第3页: 实现蓝牙程序对设备的连接 WindowsPhone8,蓝牙,编程,开发,蓝牙,是,手机