zoukankan      html  css  js  c++  java
  • 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控件。

  • 相关阅读:
    TYPE_SCROLL_INSENSITIVE is not compatible with CONCUR_UPDATABLE
    with admin option /with grant option
    通过动态SQL语句创建游标
    Vue:在vue-cli中使用Bootstrap
    Vue:$set和$delete
    Vue:如何在vue-cli中创建并引入自定义组件
    MVC教程四:Controller向View传值的几种方式
    Vue:生命周期
    Vue.js常用指令:v-model
    vue:过滤器
  • 原文地址:https://www.cnblogs.com/wang7/p/2526551.html
Copyright © 2011-2022 走看看