C#网络编程----使用UdpClient实现网络会议讨论(详解)

实验环境

WindowsServer2012+vs2010

实现效果

单机MainWindow里面的“网络会议室”按钮,然后弹出三个客户端,三个客户端端口和姓名已分配
每个客户端可以点击登陆按钮,进入会议室,只要登入的客户端,每个客户端左边的会议室成员列表都会实时更新
登陆进去的客户端,可以参与讨论,消息共享。
单机退出按钮,关闭客户端,并且其余未退出的客户端会更新在线人员列表。
在这里插入图片描述

实现原理

主要是利用套接字实现发送和接受消息,然后利用多播和单播结合来实现会议讨论功能。
具体功能设计思路解析如图:
一、
在这里插入图片描述
二、
在这里插入图片描述
三、
在这里插入图片描述
四、
在这里插入图片描述
五、
发消息聊天比较简单,直接RoomService发广播就好了。

一些需要注意的点

  • 三个客户端自始至终都只监听8000,只跟RoomService通信
  • 用户登录,先告知他当前在线成员,再将他加入用户列表,注意顺序,否则会出现一个名字加两遍
  • 当收到用户登录信息,RoomService在实例化User时,要注意,User的UserEndPoint是该用户端口信息,后边ShowCurrentUsers要用到

ps:这个程序目前的缺点是,客户端退出之后,窗口就关闭了,没有办法再次加入会议室,我本来想着通过登录标志isLogin和关闭标志isExit,来实现单机退出按钮时,不关闭窗口,只退出多播组。这样虽然成功了,但是再次加入会议室的时候,报错,提示套接字只能使用一次。。。。。。算咯,以后会了再改吧

源码及详细注释

MainWindow后台代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
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;
using System.Net;
using System.Net.Sockets;

namespace ch11
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        private IPAddress ip;

        public MainWindow()
        {
            InitializeComponent();
            //获取本机ip地址
            IPAddress[] ipes = Dns.GetHostAddresses(Dns.GetHostName());
            foreach (IPAddress v in ipes)
            {
                if (v.AddressFamily == AddressFamily.InterNetwork)
                { ip = v; break; }
            }
        }
        //启动会议室程序
        private void button1_Click(object sender, RoutedEventArgs e)
        {
            int servicePort = 8000;//RoomService端口
            RoomService service = new RoomService(ip, servicePort);//先启动RoomService
            
            ShowClient("客户端1", "张三", -360, -50, 8001, servicePort);
            ShowClient("客户端2", "李四", 210, -50, 8002, servicePort);
            ShowClient("客户端3", "王五", -100, 140, 8003, servicePort);           
        }
        private void ShowClient(string title, string userName,double dx, double dy, int port, int servicePort)
        {
            NetMeetingClient w = new NetMeetingClient()
            {
                Title = title,
                Owner = this,
                Left = this.Left + dx,
                Top = this.Top + dy,
                localEndPoint = new IPEndPoint(ip, port),//这里是他们自己的端口
                remoteEndPoint = new IPEndPoint(ip, servicePort)//这里是8000
            };
            w.UserName = userName;
            w.Show();
        }
    }
}
NetMeetingClient前台设计代码
<Window x:Class="ch11.NetMeetingClient"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="网络会议室" Height="354" Width="480">
    <Grid>
        <GroupBox Header="会议室成员" Height="185" HorizontalAlignment="Left" Margin="21,87,0,0" Name="groupBox1" VerticalAlignment="Top" Width="133">
            <Grid>
                <ListBox Height="142" HorizontalAlignment="Left" Margin="6,6,0,0" Name="listBox1" VerticalAlignment="Top" Width="97" />
            </Grid>
        </GroupBox>
        <GroupBox Header="发言内容" Height="179" HorizontalAlignment="Left" Margin="171,87,0,0" Name="groupBox2" VerticalAlignment="Top" Width="261">
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="53*" />
                    <RowDefinition Height="25*" />
                </Grid.RowDefinitions>
                <ListBox Grid.RowSpan="2" Height="142" HorizontalAlignment="Left" Margin="6,6,0,0" Name="listBox2" VerticalAlignment="Top" Width="237" />
            </Grid>
        </GroupBox>
        <Label Content="发言:" Height="32" HorizontalAlignment="Left" Margin="22,281,0,0" Name="label1" VerticalAlignment="Top" Width="60" />
        <TextBox Height="32" HorizontalAlignment="Right" Margin="0,281,110,0" Name="textBox1" VerticalAlignment="Top" Width="260" />
        <Button Content="发送" Height="23" HorizontalAlignment="Left" Margin="371,285,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click" />
        <Label Content="用户名:" Height="28" HorizontalAlignment="Left" Margin="27,21,0,0" Name="label2" VerticalAlignment="Top" />
        <TextBox Height="24" HorizontalAlignment="Left" Margin="91,21,0,0" Name="textBox2" VerticalAlignment="Top" Width="95" />
        <Button Content="登录(进入会议室)" Height="22" HorizontalAlignment="Left" Margin="224,23,0,0" Name="button2" VerticalAlignment="Top" Width="124" Click="button2_Click" />
        <Button Content="退出" Height="23" HorizontalAlignment="Left" Margin="371,22,0,0" Name="button3" VerticalAlignment="Top" Width="75" Click="button3_Click" />
    </Grid>
</Window>

NetMeetingClient后台代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
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 System.Net;
using System.Net.Sockets;
using System.Threading;

namespace ch11
{
    /// <summary>
    /// NetMeetingClient.xaml 的交互逻辑
    /// </summary>
    public partial class NetMeetingClient : Window
    {
        public IPEndPoint localEndPoint { get; set; }
        public IPEndPoint remoteEndPoint;
        private UdpClient client;
        public string UserName;
        
        private IPAddress multicastAddress = IPAddress.Parse("224.0.1.1");
        
        private bool isExit = false;//窗口是否关闭
        private bool isLogin = false;//是否登陆

        public NetMeetingClient()
        {
            InitializeComponent();
            
            this.Loaded += NetMeeting_Loaded;
            this.Closing += NetMeeting_Closing;//加载关闭属性
        }
        //新启动的窗口为用户名一栏赋值  并设置“退出”按钮
        void NetMeeting_Loaded(object sender, RoutedEventArgs e) 
        {
            textBox2.Text=this.UserName;
            button3.IsEnabled = false;
        }
        //关闭窗口
        void NetMeeting_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            if (isLogin)
            {
                SendMessage("Logout," + UserName);//告诉管理员 用户退出
            }
            isExit = true;//窗口关闭
        }
        //给remoteEndPoint发送消息
        private void SendMessage(string Sendstring)
        {
            byte[] bytes = System.Text.Encoding.Unicode.GetBytes(Sendstring);
            client.Send(bytes, bytes.Length, remoteEndPoint);
        }
        //用户列表添加用户
        private void AddUser(string userName)
        {
            Action act = delegate() 
            { 
                listBox1.Items.Add(userName); 
            };
            listBox1.Dispatcher.Invoke(act);
        }
        //用户列表移除用户
        private void RemoveUser(string userName)
        {
            for (int i = 0; i < listBox1.Items.Count; i++) 
            {
                if (listBox1.Items[i].ToString() == userName) 
                {
                    Action act = delegate() 
                    { 
                    	listBox1.Items.Remove(userName); 
                    };
                    listBox1.Dispatcher.Invoke(act);
                    break;
                }
            }
        }
        //聊天列表添加信息
        private void AddTalk(string format,params object[] args)
        {
            string content = string.Format(format, args);
            Action act = delegate() { listBox2.Items.Add(content); };
            listBox2.Dispatcher.Invoke(act);
        }
		//登录按钮
        private void button2_Click(object sender, RoutedEventArgs e)
        {
            client = new UdpClient(localEndPoint);//生成套接字
            client.JoinMulticastGroup(multicastAddress);//加入多播组
            SendMessage("Login," + UserName);//告诉8000端口 用户登录
            
            Thread t1 = new Thread(ReceiveDate);//委托线程监听8000端口消息
            t1.Start();

            isLogin = true;//登陆标志
            button2.IsEnabled = false;//登陆按钮
            button3.IsEnabled = true;//退出按钮

        }
        //监听并接收信息
        public void ReceiveDate()
        {
            while (isExit == false)
            {
                try
                {
                    byte[] result = client.Receive(ref remoteEndPoint);//接受8000端口信息
                    string message = Encoding.Unicode.GetString(result);
                    string[] split = message.Split(',');//分割
                    string command = split[0];
                    string args = message.Remove(0, split[0].Length + 1);
                    switch (command)
                    {
                        case "Login":
                            {
                                string userName = args;
                                AddUser(userName);
                                break;
                            }
                        case "List":  //添加已在线成员列表
                            {
                                for (int i = 1; i < split.Length; i++)
                                {
                                    AddUser(split[i]);
                                }
                                break; 
                            }
                        case "Logout":
                            {
                                string userName = args;
                                RemoveUser(userName);
                                break;
                            }
                        case "Say":
                            {
                                AddTalk("{0}:{1}", split[1], args.Remove(0, split[1].Length + 1));
                                break;
                            }
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.ToString());
                }
            }
            client.DropMulticastGroup(multicastAddress);//退出多播组
            client.Close();//关闭套接字
        }
        private void button3_Click(object sender, RoutedEventArgs e)//退出
        {
            this.Close();//关闭窗口  退出信息在关闭窗口的属性方法里面写着
        }
        private void button1_Click(object sender, RoutedEventArgs e)//发送
        {
            SendMessage("Say," + UserName + "," + textBox1.Text);
            textBox1.Text = "";//发送完消息  输入框置为空
        }
    }
}
RoomService类源码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Windows;

namespace ch11
{
    class RoomService
    {
        private Dictionary<string, User> users = new Dictionary<string, User>();
        public IPEndPoint localEndPoint { get; set; }
        
        private UdpClient client;
        private IPAddress multicastAddress = IPAddress.Parse("224.0.1.1");//多播地址
        public bool isExit = false;
        
        public RoomService(IPAddress localAddress, int servicePort) 
        {
            localEndPoint = new IPEndPoint(localAddress, servicePort);
            client = new UdpClient(localEndPoint);
            Thread t1 =new Thread(ReceiveDate);//加入多播组 监听所有端口信息
            t1.Start();
        }
        public void CloseSerivce()
        {
            isExit = true;//while循环结束 退出多播组
        }

        public void ReceiveDate()
        {
            IPEndPoint remote = null;//监听所有端口信息
            client.JoinMulticastGroup(multicastAddress);
            while (isExit==false)
            {
                try 
                {
                    byte[] result = client.Receive(ref remote);//接收所有端口信息
                    string message = Encoding.Unicode.GetString(result);
                    string[] split = message.Split(',');//分割
                    switch (split[0])
                    {
                        case "Login":
                            {
                                string userName = split[1];
                                if (users.ContainsKey(userName))
                                {
                                    string str = "CanNotLogin," + "已有该名称";
                                    byte[] bytess = Encoding.Unicode.GetBytes(str);
                                    client.Send(bytess, bytess.Length, remote);
                                }
                                else 
                                {
                                    User user = new User()
                                    {
                                        UserName=userName,
                                        UserEndPoint = remote//注意:此时的remote是当前正在登录的用户的端口
                                    };
                                    ShowCurrentUsers(user);//要先为该用户发送已在线人员信息
                                    users.Add(userName, user);//再将此用户加入字典
                                    Multicast(message);//广播该用户上线信息 使其他客户端将其加入列表
                                }
                                break;
                            }
                        case "Logout":
                            {
                                string userName = split[1];
                                users.Remove(userName);//移除该用户
                                Multicast(message);//广播移除信息
                                break;
                            }
                        case "Say":
                            {
                                Multicast(message);//广播消息
                                break;
                            }
                    }
                }
                catch(Exception ex)
                {
                    MessageBox.Show(ex.ToString());
                }
            }
            
            client.DropMulticastGroup(multicastAddress);//退出多播组
            client.Close();
        }
        //用户刚刚登陆 先将会议室现有人员列表发送给该用户
        private void ShowCurrentUsers(User user)
        {

            if (users.Count == 0) return;
            string s = "List";
            //遍历现有的在线人员列表
            foreach (var v in users.Keys) 
            {
                s += "," + v;
            }
            byte[] bytes = Encoding.Unicode.GetBytes(s);
            client.Send(bytes, bytes.Length, user.UserEndPoint);//发送给该用户  UserEndPoint
        }
        //在会议室广播
        private void Multicast(string message)
        {
            string hostname = multicastAddress.ToString();
            byte[] bytes = Encoding.Unicode.GetBytes(message);
            for (int i = 8001; i < 8004; i++) 
            {
                client.Send(bytes, bytes.Length, hostname,i);//将登陆信息发给所有客户端
            }
        }
    }
    public class User
    {
        public string UserName{get;set;}
        public IPEndPoint UserEndPoint{get;set;}
    }
}

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

1900_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值