c#网编实验五--WCF和TCP消息通信实验

分别编写服务端和客户端程序,利用基于WCF的TCP技术,实现在线聊天功能,完成在线用户列表管理,消息发送、接收的功能。

在同一个解决方案中,分别编写服务端程序和客户端程序,利用TCP实现简单的群聊功能。

 

 

具体要求如下:
(1)服务端程序选择【WCF服务库】模板,客户端程序选择【WPF应用程序】模板。
(2)客户端与服务端连接成功后,通过服务端获取已经在线的用户,并将其显示在客户端的在线用户列表中。
(3)不论哪个用户发送聊天消息,其他所有用户都能看到该消息。
(4)当某个用户退出后,在线用户列表中自动移除该用户。
 

重点:

(1)掌握TCP协议下WCF服务应用程序构建方法。
(2)掌握客户端和服务端通信接口设计方法。
(3)掌握协定的设计及实现方法。
(4)掌握双工通信的设计及实现方法。
(5)熟悉WCF和TCP的相关绑定设置。
难点:

(1)掌握客户端和服务端通信接口设计方法。
(2)掌握协定的设计及实现方法。
(3)掌握双工通信的设计及实现方法。
(4)熟悉WCF和TCP的相关绑定设置。
 

客户端

MainWindow.xaml

<Window x:Class="ChatClient.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="主界面" Height="350" Width="525" WindowStartupLocation="CenterScreen">
    <StackPanel VerticalAlignment="Center">
        <Button Name="btn1" Margin="10" Width="170" Content="同时启动2个客户端(测试用)" Click="btn1_Click"/>
        <Button Name="btn2" Margin="10" Width="170" Content="只启动1个客户端(实际情况)" Click="btn2_Click"/>
    </StackPanel>
</Window>

 

 

MainWindow.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace ChatClient
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void btn1_Click(object sender, RoutedEventArgs e)
        {
            StartWindow("西西皮", 50, 300);
            StartWindow("瓜瓜糖", 600, 300);
        }

        private void StartWindow(string userName, int left, int top)
        {
            ClientWindow w = new ClientWindow();
            w.Left = left;
            w.Top = top;
            w.UserName = userName;
            w.Owner = this;
            w.Closed += (sender, e) => this.Activate();
            w.Show();
        }

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

ClientWindow.xaml

<Window x:Class="ChatClient.ClientWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="群聊客户端" Height="300" Width="500">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <DockPanel Grid.Row="0" Background="Cornsilk" LastChildFill="False">
            <TextBlock Text="用户名:" DockPanel.Dock="Left" VerticalAlignment="Center"/>
            <TextBox Name="textBoxUserName" Text="西西" Width="100" VerticalAlignment="Center"/>
            <Button Name="btnLogin" Content="登录" Click="btnLogin_Click" DockPanel.Dock="Left" Padding="10 0 10 0" Margin="5"/>
        </DockPanel>
        <DockPanel Name="dockPanel1" Grid.Row="1">
            <Grid Width="60">
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="*"/>
                </Grid.RowDefinitions>
                <TextBlock Text="在线用户" Grid.Row="0" Background="Blue" Foreground="White" TextAlignment="Center"/>
                <Grid Grid.Row="1">
                    <ListBox Name="listBoxOnLine" Background="AntiqueWhite"/>
                </Grid>
            </Grid>
            <Grid Grid.Row="1">
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="*"/>
                    <RowDefinition Height="Auto"/>
                </Grid.RowDefinitions>
                <TextBlock Name="textBlockMessage" Grid.Row="0"
                 Text="对话信息" Background="Beige" TextAlignment="Center"/>
                <ListBox Name="listBoxMessage" Grid.Row="1" BorderThickness="1" Padding="0 5 0 0"/>
                <DockPanel Grid.Row="2">
                    <TextBlock Text="发言:" Margin="10 0 0 0" DockPanel.Dock="Left" VerticalAlignment="Center"/>
                    <Button Name="btnSend" Click="btnSend_Click" Content="发送" Width="50" DockPanel.Dock="Right" Margin="10 0 10 0"/>
                    <TextBox Name="textBoxTalk" KeyDown="textBoxTalk_KeyDown"/>
                </DockPanel>
            </Grid>
        </DockPanel>
    </Grid>
</Window>

 

 

ClientWindow.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using ChatClient.ChatServiceReference;

namespace ChatClient
{
    /// <summary>
    /// ClientWindow.xaml 的交互逻辑
    /// </summary>
    public partial class ClientWindow : Window, IChatServiceCallback
    {
        public ClientWindow()
        {
            InitializeComponent();
            this.Closing += ClientWindow_Closing;
            dockPanel1.Visibility = System.Windows.Visibility.Hidden;
        }

        public string UserName
        {
            get { return textBoxUserName.Text; }
            set { textBoxUserName.Text = value; }
        }
        private ChatServiceClient client;

        void ClientWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            if (client != null)
            {
                client.Logout(UserName); //退出
                client.Close();
            }
        }

        private void AddMessage(string str)
        {
            TextBlock t = new TextBlock();
            t.Text = str;
            t.Foreground = Brushes.Blue;
            listBoxMessage.Items.Add(t);
        }

        //单击登录按钮引发的事件
        private void btnLogin_Click(object sender, RoutedEventArgs e)
        {
            this.Cursor = Cursors.Wait;
            UserName = textBoxUserName.Text;
            InstanceContext context = new InstanceContext(this);
            client = new ChatServiceReference.ChatServiceClient(context);
            try
            {
                client.Login(textBoxUserName.Text);
                btnLogin.IsEnabled = false;
            }
            catch (Exception ex)
            {
                MessageBox.Show("与服务端连接失败:" + ex.Message);
                return;
            }
            this.Cursor = Cursors.Arrow;
        }

        //单击发送按钮引发的事件
        private void btnSend_Click(object sender, RoutedEventArgs e)
        {
            client.Talk(UserName, textBoxTalk.Text);
            textBoxTalk.Text = "";
        }

        //在文本框中输入对话后按回车键时引发的事件
        private void textBoxTalk_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Enter)
            {
                client.Talk(UserName, textBoxTalk.Text);
                textBoxTalk.Text = "";
            }
        }

        #region 实现IRndGameServiceCallback接口
        /// <summary>有用户登录</summary>
        public void ShowLogin(string loginUserName)
        {
            if (loginUserName == UserName)
            {
                dockPanel1.Visibility = System.Windows.Visibility.Visible;
            }
            listBoxOnLine.Items.Add(loginUserName);
        }

        /// <summary>其他用户退出</summary>
        public void ShowLogout(string userName)
        {
            listBoxOnLine.Items.Remove(userName);
        }

        public void ShowTalk(string userName, string message)
        {
            AddMessage(string.Format("[{0}]说:{1}", userName, message));
        }

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

服务端

IChatService.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;

namespace ChatServer
{
    //需要服务端实现的协定
    [ServiceContract(Namespace = "WcfChatExample", CallbackContract = typeof(IChatServiceCallback))]
    public interface IChatService
    {
        [OperationContract(IsOneWay = true)]
        void Login(string userName);

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

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

    //需要客户端实现的接口
    public interface IChatServiceCallback
    {
        [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);
    }
}

 

 

 ChatService.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;

namespace ChatServer
{
    public class ChatService : IChatService
    {
        /// <summary>
        /// 用户退出。参数:登录者用户名
        /// </summary>
        public void Login(string userName)
        {
            OperationContext context = OperationContext.Current;
            IChatServiceCallback callback = context.GetCallbackChannel<IChatServiceCallback>();
            User newUser = new User(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);
            }
        }

        /// <summary>
        /// 用户退出。参数:退出者用户名
        /// </summary>
        public void Logout(string userName)
        {
            User logoutUser = CC.GetUser(userName);
            CC.Users.Remove(logoutUser);
            logoutUser = null; //将其设置为null后,WCF会自动关闭该客户端
            foreach (var user in CC.Users)
            {
                user.callback.ShowLogout(userName);
            }
        }

        /// <summary>
        /// 客户端发来的对话信息。参数:发送者用户名,对话内容
        /// </summary>
        public void Talk(string userName, string message)
        {
            User user = CC.GetUser(userName);
            foreach (var v in CC.Users)
            {
                v.callback.ShowTalk(userName, message);
            }
        }
    }
}

User.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ChatServer
{
    public class User
    {
        public string UserName { get; set; }
        public readonly IChatServiceCallback callback;

        public User(string userName, IChatServiceCallback callback)
        {
            this.UserName = userName;
            this.callback = callback;
        }
    }
}

 

CC.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ChatServer
{
    public class CC
    {
        //连接的用户,每个用户都对应一个ChatService线程
        public static List<User> Users { get; set; }

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/31871.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

校园wifi网页认证登录入口

很多校园wifi网页认证登录入口是1.1.1.1 连上校园网在浏览器写上http://1.1.1.1就进入了校园网 使 用 说 明 一、帐户余额 < 0.00元时&#xff0c;帐号被禁用&#xff0c;需追加网费。 二、在计算中心机房上机的用户&#xff0c;登录时请选择新建帐号时给您指定的NT域&…

TOGAF10®标准中文版--(阶段C —数据架构阶段B )方法

6.5 方法 6.5.1 数据结构 数据架构应该能够处理&#xff1a; 静态数据——存储中的数据动态数据——事务或服务/API 中的数据使用中的数据——应用边界的数据&#xff08;例如&#xff0c;GUI&#xff09;开放数据——组织提供给公众使用并且自愿或合法要求提供的数据 将添…

Linux主分区,扩展分区,逻辑分区的联系和区别

基本概念 硬盘分区有三种&#xff0c; 主磁盘分区、扩展 磁盘分区、 逻辑分区。 一个 硬盘 主分区至少有1个&#xff0c;最多4个&#xff0c;扩展分区可以没有&#xff0c;最多1个。且 主分区扩展分区总共不能超过4个。 逻辑分区可以有若干个。 在windows下激活的 主分区是 …

MySQL 被 PG 干翻了。。

出品 | OSC开源社区&#xff08;ID&#xff1a;oschina2013) Stack Overflow 发布了 2023 年开发者调查报告&#xff0c;据称共计超过 9 万名开发者参与了此次调查。 完整报告包含了受访开发者画像&#xff0c;以及关于开发技术、AI、职业、社区等方面的内容。本文主要介绍关于…

stm32读取BH1750光照传感器

stm32读取BH1750光照传感器 一.序言二.BH1750指令三.IIC协议四.代码实例4.1 bh1750.c源文件4.2 bh1750.h头文件 一.序言 BH1750是用IIC协议进行数据传输的。有SCL,SDA&#xff0c;VCC,GND四根线。下图是原理图 二.BH1750指令 我们先看芯片手册的操作指令&#xff08;下图&a…

2023年网络安全竞赛——网页渗透

网页渗透 任务环境说明:  服务器场景:Server2120  服务器场景操作系统:未知(封闭靶机)  用户名:未知 密码:未知 访问服务器的网站主页,猜测后台数据库中本网页中应用的库名称长度,将长度作为flag提交; 通过扫描发现靶机开放80端口,直接访问80 尝试输入一个1,…

【设计模式】工厂模式(简单工厂模式、工厂方法模式、抽象工厂模式)详记

注&#xff1a;本文仅供学习参考&#xff0c;如有错漏还请指正&#xff01; 参考文献/文章地址&#xff1a; https://zh.wikipedia.org/wiki/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%EF%BC%9A%E5%8F%AF%E5%A4%8D%E7%94%A8%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E8%BD%AF%E4%BB%B…

el-table合计行单元格合并、单元格样式修改

1、目标效果 源码放在下面&#xff0c;复制粘贴即可 &#xff08;1&#xff09;合计行放在头部&#xff0c;且字体颜色变粗、合计行背景色变粗 &#xff08;2&#xff09;合计行年龄算平均值且字体颜色为绿色&#xff0c;财产算总数且字体颜色为红色 2、原理 2.1、el-table中s…

WPF 零基础入门笔记(1):WPF静态页面,布局+样式+触发器

文章目录 官方文档往期回顾零基础笔记项目实战&#xff08;已完结&#xff09; WPF项目创建为什么选net core版本 WPF 静态页面WPF 页面布局WPF样式Style样式行内样式行外样式如果是简单样式&#xff0c;可以这么写如果是复杂样式 WPF样式继承WPF触发器单条件触发器多条件触发 …

【性能测试一】性能测试概述

目录 &#x1f31f;一、性能测试的基础概念 &#x1f308;1、生活中软件相关的性能问题&#xff1f; &#x1f308;2、性能测试的概念 &#x1f308;3、性能测试与功能测试的区别&#xff1f; &#x1f308;4、什么样的软件属于性能好&#xff1f;什么样的软件属于性能不好…

网络协议TCP/IP 协议学习笔记一

T C P / I P通常被认 为是一个四层协议系统&#xff0c;每一层负责不同的功能&#xff1a; 1) 链路层&#xff0c;有时也称作数据链路层或网络接口层&#xff0c; 通常包括操作系统中的设备驱动程序和计算机 中对应的网络接口卡。它们一起处理与电缆&#xff08;或其他任何传输…

黑客常用cmd命令(window版)

1、ping命令 ping命令是一个常用的网络工具&#xff0c;用来测试和诊断网络连接状况。通过发送ICMP&#xff08;Internet控制消息协议&#xff09;数据包到目标主机&#xff0c;并接收回复的数据包&#xff0c;可以测量目标主机的可达性、平均响应时间等指标。 在Windows操作…

【C++】哈希的应用

文章目录 一、位图1. 位图的引入2. 位图的实现3. 位图的应用4. 哈希切割 二、布隆过滤器1. 布隆过滤器的引入2. 布隆过滤器的实现3. 布隆过滤器的应用4. 布隆过滤器的总结 一、位图 1. 位图的引入 我们先来看一道面试题&#xff1a; 给40亿个不重复的无符号整数&#xff0c;没…

Spring Boot 如何使用 @RequestParam 进行数据校验

Spring Boot 如何使用 RequestParam 进行数据校验 在 Web 应用程序中&#xff0c;用户提交的数据通常以请求参数的形式传递。在 Spring Boot 中&#xff0c;可以使用 RequestParam 注解来获取请求参数。但是&#xff0c;如何确保这些请求参数的有效性呢&#xff1f;在本文中&a…

APP测试面试题快问快答(五)

21. App自动化你用的什么工具&#xff1f; 框架&#xff1a;Appium 编译环境和工具&#xff1a;python3.7和PyCharm 环境&#xff1a;Android sdk 第三方模拟器&#xff1a;夜神、蓝叠等模拟器 定位工具&#xff1a;uiautomatorviewer 实时日志查看&#xff1a;ddms 22.…

智慧加油站卸油作业行为分析算法 opencv

智慧加油站卸油作业行为分析系统通过opencvpython网络模型技术&#xff0c;智慧加油站卸油作业行为分析算法实现对卸油作业过程的实时监测。当现场出现卸油作业时人员离岗&#xff0c;打电话人员抽烟等违规行为&#xff0c;灭火器未正确摆放&#xff0c;明火和烟雾等异常状态&a…

TypeScript零基础入门之背景介绍和环境安装

一、什么是TypeScript TypeScript是一种由微软开发和维护的开源编程语言。它是JavaScript的超集&#xff0c;意味着任何JavaScript程序都是一种有效的TypeScript程序。TypeScript添加了静态类型、类、接口、枚举和命名空间等概念&#xff0c;同时支持ES6特性。TypeScript被视为…

Kubernetes入门实战课-初始容器

Kubernetes入门实战课-初始容器 文章目录 Kubernetes入门实战课-初始容器课前准备初始容器Docker 的形态Docker 的安装Docker 的架构 容器的本质隔离原因与虚拟机区别隔离是怎么实现的 镜像创建容器镜像&#xff1a;如何编写正确、高效的Dockerfile镜像的内部机制是什么Dockerf…

Spring介绍

⭐作者介绍&#xff1a;大二本科网络工程专业在读&#xff0c;持续学习Java&#xff0c;努力输出优质文章 ⭐作者主页&#xff1a;逐梦苍穹 ⭐所属专栏&#xff1a;JavaEE、Spring 目录 1、Spring简介2、轻量级和非侵入性3、IoC容器4、AOP支持5、声明式事务管理6、数据访问支持…

【基于Django框架的在线教育平台开发-02】用户注册功能开发

用户注册功能开发 文章目录 用户注册功能开发1 模型层开发2 视图层开发3 配置urls.py4 表单验证5 模板层开发6 效果展示 1 模型层开发 用户数据表如下所示&#xff1a; FieldTypeExtraidintPrime Key & Auto Incrementpasswordvarchar(128)last_logindatetime(6)Allow Nu…
最新文章