前言

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的特点

  1. 一对一通信
  2. 安全顺序传输
  3. 通过字节流收发数据
  4. 传输的数据无消息边界
    当发送方发送多个消息时,这些消息可能被分成多个数据段进行传输,并且由于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的特点

  1. UDP可以一对多传输
  2. UDP传输速度比TCP快
  3. UDP有消息边界
  4. UDP可靠性不如TCP