前段时间在做WCF开发的过程中,用户需要在服务端对客户端进行监控,需要知道客户端什么时候上线,什么时候下线,当然服务端也可以给客户端推送信息,就是所谓的双向通信了。
要双向通信用我以前的HTTP协议是行不通了,问了一下别人,别人说了一个“心跳更新”这个概念,想必大家都懂这个吧,大概意思呢就是A向B定时发送一个消息来监测B是否活着,如果活着就返回一个消息,死掉当然就不需要了,其实我觉得心跳更新就是TCP协议,也就是双向通信,互相监测活着或者死掉。
行了,废话不多说了,先让大家看下效果:
客户端:
客户端没有多少内容,就是引用一下服务端服务(WCF服务),并调用服务的方法。
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.ServiceModel; namespace WinTestTcp { public partial class ClientFrm : Form,MessageService.IMessageServiceCallback { private MessageService.MessageServiceClient mService = null; public ClientFrm() { InitializeComponent(); } /// /// 窗体加载事件 /// /// /// private void ClientFrm_Load(object sender, EventArgs e) { InstanceContext context = new InstanceContext(this); mService = new MessageService.MessageServiceClient(context); mService.Register(); } /// /// 收到服务器消息 /// /// public void SendMessage(string message) { SetDisplayMessage("服务器:"+message); } /// /// 设置显示消息 /// private void SetDisplayMessage(string message) { if (this.rtxtResult.InvokeRequired) { this.rtxtResult.BeginInvoke(new MethodInvoker(delegate { SetDisplayMessage(message); })); } else{ this.rtxtResult.Text += string.Format("{0}\r\n{1}\r\n",message,DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); } } /// /// 发送事件 /// /// /// private void btnSend_Click(object sender, EventArgs e) { mService.ClientSendMessage(this.textBox1.Text); SetDisplayMessage("我说:"+this.textBox1.Text); this.textBox1.Text = ""; } } }
代码注释很明显,我就不作解释了,如果还有看不懂可以留言。。。。
服务端:
WcfServiceLibraryDemo是一个WCF服务,行了创建的时候只需要把名字改一下,里面的东西不用动就OK了。
WinWcf是Winform,先引用上面的WCF服务,个人不喜欢直接在WCF服务里面直接写服务,在这下面就新建WCF服务需要的两个类IMessageService.cs和MessageService.cs。里面有两个窗体,TcpFrm是带服务端广播的,MainFrm是不带广播的,根据个人需求只需在Program中设置启动就可以了。
具体看下面:
MessageService代码: using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; namespace WinWcf { /// /// 实例使用Single,共享一个 /// 并发使用Mutiple, 支持多线程访问(一定要加锁) /// [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]//必须要加这句话 public class MessageService:IMessageService { /// /// 客户端注册 /// public void Register() { ICallBackServices client = OperationContext.Current.GetCallbackChannel(); string sessionid = OperationContext.Current.SessionId;//获取当前机器Sessionid--------------------------如果多个客户端在同一台机器,就使用此信息。 string ClientHostName = OperationContext.Current.Channel.RemoteAddress.Uri.Host;//获取当前机器名称-----多个客户端不在同一台机器上,就使用此信息。 TcpFrm.GetInstance().SetDisplayMessage(string.Format("客户端上线:\r\n{0}\r\n{1}", sessionid, ClientHostName)); TcpFrm.GetInstance().ListClient.Add(client); OperationContext.Current.Channel.Closed+=new EventHandler(Channel_Closed); TcpFrm.GetInstance().SetClientNum(); } /// /// 客户端发送消息 /// /// public void ClientSendMessage(string message) { ICallBackServices client = OperationContext.Current.GetCallbackChannel(); string sessionid = OperationContext.Current.SessionId;//获取当前机器Sessionid--------------------------如果多个客户端在同一台机器,就使用此信息。 string ClientHostName = OperationContext.Current.Channel.RemoteAddress.Uri.Host;//获取当前机器名称-----多个客户端不在同一台机器上,就使用此信息。 TcpFrm.GetInstance().SetDisplayMessage(string.Format("客户端:{0}\r\n{1}\r\n{2}",message,sessionid,ClientHostName)); client.SendMessage("你好,我是服务器"); } /// /// 客户端关闭事件 /// /// /// private void Channel_Closed(object sender, EventArgs e) { ICallBackServices client = sender as ICallBackServices; TcpFrm.GetInstance().ListClient.Remove(client); TcpFrm.GetInstance().SetClientNum(); } } }
IMessageService代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
namespace WinWcf
{
[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(ICallBackServices))]
public interface IMessageService
{
///
以上两个就是WCF服务了。
TcpFrm代码:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.ServiceModel; using System.ServiceModel.Description; using System.Net; namespace WinWcf { public partial class TcpFrm : Form { private bool isServerRun = true; private int ServerPort = 9900; private static TcpFrm instance = null; public List ListClient = new List(); /// /// 获取单例 /// /// public static TcpFrm GetInstance() { if (instance == null) { instance = new TcpFrm(); } return instance; } private TcpFrm() { InitializeComponent(); } /// /// wcf 宿主服务线程 /// /// /// 启动wcf服务 /// private void StartServer() { System.Threading.ThreadPool.QueueUserWorkItem(delegate { try { isServerRun = true; //获取本机IP string ip = GetIP(); string tcpaddress = string.Format("net.tcp://{0}:{1}/", ip, ServerPort); //定义服务主机 ServiceHost host = new ServiceHost(typeof(MessageService), new Uri(tcpaddress)); //设置netTCP协议 NetTcpBinding tcpBinding = new NetTcpBinding(); tcpBinding.MaxBufferPoolSize = 2147483647; tcpBinding.MaxReceivedMessageSize = 2147483647; tcpBinding.MaxBufferSize = 2147483647; //提供安全传输 tcpBinding.Security.Mode = SecurityMode.None; //需要提供证书 tcpBinding.Security.Transport.ClientCredentialType = TcpClientCredentialType.Certificate; //添加多个服务终结点 //使用指定的协定、绑定和终结点地址将服务终结点添加到承载服务中 //netTcp协议终结点 host.AddServiceEndpoint(typeof(IMessageService), tcpBinding, tcpaddress); #region 添加行为 //元数据发布行为 ServiceMetadataBehavior behavior = new ServiceMetadataBehavior(); //支持get请求 behavior.HttpGetEnabled = false; //设置到Host中 host.Description.Behaviors.Add(behavior); #endregion /////这部分其实就是config配置文件。我不过是把它写在了代码里面,看大家的习惯了,个人感觉这样他有助于理解里面的字段 //设置数据交换类型 host.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexTcpBinding(), "mex"); //启动服务 host.Open(); SetDisplayMessage(string.Format("服务启动成功,正在运行...\r\n{0}", tcpaddress)); } catch (Exception ex) { SetDisplayMessage("服务启动失败"); MessageBox.Show(ex.Message, "服务启动失败,请检测配置中IP地址"); Environment.Exit(0); } }); } /// /// 获取本机IP地址 /// /// public string GetIP() { string name = Dns.GetHostName(); IPHostEntry me = Dns.GetHostEntry(name); IPAddress[] ips = me.AddressList; foreach (IPAddress ip in ips) { if (ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) { return ip.ToString(); } } return (ips != null && ips.Length > 0 ? ips[0] : new IPAddress(0x0)).ToString(); } /// /// 设置运行内容 /// /// public void SetDisplayMessage(string msg) { //在线程里以安全方式调用控件 if (this.rtxtMsg.InvokeRequired) { rtxtMsg.BeginInvoke(new MethodInvoker(delegate { SetDisplayMessage(msg); })); } else { if (rtxtMsg.Lines.Length >= 200) { rtxtMsg.Text = ""; } rtxtMsg.AppendText(string.Format("{0}\r\n{1}\r\n\r\n", msg, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"))); rtxtMsg.SelectionStart = rtxtMsg.Text.Length; rtxtMsg.ScrollToCaret(); } } /// /// 窗体刚加载事件 /// /// /// private void MainFrm_Load(object sender, EventArgs e) { StartServer();//开启WCF服务 SetClientNum(); } /// /// 设置在线客户端数量 /// public void SetClientNum() { if (this.InvokeRequired) { this.BeginInvoke(new MethodInvoker(delegate{ SetClientNum(); })); } else { this.Text=string.Format("服务已启动,当前客户端数量{0}",ListClient.Count); } } /// /// 广播事件 /// /// /// private void btnSend_Click(object sender, EventArgs e) { string message = this.txtMessage.Text; foreach (ICallBackServices icbs in ListClient) { icbs.SendMessage(message); } } } }
算了,大概说一下过程吧,客户端打开后会调用服务端的Register(),服务端监控把客户端上线的信息显示出来,客户端点击发送后会调用服务端的ClientSendMessage(),并向服务端发送消息,服务端收到后显示出来,服务端做到了实时监控,服务端广播给客户端发送信息,并显示在客户端,大概流程就是这样,代码很容易理解。
这样了,我们不管是实用windows还是Android调用WCF服务,都可以做到实时监控,服务端知道客户端什么时候上线或者下线,客户端可以知道服务端此时是不是卡死或者活着,其实我的项目要求有一部分就是需要服务端可客户端的状态实时监控并保存到数据库,并定期给客户端推送消息,至于下线相信大家看了这个demo应该自己会加,我就不多说了。
最后附上源代码,虽然现在缺C币,但我知道人急的时候想下载东西没有C币的 痛苦,所以下载我不需要,大家要是看着觉得可以,给小弟点个赞评论支持下就OK了。如果有问题的可以留言,小弟上线后第一时间会回答。