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控件。

转载于:https://www.cnblogs.com/wang7/archive/2012/05/30/2526551.html

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是一个简单的基于Winsock的多人聊天程序C#代码: ```csharp using System; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; namespace ChatRoomServer { class ChatRoomServer { static Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); static byte[] buffer = new byte[1024]; static int port = 8888; static int maxClients = 10; static int clientCount = 0; static Socket[] clientSockets = new Socket[maxClients]; static void Main(string[] args) { Console.Title = "Chat Room Server"; Console.WriteLine("Starting server..."); serverSocket.Bind(new IPEndPoint(IPAddress.Any, port)); serverSocket.Listen(maxClients); while (true) { Socket clientSocket = serverSocket.Accept(); clientSockets[clientCount] = clientSocket; clientCount++; Console.WriteLine("Client connected."); Thread receiveThread = new Thread(() => ReceiveData(clientSocket)); receiveThread.Start(); } } static void ReceiveData(Socket clientSocket) { while (true) { try { int bytesReceived = clientSocket.Receive(buffer); if (bytesReceived == 0) { clientSocket.Shutdown(SocketShutdown.Both); clientSocket.Close(); break; } string message = Encoding.ASCII.GetString(buffer, 0, bytesReceived); Console.WriteLine("Received message: " + message); for (int i = 0; i < clientCount; i++) { if (clientSockets[i] != null && clientSockets[i] != clientSocket) { clientSockets[i].Send(Encoding.ASCII.GetBytes(message)); } } } catch (Exception ex) { Console.WriteLine("Error: " + ex.Message); clientSocket.Shutdown(SocketShutdown.Both); clientSocket.Close(); break; } } } } } ``` 这个程序使用了一个静态的 `clientSockets` 数组来存储所有连接到服务器的客户端套接字。当一个新的客户端连接时,它会被添加到数组中,并且一个新的线程会被启动来接收来自该客户端的消息。当一个客户端发送消息时,服务器会将该消息转发给所有其他客户端。 请注意,这个程序只是一个简单的示例,没有任何安全功能,并且没有处理任何异常情况。在实际应用中,你需要考虑更多的安全问题,并编写更健壮的代码来处理各种异常情况。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值