C# WCF实现聊天室功能

1、WCF是什么

  Windows Communication Foundation(WCF)是由微软开发的一系列支持数据通信的应用程序框架

  看这篇文章之前,可以先看我的另一篇文章,初步了解一下WCF:https://www.cnblogs.com/wskxy/p/9435300.html

2、如何通过WCF做聊天室

  首先,我们画个示意图:

 

  按照图示,我们来建立Service和Client

  客户端选择WinForm,这是不用争议的

  服务端为啥要选用Web呢,这是为了方便IIS配置

3、首先是ServiceWeb

  

   IUserService.cs 定义好接口(IUserService)和关联回调(IUserCallback)

using System.ServiceModel;

namespace ServiceWeb
{
    //信息类,懒得新建,放一起了
    [ServiceContract]
    public class UserInfo
    {
        public string acc;
        public string sayContext;
    }
    [ServiceContract]
    public class UserInfoSubscriber : UserInfo
    {
        public IUserCallback subscriber;
    }

    //回调Client函数接口
    public interface IUserCallback
    {
        [OperationContract(IsOneWay = true)]
        void OnLogin(UserInfo user);
        [OperationContract(IsOneWay = true)]
        void OnQuit(UserInfo user);
        [OperationContract(IsOneWay = true)]
        void OnSay(UserInfo user);
    }
    //Service服务函数接口
    [ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(IUserCallback))]
    public interface IUserService
    {
        [OperationContract(IsOneWay = true)]
        void Login(string acc, string pwd);
        [OperationContract(IsOneWay = true)]
        void Quit(string acc);
        [OperationContract(IsOneWay = true)]
        void Say(UserInfo user);
    }
}

  UserService.svc 实现接口函数

using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;

namespace ServiceWeb
{
    // 注意: 使用“重构”菜单上的“重命名”命令,可以同时更改代码和配置文件中的类名“User”。
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession, ConcurrencyMode = ConcurrencyMode.Multiple)]
    public class UserService : IUserService
    {
        /*存储登录信息*/
        /*必须加static,不然每次都被重新实例化*/
        public static List<UserInfoSubscriber> userList = new List<UserInfoSubscriber>();
        public void Login(string acc, string pwd)
        {
            /*1、检测登录,在数据库判断登录信息,不涉及本章重点,为方便代码省略,默认成功*/

            /*2、成功登录后广播*/
            //为当前用户创建订阅者(回调接口)
            IUserCallback subscriber = OperationContext.Current.GetCallbackChannel<IUserCallback>();

            if (userList.FindIndex(x => x.acc == acc) == -1)//避免重复登录
            {
                var user = new UserInfoSubscriber()
                {
                    acc = acc,
                    subscriber = subscriber
                };
                userList.Add(user);
                //user.subscriber.OnLogin(user);//用子类作为参数无效,所以需要重新new返回信息(可能和WCF协议有关)
                //user.subscriber.OnLogin((UserInfo)user);//这样也不行
                user.subscriber.OnLogin(new UserInfo() { acc = acc });//成功执行回调
                foreach (UserInfoSubscriber item in userList.Where(x => x.acc != acc))  //告知其他客户端当前用户已上线
                {
                    item.subscriber.OnLogin(new UserInfo() { acc = acc });
                }
            }
        }

        public void Quit(string acc)
        {
            var user = userList.Find(x => x.acc == acc);
            if (user != null)
            {
                user.subscriber.OnQuit(new UserInfo() { acc = acc });
                userList.Remove(user);
            }
        }

        public void Say(UserInfo _user)
        {
            var user = userList.Find(x => x.acc == _user.acc);
            foreach (UserInfoSubscriber item in userList)
            {
                item.subscriber.OnSay(_user);
            }
        }

    }
}

  注意 Web.config 的<services>,需要有User服务(有的不会自动建立,然后产生错误,缺了就补上)

      <services>
          <service name="ServiceWeb.UserService">
              <!--问题:协定需要会话,但是绑定“BasicHttpBinding”不支持它或者因配置不正确而无法支持它-->
              <!--解决:将BasicHttpBinding换成wsDualHttpBinding-->
              <endpoint address="" binding="wsDualHttpBinding" contract="ServiceWeb.IUserService">
                  <identity>
                      <dns value="localhost"/>
                  </identity>
              </endpoint>
              <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
              <host>
                  <baseAddresses>
                      <add baseAddress="http://localhost:8733/Design_Time_Addresses/Service/UserService/"/>
                  </baseAddresses>
              </host>
          </service>
      </services>

   这样,Service就算定义好了,可以直接配置到IIS,打开svc可以看到:

  

 

 

 

 4、Client

  

   先把服务引用进来,引用后配置文件有如下信息:

    <system.serviceModel>
        <bindings>
            <wsDualHttpBinding>
                <binding name="WSDualHttpBinding_IUserService" />
            </wsDualHttpBinding>
        </bindings>
        <client>
            <endpoint 
                address="http://10.163.101.252:7001/UserService.svc"
                binding="wsDualHttpBinding" bindingConfiguration="WSDualHttpBinding_IUserService"
                contract="VWCFUserService.IUserService" name="WSDualHttpBinding_IUserService">
                <identity>
                    <dns value="localhost" />
                </identity>
            </endpoint>
        </client>
    </system.serviceModel>

  然后,让我们开心写界面

  

  代码如下:

using Client.VWCFUserService;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Client
{
    public partial class Form1 : Form, IUserServiceCallback
    {
        InstanceContext context;
        UserServiceClient sc;

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            this.tb_Acc.Text = "kxy";
            this.tb_Pwd.Text = "123";
            this.tb_Message_Box.ReadOnly = true ;
            this.btn_Quit.Enabled = false;
            this.btn_Send.Enabled = false;
            //this.tb_Send_Box.Focus();  //这个不知道为啥无效
            this.ActiveControl = this.tb_Send_Box;
        }
        private void Login_Click(object sender, EventArgs e)
        {
            context = new InstanceContext(this);
            sc = new UserServiceClient(context);
            //sc.Login(sc.InnerChannel.SessionId, this.tbAcc.Text);
            sc.Login(this.tb_Acc.Text, this.tb_Pwd.Text);
        }

        private void Quit_click(object sender, EventArgs e)
        {
            if (sc != null)
                sc.Quit(this.tb_Acc.Text);
        }

        private void send_Click(object sender, EventArgs e)
        {
            if (!string.IsNullOrEmpty(tb_Send_Box.Text))
            {
                sc.Say(new UserInfo() { acc = this.tb_Acc.Text, sayContext = this.tb_Send_Box.Text });
            }
        }
        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (sc != null)
                sc.Quit(this.tb_Acc.Text);
        }

        public void OnLogin(VWCFUserService.UserInfo user)
        {
            tb_Message_Box.Text += $"\r\n已登录:{user.acc}";
            this.tb_Acc.ReadOnly = true;
            this.tb_Pwd.ReadOnly = true;
            this.btn_Login.Enabled = false;
            this.btn_Quit.Enabled = true;
            this.btn_Send.Enabled = true;
        }
        public void OnQuit(VWCFUserService.UserInfo user)
        {
            tb_Message_Box.Text += $"\r\n已退出:{user.acc}";
            this.tb_Acc.ReadOnly = false;
            this.tb_Pwd.ReadOnly = false;
            this.btn_Login.Enabled = true;
            this.btn_Quit.Enabled = false;
            this.btn_Send.Enabled = false;
        }
        public void OnSay(VWCFUserService.UserInfo user)
        {
            tb_Message_Box.Text += $"\r\n{user.acc}-{DateTime.Now.ToString("f")}";
            tb_Message_Box.Text += $"\r\n{user.sayContext}";
            this.tb_Send_Box.Text = "";
            this.tb_Send_Box.Focus();

        }
    }
}

  到这里,我们的通讯就可以正常进行了。

  请大家自己尝试,可以打开多个进程,体验多人在线!!!

 

5、Client的发布和版本更新

  WinForm怎么进行发布和检测更新呢?

  也是利用IIS!!!

  具体操作如下:

  首先将WinForm发布打包(一步一步截图太麻烦,直接看最后信息呈现)

  

参数解释:
1、该应用程序将发布到(发布生产程序存放的位置,本地随便都行)
2、用户将从以下位置启动此应用程序(这个地址是后面程序将要在IIS上的host地址)

  生成文件如下:

  

  将文件发布到服务器(服务器文件位置随便),定义站点10.163.101.252:7002

  访问http://10.163.101.252:7002/publish.htm,可以进行Client下载安装

  

  并且每次启动Client的时候,会自动检查是否有版本更新

  (当然需要发布新版本,并将新程序Copy到站点,才会进行更新)

 

 

 

   

   

  

 

posted @ 2022-09-13 13:30  wskxy  阅读(450)  评论(0编辑  收藏  举报