C#使用TCP消息通信实现简易群聊功能(完整过程)

首先创建一个解决方案,选择WPF应用程序,在WPF这个项目里面点击添加,找到新建项,选择WCF服务,切记不要在解决方案点添加。

服务名可以自己起,但是要记得在配置和其他引用的地方修改。我的服务名是ChatService

所以我在创建服务时有两个文件,IChatService.cs,ChatService.cs。

我们要先在IChatService.cs定义接口。

[ServiceContract (Namespace = "TcpService",
        //SessionMode = SessionMode.Required,
        CallbackContract = typeof(IChatServiceCallback))]
    public interface IChatService
    {
        /// <summary>
        /// 登录
        /// </summary>
        /// <param name="userName">用户名</param>
        [OperationContract (IsOneWay =true)] void Login(string userName);
        /// <summary>
        /// 退出登录
        /// </summary>
        /// <param name="userName"></param>
        [OperationContract(IsOneWay = true)] void Logout(string userName);
        /// <summary>
        /// 发送消息
        /// </summary>
        /// <param name="userName">用户名</param>
        /// <param name="message">消息内容</param>
        [OperationContract(IsOneWay = true)] void Talk(string userName, string message);
    }
    /// <summary>
    /// 客户端要实现的回调接口
    /// </summary>
    public interface IChatServiceCallback
    {
        /// <summary>
        /// 把其他用户的用户名发送客户端
        /// </summary>
        /// <param name="userNames">用户名数组</param>
        [OperationContract(IsOneWay = true)] void ShowUsersInfo(string[] userNames);
        /// <summary>
        /// 显示其他用户退出
        /// </summary>
        /// <param name="userName">用户名</param>
        [OperationContract(IsOneWay = true)] void ShowLogon(string userName);
        /// <summary>
        /// 显示其他人发送的消息
        /// </summary>
        /// <param name="userName"></param>
        /// <param name="message"></param>
        [OperationContract(IsOneWay = true)] void ShowTalk(string userName, string message);
    }

我在这里使用的是单工通信,如果你对TCP的其他通信方式了解的话,也可以使用别的通信方式,但不要忘记修改配置。

然后我们在ChatService.cs实现客户端的接口。

在此之前我们还要先写好几个要使用到的类。

User类,用来储存用户的用户名和实现IChatService.cs接口的用户实例。

class User
    {
        /// <summary>
        /// 用户名
        /// </summary>
        public string name { get; set; }
        /// <summary>
        /// 客户端实例
        /// </summary>
        public IChatServiceCallback callback { get; set; }
        public User() { }
        public User(string name,IChatServiceCallback callback)
        {
            this.name = name;
            this.callback = callback;
        }
    }

Lobby类,用来储存以及登录到大厅中的用户。

class Lobby
    {
        /// <summary>
        /// 大厅中在线的用户
        /// </summary>
        public static List<User> users { get;set; }
        /// <summary>
        /// 通过用户名找到用户实例
        /// </summary>
        /// <param name="name">用户名</param>
        /// <returns></returns>
        public static User getUserByName(string name)
        {
            User user = new User();
            if (users != null)
            {
                foreach(User u in users)
                {
                    if (u.name == name)
                    {
                        user = u;
                        return user;
                    }
                }
            }
            return user;
        }
    }

这两个类写完我们就可以在ChatService.cs实现客户端的接口了。

这些做完之后我们就可以修改配置了,打开App.config,把里面的代码修改成下面的代码

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
    </startup>
    <system.serviceModel>
        <behaviors>
            <serviceBehaviors>
                <behavior name="">
                    <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
                    <serviceDebug includeExceptionDetailInFaults="true" />
                </behavior>
            </serviceBehaviors>
        </behaviors>
        <services>
            <service name="TcpService.ChatService">
                <endpoint address="net.tcp://localhost:51888/TcpService/ChatService" binding="netTcpBinding" contract="TcpService.IChatService">
                    <identity>
                        <dns value="localhost" />
                    </identity>
                </endpoint>
                <host>
                    <baseAddresses>
                        <add baseAddress="http://localhost:8733/Design_Time_Addresses/TcpService/ChatService/" />
                    </baseAddresses>
                </host>
            </service>
        </services>
    </system.serviceModel>
</configuration>

<service name="TcpService.ChatService">这一栏中的TcpService对应的是你的项目名,即命名空间名,ChatService是你的服务名。同理下面一行endpoint标签中的contract也是这样。

<add baseAddress="http://localhost:8733/Design_Time_Addresses/TcpService/ChatService/" />这一行是系统自动配置的,一般情况下不要修改,尤其是Design_Time_Addresses关键字,它的用途是确保你以非管理员身份运行和调试。

为了使这个服务可以被引用,我们必须要打开ServiceHost

首先我们修改一下MainWindow.xaml界面

<Window x:Class="TcpService.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:TcpService"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Button Content="启动服务" HorizontalAlignment="Left" Height="45" Margin="100,35,0,0" VerticalAlignment="Top" Width="176" RenderTransformOrigin="0.452,0.252" FontSize="18" Click="Button_Start"/>
        <Button Content="关闭服务" HorizontalAlignment="Left" Height="45" Margin="480,35,0,0" VerticalAlignment="Top" Width="183" RenderTransformOrigin="-0.148,-0.625" FontSize="18" Click="Button_Stop"/>
        <TextBlock Name="tbk" HorizontalAlignment="Left" Height="256" Margin="25,147,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="744" FontSize="18"/>
        <TextBlock HorizontalAlignment="Left" Height="24" Margin="340,103,0,0" TextWrapping="Wrap" Text="运行信息" VerticalAlignment="Top" Width="126" FontSize="18"/>
    </Grid>
</Window>

记得把类名修改成你自己命名空间下的类。

然后我们再来修改MainWindow.xaml.cs中的代码

public partial class MainWindow : Window
    {
        /// <summary>
        /// 提供服务的主机
        /// </summary>
        private ServiceHost host;
        public MainWindow()
        {
            InitializeComponent();
            //添加关闭窗体的事件
            this.Closing += MainWindow_Closing;
        }
        /// <summary>
        /// 关闭窗体时触发
        /// </summary>
        private void MainWindow_Closing(object sender, CancelEventArgs e)
        {
            if (host != null)
            {
                if(host.State == CommunicationState.Opened)
                {
                    host.Close();
                }
            }
        }
        /// <summary>
        /// 点击启动服务
        /// </summary>
        private void Button_Start(object sender, RoutedEventArgs e)
        {
            //新建ChatService服务的服务主机并打开
            host = new ServiceHost(typeof(ChatService));
            host.Open();
            tbk.Text += "本机服务已启动,监听服务的Uri为:\n";
            foreach(var v in host.Description.Endpoints)
            {
                tbk.Text += v.ListenUri.ToString() + "\n";
            }
        }
        /// <summary>
        /// 点击关闭服务
        /// </summary>
        private void Button_Stop(object sender, RoutedEventArgs e)
        {
            host.Close();
            tbk.Text += "本机服务已关闭!"+"\n";
        }
    }

这样我们服务端的配置就完成了。

接下来我们要在该解决方案下创建客户端,点击该解决方案,仍然选择添加WPF应用程序。

系统加载完后我们就可以添加服务引用了,不要关闭原来的程序,在文件夹中找到你的程序打开刚建好的项目,我新建的项目是TcpClient,我们从这个文件夹下找到TcpClient.csproj打开,就可以再打开一个程序了。

我们回到原程序界面,把原项目设为启动项目(没改过一般不用自己调默认就是)

点击启动进行调试,点击启动服务按钮

出现了运行信息就说明启动成功了,去到新项目的程序界面,右键点击引用,添加服务引用

在地址栏中将配置App.config时,<add baseAddress="http://localhost:8733/Design_Time_Addresses/TcpService/ChatService/" />标签中的地址过去,点击转到,服务栏中出现了服务,说明服务下载成功了,修改最下面的命名空间

也可以使用默认的命名空间,我使用的是ChatServiceReference

保存后我们就可以关闭这个程序界面了,回到原程序界面,重新从磁盘中加载一下。

然后我们右键客户端项目,添加,窗口,我的窗体名是ChatWindow,修改ChatWindow.xaml为

<Window x:Class="TcpClient.ChatWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:TcpClient"
        mc:Ignorable="d"
        Title="群聊客户端" Height="450" Width="800">
    <Grid>
        <TextBlock HorizontalAlignment="Left" Height="23" Margin="10,10,0,0" TextWrapping="Wrap" Text="用户名:" VerticalAlignment="Top" Width="75" RenderTransformOrigin="0.302,0.348" FontSize="18" />
        <TextBox Name="Name" HorizontalAlignment="Left" Height="23" Margin="96,10,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="100" FontSize="18"/>
        <TextBlock HorizontalAlignment="Left" Height="23" Margin="13,49,0,0" TextWrapping="Wrap" Text="在线用户" VerticalAlignment="Top" Width="150" TextAlignment="Center" FontSize="18"/>
        <ListBox Name="userList" HorizontalAlignment="Left" Height="323" Margin="14,88,0,0" VerticalAlignment="Top" Width="150" FontSize="18"/>
        <TextBlock HorizontalAlignment="Left" Height="42" Margin="350,24,0,0" TextWrapping="Wrap" Text="对话信息" VerticalAlignment="Top" Width="220" RenderTransformOrigin="0.288,-0.171" FontSize="30" TextAlignment="Center"/>
        <ListBox Name="mesList" HorizontalAlignment="Left" Height="271" Margin="190,88,0,0" VerticalAlignment="Top" Width="575" FontSize="18"/>
        <TextBlock HorizontalAlignment="Left" Height="35" Margin="190,375,0,0" TextWrapping="Wrap" Text="发言:" VerticalAlignment="Top" Width="73" FontSize="18"/>
        <TextBox Name="Message" HorizontalAlignment="Left" Height="35" Margin="283,375,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="357" FontSize="18" />
        <Button Content="发送" HorizontalAlignment="Left" Height="35" Margin="679,375,0,0" VerticalAlignment="Top" Width="71" RenderTransformOrigin="0.207,0.361" FontSize="18" Click="Button_Send"/>
        <Button Name="btn_Login" Content="登录" HorizontalAlignment="Left" Height="30" Margin="665,10,0,0" VerticalAlignment="Top" Width="100" FontSize="18" Click="Button_Login"/>

    </Grid>
</Window>

同样,类和项目名与我不一样的记得修改。

然后在ChatWindow.xaml.cs实现聊天功能

public partial class ChatWindow : Window,IChatServiceCallback
    {
        /// <summary>
        /// 服务端实例
        /// </summary>
        private ChatServiceClient client;
        /// <summary>
        /// 用户名
        /// </summary>
        private string name;
        /// <summary>
        /// 在线用户信息显示控件的列表
        /// </summary>
        private List<TextBlock> texts = new List<TextBlock>();
        /// <summary>
        /// 通过用户名找到相应控件
        /// </summary>
        /// <param name="text">用户名信息</param>
        /// <returns></returns>
        private TextBlock GetTextBlockByText(string text)
        {
            TextBlock tb = new TextBlock();
            foreach(TextBlock t in texts)
            {
                if (t.Text == text)
                {
                    tb = t;
                    return tb;
                }
            }
            return tb;
        }
        public ChatWindow(string name)
        {
            InitializeComponent();
            //获取服务端实例
            InstanceContext context = new InstanceContext(this);
            client = new ChatServiceReference.ChatServiceClient(context);
            //把传入的用户名显示出来
            Name.Text = name;

            this.name = name;
            this.Closing += MainWindow_Closing;
        }
        #region 点击事件
        /// <summary>
        /// 关闭窗体时执行的语句
        /// </summary>
        private void MainWindow_Closing(object sender, CancelEventArgs e)
        {
            client.Logout(name);
        }
        /// <summary>
        /// 点击登录按钮
        /// </summary>
        private void Button_Login(object sender, RoutedEventArgs e)
        {
            name = Name.Text.Trim();
            client.Login(name);
            //仅可登录一次
            btn_Login.IsEnabled = false;
        }
        /// <summary>
        /// 点击发送按钮
        /// </summary>
        private void Button_Send(object sender, RoutedEventArgs e)
        {
            client.Talk(name, Message.Text.Trim());
            TextBlock tb = new TextBlock()
            {
                Text = name + ":" + Message.Text.Trim(),
                FontSize = 18,
                Height = 23
            };
            mesList.Items.Add(tb);
        }
        #endregion
        #region 实现的接口
        /// <summary>
        /// 在用户信息列表中显示信息
        /// </summary>
        /// <param name="userNames">用户列表数组</param>
        public void ShowUsersInfo(string[] userNames)
        {
            if (userList.Items.Count==0)
            {
                foreach (string name in userNames)
                {
                        TextBlock tb = new TextBlock()
                        {
                            Text = name,
                            FontSize = 18,
                            Height = 23
                        };
                        userList.Items.Add(tb);
                        texts.Add(tb);
                }
            }
            else foreach(string name in userNames)
            {
                if (GetTextBlockByText(name).Text=="")
                { 
                   TextBlock tb = new TextBlock()
                    {
                        Text = name,
                        FontSize = 18,
                        Height = 23
                    };
                    userList.Items.Add(tb);
                    texts.Add(tb);
                }
            }
        }
        /// <summary>
        /// 显示其他用户发送的消息
        /// </summary>
        /// <param name="userName"></param>
        /// <param name="message"></param>
        public void ShowTalk(string userName, string message)
        {
            TextBlock tb = new TextBlock()
            {
                Text = userName+":"+message,
                FontSize = 18,
                Height = 23
            };
            mesList.Items.Add(tb);
        }

        public void ShowLogon(string userName)
        {
            TextBlock tb =  GetTextBlockByText(userName);
            userList.Items.Remove(tb);
        }
        #endregion
    }

不要忘记导入服务引用using TcpClient.ChatServiceReference;

ShowUsersInfo接口中更新信息代码效率比较低,这里只是简易实现,把两种情况分开实现接口可以提高效率。

然后我们在MianWindow中分测试和实际情况打开聊天窗口。修改MainWindow.xaml为

<Window x:Class="TcpClient.ChatWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:TcpClient"
        mc:Ignorable="d"
        Title="群聊客户端" Height="450" Width="800">
    <Grid>
        <TextBlock HorizontalAlignment="Left" Height="23" Margin="10,10,0,0" TextWrapping="Wrap" Text="用户名:" VerticalAlignment="Top" Width="75" RenderTransformOrigin="0.302,0.348" FontSize="18" />
        <TextBox Name="Name" HorizontalAlignment="Left" Height="23" Margin="96,10,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="100" FontSize="18"/>
        <TextBlock HorizontalAlignment="Left" Height="23" Margin="13,49,0,0" TextWrapping="Wrap" Text="在线用户" VerticalAlignment="Top" Width="150" TextAlignment="Center" FontSize="18"/>
        <ListBox Name="userList" HorizontalAlignment="Left" Height="323" Margin="14,88,0,0" VerticalAlignment="Top" Width="150" FontSize="18"/>
        <TextBlock HorizontalAlignment="Left" Height="42" Margin="350,24,0,0" TextWrapping="Wrap" Text="对话信息" VerticalAlignment="Top" Width="220" RenderTransformOrigin="0.288,-0.171" FontSize="30" TextAlignment="Center"/>
        <ListBox Name="mesList" HorizontalAlignment="Left" Height="271" Margin="190,88,0,0" VerticalAlignment="Top" Width="575" FontSize="18"/>
        <TextBlock HorizontalAlignment="Left" Height="35" Margin="190,375,0,0" TextWrapping="Wrap" Text="发言:" VerticalAlignment="Top" Width="73" FontSize="18"/>
        <TextBox Name="Message" HorizontalAlignment="Left" Height="35" Margin="283,375,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="357" FontSize="18" />
        <Button Content="发送" HorizontalAlignment="Left" Height="35" Margin="679,375,0,0" VerticalAlignment="Top" Width="71" RenderTransformOrigin="0.207,0.361" FontSize="18" Click="Button_Send"/>
        <Button Name="btn_Login" Content="登录" HorizontalAlignment="Left" Height="30" Margin="665,10,0,0" VerticalAlignment="Top" Width="100" FontSize="18" Click="Button_Login"/>

    </Grid>
</Window>

修改MainWindow.xaml.cs为

public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
        /// <summary>
        /// 打开两个窗体
        /// </summary>
        private void Button_Click1(object sender, RoutedEventArgs e)
        {
            ChatWindow chat1 = new ChatWindow("西西皮");
            chat1.Show();
            ChatWindow chat2 = new ChatWindow("瓜瓜糖");
            chat2.Show();
        }
        /// <summary>
        /// 打开一个窗体
        /// </summary>
        private void Button_Click2(object sender, RoutedEventArgs e)
        {
            ChatWindow chat1 = new ChatWindow("西西皮");
            chat1.Show();
        }
    }

 

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值