博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
WCF也可以做聊天程序
阅读量:4467 次
发布时间:2019-06-08

本文共 4832 字,大约阅读时间需要 16 分钟。

先看一个截图。

 

上面的图,各位乍一看,可能会觉得是用Socket编写的聊天程序。告诉你吧,这玩意儿不是用Socket实现,呵呵,当然它的底层肯定与Socket有一定关系,我只说我的代码没有用到socket而已。

那么,除了Socket可以用于通信,还有其他技术吗?有啊,首先,如果你足够强大,用HTTP也行,但HTTP初始化的过程貌似比较慢。那么还有吗?当然了,各位还记得.NET以前有一个很X但又很少被关注的技术——Remoting。用过吧?没用过也没关系,因为它已经有替代品了。

这时候大家肯定想到WCF不是一盏“省油”的灯,其实不然,对比于用Socket要编写的代码数量和维护成本,用WCF编写网络通信程序,不仅省油,而且省时省力,最重要的是省心。所以,人的健康,心理健康是占主导的,你看一个心理不健康的人,其身体也不会健康到哪里去,今天这病明天那病。

因而,编程这事啊,越省心越好,有利于我们的健康,赚钱永远不是目的,身心健康才是活在这个世界上的主旋律,至于你信不信,反正我深信不疑。

 

我就这个WCF版的聊天程序的大致思路说一说。

这个程序既可以充当服务器端也同时作为客户端,每个应用实例都扮演着双重角色。这里我不需要引用服务。首先看服务协定和服务类。

using System;using System.ServiceModel;namespace ServiceDef{    [ServiceContract]    public interface IService    {        [OperationContract(IsOneWay = true)]        void SendMessage(string msg);    }    ///     /// 服务    ///     public class MyChatService : IService    {        ///         /// 收到消息后引发的事件        ///         public event EventHandler
MessageGot; public MyChatService() { MessageGot += new EventHandler
(TestApp.Form1.GetMessageCallBack); } public void SendMessage(string msg) { if (MessageGot != null) { MessageGot(this, new MessageReceiveEventArgs(msg)); } } } ///
/// 收到消息后引发事件的参数 /// public class MessageReceiveEventArgs : EventArgs { private string m_Message = string.Empty; public MessageReceiveEventArgs(string message) { this.m_Message = message; } public string MessageText { get { return this.m_Message; } } }}

服务协定没什么好看的了,相信大家都会写,这里的服务类与以往的有些不同,大家看到,里面定义了一个事件。那么,为什么要这样做呢?为什么要在服务方法被调用时引发这个事件呢?

想一想,我们以上代码是与UI分离的,也就是说,与UI分离是一种很好的编程方法,这样在修改维护时不会搞得乱七八糟。但是,我们都知道世间万物皆为阴阳所生,所以才有太极生两仪,两仪生四象,四象成八卦。而阴与阳是统一的,阴中有阳,阳在有阴。

我们的应用程序的UI就是阳,而业务逻辑就是阴,所以编程就是这么简单——阴阳互动。为了完成阴中有阳的功能,我们要想办法让这些代码与窗口上的控件互动,当然方法很多,也相当灵活。使用事件是比较好的。

于是,在服务类中定义一个事件,而事件的处理方法在主窗口类中定义,这样一来,阳与阴之间就有了一个可以相通的渠道。

为了使用访问方便,在窗口类中定义的处理事件的方法使用静态方法,静态方法的好处在于,它不基于对象,而是基于类的,你去到哪里都可以访问,它是全球化的。

这时候有人会问了,静态方法不能访问类对象的成员,那么这个静态方法又如何与窗体上的控件互动呢?技巧都是拿来用的。有了静态方法,难道我不能在窗口类中定义一个保存当前类实例的静态变量吗?

比如,我这个窗口的类名为FormMain,我只要在FormMain里面定义一个static FormMain CurrentForm = null;就完事了,这样不就可以在静态方法中访问了吗?

只要在FormMain的构造函数中赋值就行了,CurrentForm = this;

 

比如本例的代码:

#region 静态成员        static Form1 CurrentInstance = null;        public static void GetMessageCallBack(object sender, ServiceDef.MessageReceiveEventArgs e)        {            if (CurrentInstance != null)            {                CurrentInstance.AddMessageToListBox(e.MessageText);            }        }        #endregion        public Form1()        {            InitializeComponent();            CurrentInstance = this;…………

你看,这就成了。

然后当然是定义服务器了,这里我们只有一个终结点,就是上面的IService,所以不用基址了,因为我们也不需要引用服务,直接利用ChannelFactory就行了。

#region 与服操作有关        ServiceHost host = null;        ///         /// 启动服务        ///         /// 监听端口        void OpenService(int port)        {            host = new ServiceHost(typeof(ServiceDef.MyChatService));            NetTcpBinding binding = new NetTcpBinding();            binding.Security.Mode = SecurityMode.None;            host.AddServiceEndpoint(typeof(ServiceDef.IService), binding, "net.tcp://" + Dns.GetHostName() + ":"+ port.ToString() + "/chatsvc/");            host.Opened += host_Opened;            host.Closed += host_Closed;            try            {                host.Open();            }            catch (Exception ex)            {                ShowMessage(ex.Message);            }        }        void host_Closed(object sender, EventArgs e)        {            ShowMessage("服务已关闭。");        }        void host_Opened(object sender, EventArgs e)        {            ShowMessage("服务已启动。");        }        ///         /// 关闭服务        ///         void CloseService()        {            if (host != null)            {                host.Close();                host.Opened -= host_Opened;                host.Closed -= host_Closed;            }        }        #endregion

 

好了,接下来就是发送消息,其实就是调用服务方法IService.SendMessage,这里我们只用ChannelFactory<TChannel>工厂来生产一个IService通道,而后直接调用就可以了,就不必引用服务,也不用生成什么WSDL文件,也不考虑SOAP版本了。

// 发送消息,即调用服务            NetTcpBinding binding =new NetTcpBinding();            binding.Security.Mode = SecurityMode.None;                        try            {                ServiceDef.IService ep = ChannelFactory
.CreateChannel(binding,new EndpointAddress("net.tcp://" + txtSvrHostName.Text + ":" + rmPort.ToString() + "/chatsvc/")); ep.SendMessage(txtSendMessage.Text); txtSendMessage.Clear(); } catch (Exception ex) { ShowMessage(ex.Message); }

哈哈,是不是很简单,而且,你也可以想到,如果用WCF来做文件传输,比如PC与手机上的文件传送,是不是很方便呢?也不必担心TCP粘包问题。

源代码我随后上传。

 

转载于:https://www.cnblogs.com/jiangu66/archive/2013/04/05/3000628.html

你可能感兴趣的文章
笔记52 Mybatis快速入门(三)
查看>>
Cracking The Coding Interview 1.2
查看>>
PL/SQL报错:无法解析指定的连接标识符
查看>>
LAMP安全加固
查看>>
力扣 5063 最后一块石头的重量 & II
查看>>
导航狗信息导航网站首页源代码(2017年11月03日版)
查看>>
Java中的Class.forName
查看>>
20165223 实验五 网络编程与安全
查看>>
java.math.BigDecimal cannot be cast to [Ljava.lang.Object 报错解决方法
查看>>
20145104张家明 《Java程序设计》第4周学习总结
查看>>
CS 1037 A - Assessment
查看>>
夜神安卓模拟器怎么清除数据
查看>>
解决IE6不支持position:fixed;的问题
查看>>
理解jquery的$.extend()、$.fn和$.fn.extend()的区别及用法
查看>>
NGUI基本事件
查看>>
工具下载地址
查看>>
mysqld诡异crash
查看>>
eclipse启动tomcat出现内存溢出错误 java.lang.OutOfMemoryError: PermGen space
查看>>
POJ 1564 经典dfs
查看>>
PLSQL触发器
查看>>