前言
WCF(Windows Communication Foundation)是一个Microsoft提供的基于.NET Framework的通信框架,用于开发分布式应用程序。它可以帮助开发人员在不同的应用程序之间进行通信和交换数据,并支持多种传输协议和编码方式。
WCF基础使用
服务端
当创建出我们的WCF服务库项目Service1时,在解决方案中会自动为我们生成两个类文件“IService1.cs”和“Service1.cs”。
契约(Contract)
1、服务契约[ServiceContract]
服务契约将多个相关的操作联系在一起,组成单个功能单元。
在IService.cs定义接口类似于c++中的.h文件
1 2 3 4 5 6 7 8
| [ServiceContract] public interface IService1 { [OperationContract] string Weather(string city); [OperationContract] double Add(double d1); }
|
Service.cs中保存着接口的具体实现程序
1 2 3 4 5 6 7 8 9 10
| public string Weather(string city) { curentWeather = "高温 16℃, 小雨, 低温 11℃"; return city + ": " + curentWeather + "\n"; } public double Add1(double d1) { return d1 ; }
|
注意:当在Service1.svc文件下启动时,可以启动WcfTestClient.exe工具,输入终结点的位置如:http://localhost:1743/Service1.svc,就可以测试服务接口
客户端
与web的前后端分离不同的是,wpf服务端更新了之后客户端也需要更新,左键WpfApplication1\Service References\ServiceReference1\更新服务器引用
注意App.config并不用自己填写,为自动生成,添加连接时需要左键引用选择添加引用服务,注意的是这里的地址需要填写的是代码实现地址http://localhost:55234/GobangService.svc
通过对象调用服务端
1 2 3 4 5 6 7
| //创建服务客户端 Service1Client client = new Service1Client(); //调用服务 string s; s = client.Weather("ZhengZhou"); //关闭服务客户端并清理资源 client.Close();
|
配置
App.config
1 2 3 4 5
| <client> <endpoint address="http://localhost:1743/Service1.svc" binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IService1" contract="ServiceReference1.IService1" name="BasicHttpBinding_IService1" /> </client>
|
WCF双工通讯
服务端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| //需要服务端实现的协定 [ServiceContract(Namespace = "WcfGobangGameExample", CallbackContract = typeof(IGobangServiceCallback))] public interface IGobangService { [OperationContract(IsOneWay = true)]//单向模式,不向返回操作结果 void Login(string userName); [OperationContract(IsOneWay = true)] void sendInfo(string info); [OperationContract(IsOneWay = true)] void Logout(string userName); } //需要客户端实现的接口 public interface IGobangServiceCallback { [OperationContract(IsOneWay = true)] void ShowLogin(string userName, int userCount); [OperationContract(IsOneWay = true)] void ShowSendInfo(string info); [OperationContract(IsOneWay = true)] void ShowLogout(string userName, int userCount); }
|
IGobangService.cs文件下同时声明了需要服务端实现的协定和需要客户端实现的接口
Service.cs文件只需要实现服务端接口即可,当在实现中需要回调客户端接口时,可以利用回调对象调用回调方法
1 2 3
| OperationContext context = OperationContext.Current; IGobangServiceCallback callback = context.GetCallbackChannel<IGobangServiceCallback>(); callback.ShowLogin(userName, Users.userList.Count);
|
客户端
客户端调用也略有不同
1 2 3 4
| private GobangServiceClient client; //客户端实例 InstanceContext context = new InstanceContext(this); client = new GobangServiceClient(context); client.Login(textBoxUserName.Text);
|
总结:双工通信模糊了前端和后端的区别(也有可能wcf没有前后之分),当前端调用后端时,后端也能反调前端已有的方法;
同时后端也能主动调用前端的方法,实现例如:广播,实时交互的功能
WCF和TCP
TCP的特点
- 一对一通信
- 安全顺序传输
- 通过字节流收发数据
- 传输的数据无消息边界
当发送方发送多个消息时,这些消息可能被分成多个数据段进行传输,并且由于tcp协议本身没有规定消息的边界。这就使得接收方很难在数据流中识别出每个消息的边界,特别是当消息长度不固定时更为复杂。例如:针对前端服务器的http请求走私
解决TCP消息边界问题的办法:
(1)发送固定长度的消息
(2)将消息长度与消息一起发送
(3)使用特殊标记分隔消息利用WCF实现tcp编程
1
| <endpoint address="net.tcp://localhost:51888/ChatService/" binding="netTcpBinding" bindingConfiguration="NetTcpBinding_IChatService" contract="ChatServiceReference.IChatService" name="NetTcpBinding_IChatService">
|
其他与http编程相同利用TcpClient类和TcpListener类实现TCP编程
服务端
连接1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| using System.Net.Sockets TcpListener myListener = new TcpListener(CC.localIP, CC.port); myListener.Start(); while (true) { try { newClient = myListener.AcceptTcpClient();//AcceptTcpClient()方法会阻塞线程直到有新的连接到达,因此该循环一般需要在独立的线程中执行,以避免阻塞主线程 User user = new User(newClient, false); CC.userList.Add(user); } catch { break; } } myListener.Stop();//停止监听客户端连接请求
|
客户端
连接
1 2 3 4 5
| using System.Net.Sockets TcpClient tcpClient = new TcpClient("www.abcd.com", 51888);//在本地就是主机名,用 Dns.GetHostName();获得
TcpClient client = new TcpClient( ); Client.Connect("www.abcd.com",51888);
|
使用NetWorkStream类和BinaryReader类BinaryWriter类的封装来接受和发送数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| NetworkStream networkStream = client.GetStream(); br = new BinaryReader(networkStream); bw = new BinaryWriter(networkStream); /// <summary>处理接收的服务器端数据</summary>后台线程 private void ReceiveData() { string receiveString = null; while (isExit == false) { try { receiveString = br.ReadString(); }//当接受到数据时,赋值成功,跳出try,触发AddInfo函数 catch { if (isExit == false) AddInfo("与服务器失去联系。"); break; } AddInfo(receiveString); } } /// <summary>向服务器端发送信息</summary> private void SendMessage(string message) { try { bw.Write(message); bw.Flush(); }//传入数据 catch { AddInfo("发送失败!"); } }
|
服务端调用的方法相同,服务端充当中转的作用
WCF和UDP应用编程
UDP的特点
- UDP可以一对多传输
- UDP传输速度比TCP快
- UDP有消息边界
- UDP可靠性不如TCP