C# 利用WCF和TCP消息通信实现简单的群聊功能

WCF和TCP消息通信

要求:
在同1个方案中,分别编写服务端程序和客户端程序,利用tcp实现简单的群聊功能。
1.服务端程序选择WCF,客户端程序选择WPF。
2.客户端与服务端连接成功后,通过服务端获取已在线的用户,并将其显示在客户端的在线用户栏。
3.不论哪个用户发送聊天消息,其他所有用户都能看到该消息。
4.当某个用户推出后,在线用户列表中自动移除该用户。
源码:https://github.com/invokeG/WCF-TCP-chat
https://download.csdn.net/download/weixin_44223180/12580184

实现简单的聊天程序

1. 界面一,选择启动两个或一个客户端两个button
在这里插入图片描述
启动并初始化窗口方法,实例化聊天窗口,初始化窗口位置和默认用户名。关闭子窗体时激活父窗体。

private void StartWindow(string userName, int left, int top)
        {
            ChatCline w = new ChatCline();
            w.Left = left;
            w.Top = top;
            w.UserName = userName;
            w.Owner = this;
            w.Closed += (sender, e) => this.Activate();//关闭子窗体时激活父窗体
            w.Show();
        }

        private void bt1_Click(object sender, RoutedEventArgs e)
        {
            StartWindow("用户1", 0, 0);
            StartWindow("用户2", 400, 300);
        }

        private void bt2_Click(object sender, RoutedEventArgs e)
        {
            ChatCline w = new ChatCline();
            w.Owner = this;
            w.Closed += (sendObj, args) => this.Activate();
            w.Show();
        }

2. 聊天框口界面,顶部三个控件用来登录,下面的控件用一个canvas来装他们,隐藏时更方便,直接将canvas隐藏即可。
在这里插入图片描述
3. 服务端
Users类,定义属性和回调接口,拿到回调接口后可实现一对一

class Users
    {
        public string UserName { get; set; }
        public readonly IService1Callback callback;

        public Users(string userName, IService1Callback callback)
        {
            this.UserName = userName;
            this.callback = callback;
        }
    }

CC类,用来装users的数据和对users的查找

class CC
    {
        public static List<Users> Users { get; set; }

        static CC()
        {
            Users = new List<Users>();
        }

        public static Users GetUser(string userName)
        {
            Users user = null;
            foreach (var v in Users)
            {
                if (v.UserName == userName)
                {
                    user = v;
                    break;
                }
            }
            return user;
        }
    }

服务端接口:
定义callback,别名设置IService,也可以有没有

[ServiceContract(Namespace = "IService",
        CallbackContract = typeof(IService1Callback))]

定义三个接口

public interface IService1
    {

        // TODO: 在此添加您的服务操作
        [OperationContract(IsOneWay = true)]
        void Login(string userName);

        [OperationContract(IsOneWay = true)]
        void Logout(string userName);

        [OperationContract(IsOneWay = true)]
        void Talk(string userName, string message);
    }

客户端接口:
定义4个客户端需要实现的接口

public interface IService1Callback
    {
        [OperationContract(IsOneWay = true)]
        void ShowLogin(string loginUserName);

        [OperationContract(IsOneWay = true)]
        void ShowLogout(string userName);

        [OperationContract(IsOneWay = true)]
        void ShowTalk(string userName, string message);

        [OperationContract(IsOneWay = true)]
        void InitUsersInfo(string UsersInfo);
    }

4. 实现服务端接口
登录,获得回调接口,用户名,遍历users,调用客户端ShowLogin方法发给客户端

public void Login(string userName)
        {
            OperationContext context = OperationContext.Current;
            IService1Callback callback = context.GetCallbackChannel<IService1Callback>();
            Users newUser = new Users(userName, callback);
            string str = "";
            for (int i = 0; i < CC.Users.Count; i++)
            {
                str += CC.Users[i].UserName + "、";
            }
            newUser.callback.InitUsersInfo(str.TrimEnd('、'));
            CC.Users.Add(newUser);
            foreach (var user in CC.Users)
            {
                user.callback.ShowLogin(userName);
            }
        }

退出,清除CC中保存的该用户的数据,调用客户端方法发给客户端退出信息

public void Logout(string userName)
        {
            Users logoutUser = CC.GetUser(userName);
            CC.Users.Remove(logoutUser);
            logoutUser = null;
            foreach (var user in CC.Users)
            {
                user.callback.ShowLogout(userName);
            }
        }

发消息,获得用户名和内容后,调用ShowTalk方法发给各个客户端

public void Talk(string userName, string message)
        {
            Users user = CC.GetUser(userName);
            foreach (var v in CC.Users)
            {
                v.callback.ShowTalk(userName, message);
            }
        }

客户端

5. 聊天框口的启动
关闭时调用ChatCline_Closing方法,开启时隐藏canvas内容

public ChatCline()
        {
            InitializeComponent();
            this.Closing += ChatCline_Closing;
            box.Visibility = System.Windows.Visibility.Hidden;
        }

调用Logout方法进行用户退出的清除,并关闭该客户端

private void ChatCline_Closing(object sender, CancelEventArgs e)
        {
            if (client != null)
            {
                client.Logout(UserName); 
                client.Close();
            }
        }

6. 客户端普通方法
字符串的整合,添加到聊天框

private void AddMessage(string str)
        {
            TextBlock t = new TextBlock();
            t.Text = str;
            listmessage.Items.Add(t);
        }

定义UserName属性

public string UserName
        {
            get { return textbox.Text; }
            set { textbox.Text = value; }
        }

登录点击事件,获得用户名,调用服务端方法,连接失败则抛出异常

private void login_Click(object sender, RoutedEventArgs e)
        {
            UserName = textbox.Text;
            InstanceContext context = new InstanceContext(this);
            client = new Service1Client(context);
            try
            {
                client.Login(textbox.Text);
                login.IsEnabled = false;
            }
            catch (Exception ex)
            {
                MessageBox.Show("与服务端连接失败:" + ex.Message);
                return;
            }
        }

发送,点击发送和回车发送,调用同一个服务端方法

private void launch_Click(object sender, RoutedEventArgs e)
        {
            client.Talk(UserName, messagebox.Text);
            messagebox.Text = "";
        }


        private void messagebox_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Enter)
            {
                client.Talk(UserName, messagebox.Text);
                messagebox.Text = "";
            }
        }

7. 客户端实现的方法
初始化用户列表

public void InitUsersInfo(string UsersInfo)
        {
            if (UsersInfo.Length == 0) return;
            string[] users = UsersInfo.Split('、');
            for (int i = 0; i < users.Length; i++)
            {
                listbox.Items.Add(users[i]);
            }
        }

登录后现实聊天框口下部分内容,并添加用户名于列表

public void ShowLogin(string loginUserName)
        {
            if (loginUserName == UserName)
            {
                box.Visibility = System.Windows.Visibility.Visible;
            }
            listbox.Items.Add(loginUserName);
        }

显示退出和现实聊天内容

public void ShowLogout(string userName)
        {
            listbox.Items.Remove(userName);
        }
public void ShowTalk(string userName, string message)
        {
            AddMessage(userName+"说: "+message);
        }

结果:
在这里插入图片描述
问题讨论:
1.在服务端App.config中contract第一个为项目名
在这里插入图片描述
2.由于控件较多,命名省事不规范,造成输出混乱的现象。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值