实现双工通讯主要分三步。
- 通信接口的定义;
- 被调用接口的实现
- 双工通道的建立
请先引用DLL(CSDN的代码编辑器真尼玛蛋疼)
整个解决方案的结构
1、通信接口的定义;
服务端调用客户端接口IServerCallClient
/// <summary>
/// 服务器调用客户端接口
/// </summary>
public interface IServerCallClient
{
/// <summary>
/// 服务器读取客户端的IP
/// </summary>
/// <returns></returns>
[OperationContract]
IPEndPoint ServerRequestCLientIP();
/// <summary>
/// 服务器发送消息给客户端
/// </summary>
/// <param name="text"></param>
[OperationContract]
void ServerSayMSG(string text);
}
客户端调用服务端接口IClientCallServer
<span style="font-weight: normal;"> [ServiceContract(CallbackContract =typeof(IServerCallClient))]//指定回调的接口
public interface IClientCallServer
{
/// <summary>
/// 客户端发送消息给服务器
/// </summary>
/// <param name="text"></param>
[OperationContract]//指定操作约束,不添加该接口方法不可使用
void ClientSayToServer(string text);
/// <summary>
/// 客户端读取服务端时间
/// </summary>
/// <returns></returns>
[OperationContract]
DateTime ClientRequestTime();
}</span>
上面的两个接口是单独的一个项目。
双方谈好了就好互相实现对方的方法。
2、被调用接口的实现
客户端实现服务端接口的类:
class ServerCallClient :IServerCallClient
{
public IPEndPoint ServerRequestCLientIP()
{
var ip =new IPEndPoint(IPAddress.Any,10086);
return ip;
}
public void ServerSayMSG(string text)
{
File.AppendAllText("收到的数据", text);
}
}
服务端实现客户端接口的类
/// <summary>
/// 客户回调服务器类
/// </summary>
[ServiceBehavior(InstanceContextMode =InstanceContextMode.Single)]//通道只建立一个除非断开才新建(SessionID是同一个)
public class ClientCallServer : IClientCallServer, IDisposable
{
public static List<IServerCallClient> ServerCallClientList { get; set; }=new List<IServerCallClient>();
/// <summary>
/// 返回服务器时间
/// </summary>
/// <returns></returns>
public DateTime ClientRequestTime()
{
return DateTime.Now;
}
/// <summary>
/// 客户端列表
/// </summary>
/// <summary>
/// 服务端向客户端发数据
/// </summary>
/// <param name="text"></param>
public void ClientSayToServer(string text)
{
var info = OperationContext.Current;
File.AppendAllText("receive.txt",info.SessionId+text);//收到的数据存在文件
}
public void Dispose()
{
ServerCallClientList.Clear();
}
#endregion
}
3、双工通道的建立
服务端通道的建立 ServiceHost serviceHost = new ServiceHost(typeof(ClientCallServer));//<span style="font-family: Arial, Helvetica, sans-serif;">指定回调的类</span>
serviceHost.AddServiceEndpoint(typeof(IClientCallServer),new WSDualHttpBinding(), "http://localhost:12345");//指定客户端的接口,通讯格式,服务器的地址
serviceHost.BeginOpen(callback=>
{
serviceHost.EndOpen(callback);
textBox1.Invoke(new Action(delegate {//不解释也能看得懂吧
textBox1.AppendText("服务开启" + Environment.NewLine);
}));
},null);
客户端通道的建立
var rand = new Random();
InstanceContext context = new InstanceContext(new ServerCallClient());//指定回调的类
WSDualHttpBinding binding = new WSDualHttpBinding() { ClientBaseAddress = new Uri($"http://localhost:{rand.Next(10000, 20000)}") };//指定客户端地址;
using (DuplexChannelFactory proxy = new DuplexChannelFactory(context, binding))//创建通道管理器
{
IClientCallServer client = proxy.CreateChannel(new EndpointAddress("http://localhost:12345"));// 创建用于将消息发送到特定终结点地址的服务的通道client.ClientRequestTime();
client.ClientSayToServer("尼玛");
}