博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C# 多人聊天程序
阅读量:6375 次
发布时间:2019-06-23

本文共 10180 字,大约阅读时间需要 33 分钟。

  上个星期,特别想写一个点对点聊天的小程序,就上网查了一下有关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
clientMem = new Dictionary
(); //客户端列表一定要初始化,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
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控件。

转载地址:http://motqa.baihongyu.com/

你可能感兴趣的文章
白话Java I/O模型
查看>>
[TsAdmin]--一款基于Vue.js+Element UI的单页无刷新(无iframe)多选项卡的后台管理系统模板...
查看>>
排列组合技术
查看>>
哈工大发明“电子体毛”,让机器人学会“敏感”
查看>>
上传一张照片,让算法告诉你是否患有抑郁症
查看>>
VR厂商唯晶科技获2800万C+轮融资,曾开发过游戏《圣女之歌》
查看>>
Countly 19.02.1 发布,实时移动和 web 分析报告平台
查看>>
TCP连接中time_wait在开发中的影响-搜人以鱼不如授之以渔
查看>>
Oracle数据库机出新帮助不同规模企业迈向云端
查看>>
前端通信:ajax设计方案(六)--- 全局配置、请求格式拓展和优化、请求二进制类型、浏览器错误搜集以及npm打包发布...
查看>>
Android捕获监听Home键、最近任务列表键
查看>>
微服务分布式企业框架 Springmvc+mybatis+shiro+Dubbo+ZooKeeper+Redis+KafKa
查看>>
word2vec原理(三) 基于Negative Sampling的模型
查看>>
被《时代周刊》选为年度最佳发明,PS VR靠的竟然是价格
查看>>
通用唯一标识码UUID的介绍及使用。
查看>>
spring笔记--依赖注入之针对不同类型变量的几种注入方式
查看>>
Java爬虫——网易云热评爬取
查看>>
Ajax的简单学习
查看>>
无华为,不智慧:智慧城市建设为何少不了华为?
查看>>
高性能网络通信框架Netty-基础概念篇
查看>>