C# 多人聊天程序

  上个星期,特别想写一个点对点聊天的小程序,就上网查了一下有关C#网络编程的知识,用到最多的就是TcpClient和TcpListener,使用这两个类就可以完成主机之间的通信,当然,做这个程序的过程中也用到了多线程和事件与委托,这是我第一次将这些高级特性加入到程序中,通过参考

《C#和.net 3.0第一步》,我学会了如何使用事件,然后照个上面的例子写出了这个多人聊天程序。

  

  定义一个客户端类:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Net.Sockets;
using System.Windows.Forms;
using System.IO;
using System.Net;

namespace TCPClient
{
    class P2PClient
    {
        public TcpClient  tcpClientObj;//收发数据

        private Thread receiveThread; //接收数据的线程

        public delegate void receiveDelegate(string receiveData);//处理接收数据事件的方法类型

        public event receiveDelegate receiveEvent; //接收数据的事件

        public void SendConnection(string ip, int port)  //通过IP地址和端口号发送连接
        {
            IPAddress ipaddr = IPAddress.Parse(ip);//转为IP地址后在连接会加快速度
            
            tcpClientObj = new TcpClient(); //连接客户端 
           
            tcpClientObj.Connect(ipaddr, port);//连接    

            receiveThread = new Thread(Receiver); //启动接收数据线程
            receiveThread.Start();  

        }

        public void Send(string message) //发送信息
        {
            if (tcpClientObj == null)
            {
                return;
            }
            NetworkStream ns = this.tcpClientObj.GetStream();//得到网络流

            StreamWriter sw = new StreamWriter(ns);

            sw.WriteLine(message);//发送信息
            
            sw.Flush();//使所有的缓冲数据写入基础数据流  
            ns.Flush();
        }   

        private void Receiver()  //接收数据对应的线程(接收到数据以后触发事件)
        {
            while (true) //一直接受
            {
                NetworkStream ns = this.tcpClientObj.GetStream();

                StreamReader sr = new StreamReader(ns);

                string receivedata = sr.ReadLine();

                receiveEvent(receivedata);//触发事件
            }
        }  
    }
}

客户端窗体对应代码如下:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace TCPClient
{
    public partial class ClientForm : Form
    {
        private P2PClient ClientObj = new P2PClient(); //客户对象,对于一个对象一定要new啊

        public ClientForm()
        {
            InitializeComponent();
        }

        private void ClientForm_Load(object sender, EventArgs e)
        {
            btnSend.Enabled = false; //没有连接不允许发送数据
            this.AcceptButton = btnSend;

        }

        private void btnConnect_Click(object sender, EventArgs e)  
        {
            string nickName = tbName.Text;
            string ip =  tbIP.Text;
            string port = tbPort.Text;

            if(string.IsNullOrEmpty(nickName) || string.IsNullOrEmpty(ip) || string.IsNullOrEmpty(port))
            {
                MessageBox.Show("请将昵称、IP填写完整");
                return ;
            }

            try
            {           

                ClientObj.SendConnection(ip, Convert.ToInt32(port)); //连接  

                ClientObj.receiveEvent += new P2PClient.receiveDelegate(ClientObj_receiveEvent); //订阅事件的处理方法

                ClientObj.Send(tbName.Text + "登陆成功!");

                btnSend.Enabled = true;
                btnConnect.Enabled = false;
            }
            catch (Exception ex)
            {
                MessageBox.Show("连接时出错:" + ex.Message);
                return;
            }
        }

        void ClientObj_receiveEvent(string receiveData)
        {
            try
            {
                if (this.InvokeRequired) //指示是否需要在这个线程上调用方法
                {
                    P2PClient.receiveDelegate update = new P2PClient.receiveDelegate(ClientObj_receiveEvent);//当把消息传递给控件线程时重复调用该方法就会调用else

                    this.Invoke(update, new object[] { receiveData });//将消息发送给控件线程处理
                }
                else
                {
                    lbMessage.Items.Add(receiveData);//添加数据
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("接收数据错误:" + ex.Message);
                return;
            }
            
        }

        private void btnSend_Click(object sender, EventArgs e)
        {
            try
            {
                if (string.IsNullOrEmpty(tbMessage.Text))
                {
                    return;
                }
                ClientObj.Send(tbName.Text + "说:" + tbMessage.Text);//发送信息
                tbMessage.Clear();//清除原来的文本
            }
            catch (Exception ex)
            {
                MessageBox.Show("发送数据出错:" + ex.Message);
                return;
            }
        }

        private void ClientForm_FormClosing(object sender, FormClosingEventArgs e)
        {
           // ClientObj.Send(this.tbName.Text + "下线了");
            //ClientObj.tcpClientObj.Close();//关闭连接
            this.Close();//关闭窗体,让程序自动释放资源
        }
    }
}

客户端完成,下面定义一个服务器端类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using System.Net;
using System.Windows.Forms;
using System.IO;

namespace TCPServer
{
    class P2PServer
    {
        public  TcpListener listenObj;//监听对象
        public Dictionary<string,TcpClient> clientMem = new Dictionary<string,TcpClient>(); //客户端列表一定要初始化,new

        private Thread listenThread; //监听线程

        public delegate void ConnectDelegate(); //连接成功后处理事件的方法类型

        public event ConnectDelegate ConnectEvent;//连接事件

        public delegate void ReceiveDelegate(string message); //接收数据后处理事件方法类型

        public event ReceiveDelegate ReceiveEvent; //接收数据事件

        public void Listen(int port) //监听方法,启动监听线程
        {
            IPAddress[] localIP = Dns.GetHostAddresses(Dns.GetHostName()); //通过主机名得到本地IP

            this.listenObj = new TcpListener(localIP[0], port); //监听对象

            this.listenThread = new Thread(ListenClient); //这个线程仅仅用来监听客户        

            listenThread.Start();//启动这个线程方法          
          
        }

        public void ListenClient()  //监听线程对应的方法,监听到信息后向所有的客户端发送数据
        {
            while (true) //一直监听,可以有多个客户端请求连接
            {
                listenObj.Start();//开始侦听请求 ;注意在线程start之后才可以。

                TcpClient acceptClientObj = listenObj.AcceptTcpClient();//接收挂起的连接请求,这是一个阻塞方法                  

                this.ConnectEvent();//触发连接事件

                Thread receiveThread = new Thread(Receiver); //这个线程处理接收的数据

                string connectTime = DateTime.Now.ToString();

                receiveThread.Name = connectTime;//设置线程的名字

                this.clientMem.Add(connectTime, acceptClientObj); //将 客户 添加到列表     

                receiveThread.Start();//接收到的连接包含数据               
            }            
        }

       
        public void Send(string message) //发送信息
        {
           
            foreach (KeyValuePair<string, TcpClient> var in clientMem) //向所有客户发送数据
            {

                if (var.Value == null || var.Value.Connected == false)
                {
                    clientMem.Remove(var.Key);  //删除断开的连接???这个地方有待改进
                    continue;
                }             

                NetworkStream ns = var.Value.GetStream();//得到网络流

                StreamWriter sw = new StreamWriter(ns);
                sw.WriteLine(message);

                sw.Flush();//刷新数据流  
                ns.Flush();
            }           
   
        }

        public void Receiver()  //接收 数据 对应的方法
        {   //所有的TcpClient都对应一个线程,用来接收客户端发来的数据,通过线程名,找到对应的TcpClient
          
            while (true)
            {
                //收到一个TcpClient时,都有一个命名的Thread对应,
                NetworkStream ns = clientMem[Thread.CurrentThread.Name].GetStream();                 

                StreamReader sr = new StreamReader(ns);

                string message = sr.ReadLine();//读取消息

                this.ReceiveEvent(message);//接收过数据 就触发接收消息的事件            
            }
        }

    }
}

下面是服务器端窗体代码:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net.Sockets;

namespace TCPServer
{
    public partial class ServerForm : Form
    {
        P2PServer serverObj = new P2PServer();

        public ServerForm()
        {
           
            InitializeComponent();
        }

        private void ServerForm_Load(object sender, EventArgs e)
        {
            try
            {
                serverObj.ConnectEvent += new P2PServer.ConnectDelegate(serverObj_ConnectEvent);  //订阅连接事件

                serverObj.ReceiveEvent += new P2PServer.ReceiveDelegate(serverObj_ReceiveEvent);  //订阅接收数据事件

                serverObj.Listen(Convert.ToInt32(tbPort.Text)); //启动监听
            }
            catch (Exception ex)
            {
                MessageBox.Show("加载失败:" + ex.Message);
                return;
            }

        }

        void serverObj_ReceiveEvent(string message)
        {
            try
            {
                if (this.InvokeRequired)
                {
                    P2PServer.ReceiveDelegate update = new P2PServer.ReceiveDelegate(serverObj_ReceiveEvent);

                    this.Invoke(update, new object[] { message });
                }
                else
                {
                    this.lbMessage.Items.Add(message);//添加到显示栏

                    

                    serverObj.Send(message);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("处理事件方法错误:" + ex.Message);
                return;
            }
            
        }     

        void serverObj_ConnectEvent()
        {
            try
            {
                if (this.InvokeRequired)
                {
                    P2PServer.ConnectDelegate update = new P2PServer.ConnectDelegate(serverObj_ConnectEvent);

                    this.Invoke(update);
                }

                else
                {
                    this.lbMessage.Items.Add("连接成功");
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("处理连接事件方法错误:" + ex.Message);
                return;
            }
        }

        private void ServerForm_FormClosed(object sender, FormClosedEventArgs e)
        {
            
        }

        private void ServerForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            this.Close();
        }
    }
}

运行效果如下:

 

显示消息的是一个ListBox控件。

posted @ 2012-05-30 16:54  金河  阅读(...)  评论(...编辑  收藏